Commit 961b3603 张少辉

1.库存缺料报表和固定料号维护 功能提交

1 个父辈 63d17b7d
......@@ -241,6 +241,7 @@ public class DataInitManager {
addNewFunctionMenu(89,pMenuReport,"innerMaShortReport","缺料","innerMaShortReport","innerWarehouse/maShortReport/index","maShort",functionMenuMap);
addNewFunctionMenu(90,pMenuReport,"innerProLimitReport","禁限用","innerProLimitReport","innerWarehouse/proLimitReport/index","proLimit",functionMenuMap);
addNewFunctionMenu(91,pMenuReport,"imDetailsReport","导入明细","imDetailsReport","innerWarehouse/imDetailsReport/index","feeding",functionMenuMap);
addNewFunctionMenu(92, pMenuReport, "towerInventoryReport", "库存缺料信息", "towerInventoryReport", "report/towerInventoryReport/index", "towerInventoryReport", functionMenuMap);
//内外仓都有
addNewFunctionMenu(92,pMenuReport,"expiredReport","过期报表","expiredReport","report/expiredReport/index","inventory",functionMenuMap);
......@@ -267,6 +268,7 @@ public class DataInitManager {
addNewFunctionMenu(107,poutSet, "warehouseMaintenance", "库别维护", "warehouseMaintenance", "system/warehouseMaintenance/index", "translation",functionMenuMap);
addNewFunctionMenu(108,poutSet, "partMasterData", "料号基础数据", "partMasterData", "system/partMasterData/index", "partMasterData",functionMenuMap);
addNewFunctionMenu(109,poutSet, "wireQtyMaint", "开线数量维护", "wireQtyMaint", "system/wireQtyMaint/index", "wireQtyMaint",functionMenuMap);
addNewFunctionMenu(110,poutSet, "fixdPartNumber", "固定料号维护", "fixdPartNumber", "system/fixdPartNumber/index", "fixdPartNumber",functionMenuMap);
//addNewFunctionMenu(107,poutSet, "warehouseMaintenance", "库别维护", "warehouseMaintenance", "system/warehouseMaintenance/index", "translation",functionMenuMap);
//内外仓都有
......
......@@ -30,8 +30,10 @@ import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.custom.lizhen.LizhenApi;
import com.neotel.smfcore.custom.lizhen.bean.apirequest.BrandQtyRequest;
import com.neotel.smfcore.custom.lizhen.bean.apiresult.BrandQtyResult;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.FixdPartNumber;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.WareHouseCode;
import com.neotel.smfcore.custom.lizhen.innerBox.enums.ExtendType;
import com.neotel.smfcore.custom.lizhen.innerBox.service.manager.IFixdPartNumberManager;
import com.neotel.smfcore.custom.lizhen.innerBox.service.manager.WareHouseCodeManager;
import com.neotel.smfcore.custom.lizhen.virtual.service.manager.IVirInventoryManager;
import com.neotel.smfcore.custom.lizhen.wcs.bean.BackToWarehouse;
......@@ -88,10 +90,8 @@ public class DeviceController {
private IVirInventoryManager virInventoryManager;
@Autowired
private WareHouseCodeManager wareHouseCodeManager;
private IFixdPartNumberManager fixdPartNumberManager;
@Autowired
private IDataLogManager dataLogManager;
private Map<String, IDeviceHandler> handlerMap = new HashMap<>();
......@@ -215,6 +215,14 @@ public class DeviceController {
throw new ValidateException("", "335S00571/155S00462的料号不允许入到智能仓");
}
int fixPartNumberCount = fixdPartNumberManager.countByQuery(new Query());
if (fixPartNumberCount > 0) {
FixdPartNumber fixdPartNumber = fixdPartNumberManager.findByPartNumber(barcode.getPartNumber());
if (fixdPartNumber == null) {
throw new ValidateException("", barcode.getBarcode() + "对应的料号为:" + barcode.getPartNumber() + "不在固定料号维护中,不允许入库");
}
}
//判断虚拟仓有没有存在,如果有,把虚拟仓库位置空
StoragePos storagePos = storagePosManager.getByBarcode(barcode.getBarcode());
if (storagePos != null) {
......
......@@ -2,7 +2,6 @@ package com.neotel.smfcore.core.kanban.rest;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.neotel.smfcore.common.bean.PageData;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.exception.ValidateException;
......@@ -25,19 +24,15 @@ import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.manager.impl.DataLogManagerImpl;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.service.po.Humiture;
import com.neotel.smfcore.core.system.util.DevicesStatusUtil;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.security.annotation.AnonymousPutMapping;
import com.neotel.smfcore.security.service.manager.IGroupManager;
import com.neotel.smfcore.security.service.manager.impl.GroupManagerImpl;
import com.neotel.smfcore.security.service.manager.impl.UserManagerImpl;
import com.neotel.smfcore.security.service.po.Group;
import com.neotel.smfcore.security.service.po.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javafx.concurrent.Task;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -45,7 +40,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
......
package com.neotel.smfcore.custom.lizhen.innerBox.bean;
import com.neotel.smfcore.common.base.BasePo;
import lombok.Data;
import java.io.Serializable;
@Data
public class FixdPartNumber extends BasePo implements Serializable {
private String partNumber;
}
......@@ -51,7 +51,7 @@ public class ReelConsumptionController {
//下载模板
@ApiOperation("下载模板")
@RequestMapping("/download")
@AnonymousAccess
//@AnonymousAccess
public void download(HttpServletResponse response) throws IOException {
List<List<String>> headerList = new ArrayList<>();
headerList.add(Arrays.asList("序号"));
......@@ -93,7 +93,7 @@ public class ReelConsumptionController {
@ApiOperation("上传模板信息")
@PostMapping(value = "/upload")
@AnonymousAccess
//@AnonymousAccess
public ResultBean upload(@RequestParam MultipartFile uploadFile) throws Exception {
// 验证文件上传的格式
String image = "xlsx";
......@@ -164,7 +164,7 @@ public class ReelConsumptionController {
throw new ValidateException("smfcore.valueNotExist", "{0}[{1}]不存在", new String[]{"替代料"});
}
Cell cell = dataRaw.getCell(index);
String value = String.valueOf((int)cell.getNumericCellValue());
String value = cell.getStringCellValue();
if (StringUtils.isEmpty(value)) {
throw new ValidateException("smfcore.valueCanotNull", "{0}不能为空", new String[]{"替代料"});
}
......@@ -224,7 +224,7 @@ public class ReelConsumptionController {
@ApiOperation("获取列表信息")
@RequestMapping(value = "/list")
@AnonymousAccess
//@AnonymousAccess
public ResultBean list() {
Map<String,ReelConsumption> cacheMap = dataCache.getCache(Constants.Cache_reelConsumption);
if (cacheMap == null){
......
......@@ -28,7 +28,7 @@ public class WireQtyMaintController {
@ApiOperation("获取线体信息")
@RequestMapping(value = "/detail")
@AnonymousAccess
//@AnonymousAccess
public ResultBean detail() {
Map<String, Integer> cacheMap = dataCache.getCache(Constants.CACHE_wireQtyMaint);
if (cacheMap == null) {
......@@ -55,7 +55,7 @@ public class WireQtyMaintController {
@ApiOperation("修改线体信息")
@RequestMapping(value = "/update")
@AnonymousAccess
//@AnonymousAccess
public ResultBean update(@RequestBody Map<String, Integer> paramMap) {
Map<String, Integer> cacheMap = dataCache.getCache(Constants.CACHE_wireQtyMaint);
if (cacheMap == null) {
......
package com.neotel.smfcore.custom.lizhen.innerBox.service.dao;
import com.neotel.smfcore.common.base.IBaseDao;
public interface IFixPartNumberDao extends IBaseDao {
}
package com.neotel.smfcore.custom.lizhen.innerBox.service.dao.impl;
import com.neotel.smfcore.common.base.AbstractBaseDao;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.FixdPartNumber;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.VirImportLog;
import com.neotel.smfcore.custom.lizhen.innerBox.service.dao.IFixPartNumberDao;
import com.neotel.smfcore.custom.lizhen.innerBox.service.dao.IVirImportLogDao;
import org.springframework.stereotype.Service;
@Service
public class FixPartNumberDaoImpl extends AbstractBaseDao implements IFixPartNumberDao {
@Override
public Class getEntityClass() {
return FixdPartNumber.class;
}
}
package com.neotel.smfcore.custom.lizhen.innerBox.service.manager;
import com.neotel.smfcore.common.base.IBaseManager;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.FixdPartNumber;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
public interface IFixdPartNumberManager extends IBaseManager<FixdPartNumber> {
void batchSave(List<FixdPartNumber> resultList);
FixdPartNumber findByPartNumber(String partNumber);
void deleteById(String id);
int countByQuery(Query query);
}
package com.neotel.smfcore.custom.lizhen.innerBox.service.manager.impl;
import com.neotel.smfcore.common.bean.PageData;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.FixdPartNumber;
import com.neotel.smfcore.custom.lizhen.innerBox.service.dao.IFixPartNumberDao;
import com.neotel.smfcore.custom.lizhen.innerBox.service.manager.IFixdPartNumberManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class FixdPartNumberManagerImpl implements IFixdPartNumberManager {
@Autowired
private IFixPartNumberDao fixPartNumberDao;
@Override
public FixdPartNumber get(String id) {
return fixPartNumberDao.findOneById(id);
}
@Override
public FixdPartNumber save(FixdPartNumber object) throws ValidateException {
return fixPartNumberDao.save(object);
}
@Override
public void delete(FixdPartNumber object) throws ValidateException {
fixPartNumberDao.removeOne(object);
}
@Override
public PageData<FixdPartNumber> findByPage(Query query, Pageable pageable) {
int count = fixPartNumberDao.countByQuery(query);
List list = fixPartNumberDao.findByQuery(query, pageable);
return new PageData<>(list, count);
}
@Override
public List<FixdPartNumber> findByQuery(Query query) {
return fixPartNumberDao.findByQuery(query);
}
@Override
public void batchSave(List<FixdPartNumber> resultList) {
List<String> queryList = new ArrayList<>();
for (FixdPartNumber fixdPartNumber : resultList) {
queryList.add(fixdPartNumber.getPartNumber());
}
List<FixdPartNumber> partNumberList = fixPartNumberDao.findByQuery(new Query(Criteria.where("partNumber").in(queryList)));
if (partNumberList == null){
partNumberList = new ArrayList<>();
}
Map<String,String> partNumberMap = new HashMap<>();
for (FixdPartNumber fixdPartNumber : partNumberList) {
partNumberMap.put(fixdPartNumber.getPartNumber(),fixdPartNumber.getPartNumber());
}
for (FixdPartNumber fixdPartNumber : resultList) {
String value = partNumberMap.get(fixdPartNumber.getPartNumber());
if (StringUtils.isEmpty(value)) {
fixPartNumberDao.save(fixdPartNumber);
}
}
}
@Override
public FixdPartNumber findByPartNumber(String partNumber) {
return fixPartNumberDao.findOne(new Query(Criteria.where("partNumber").is(partNumber)));
}
@Override
public void deleteById(String id) {
fixPartNumberDao.removeOneById(id);
}
@Override
public int countByQuery(Query query) {
return fixPartNumberDao.countByQuery(query);
}
}
package com.neotel.smfcore.custom.lizhen.report;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.utils.Constants;
import com.neotel.smfcore.common.utils.FileUtil;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.bean.InventoryItem;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.ReelConsumption;
import com.neotel.smfcore.custom.lizhen.innerBox.bean.ReelConsumptionInventory;
import com.neotel.smfcore.custom.lizhen.report.bean.dto.TowerInventory;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
//根据开线数/UPH/站别数/颗数设定C2 3F Tower标准水位,自动根据Tower库存展示缺料盘数及缺料百分比
@Slf4j
@RestController
@RequestMapping("/towerInventory")
public class TowerInventoryController {
@Autowired
private DataCache dataCache;
//库存明细
@RequestMapping("/detail")
@AnonymousAccess
public ResultBean detail(String partNumber) {
List<TowerInventory> towerInventoryList = getTowerInventoryDetail(partNumber);
return ResultBean.newOkResult(towerInventoryList);
}
@RequestMapping("/detail/download")
@AnonymousAccess
public void detailDownload(String partNumber, HttpServletResponse response) {
List<TowerInventory> towerInventoryList = getTowerInventoryDetail(partNumber);
if (towerInventoryList == null || towerInventoryList.isEmpty()) {
log.info("库存缺料报表无数据,不导出");
return;
}
// 1. 构造表头
List<List<String>> headers = new ArrayList<>();
headers.add(Arrays.asList("料号"));
headers.add(Arrays.asList("需求最小库存盘数"));
headers.add(Arrays.asList("需求最大库存盘数"));
headers.add(Arrays.asList("当前库存盘数"));
headers.add(Arrays.asList("缺料盘数"));
headers.add(Arrays.asList("缺料百分比"));
// 收集所有动态列的线体名称
Set<String> allLineNames = new LinkedHashSet<>();
for (TowerInventory inv : towerInventoryList) {
if (inv.getMaxAndMinInventoryMap() != null) {
allLineNames.addAll(inv.getMaxAndMinInventoryMap().keySet());
}
}
// 排序(字母排序)
List<String> sortedLineNames = new ArrayList<>(allLineNames);
Collections.sort(sortedLineNames);
// 如果要按数字排序,可以用:
// sortedLineNames.sort(Comparator.comparingInt(s -> Integer.parseInt(s.replaceAll("\\D", ""))));
// 添加动态列到表头
for (String line : sortedLineNames) {
headers.add(Arrays.asList(line + "_最大库存"));
headers.add(Arrays.asList(line + "_最小库存"));
}
// 2. 构造数据
List<List<Object>> datas = new ArrayList<>();
for (TowerInventory inv : towerInventoryList) {
List<Object> rowData = new ArrayList<>();
// 固定列
rowData.add(inv.getPartNumber());
rowData.add(inv.getMinNeedInventory());
rowData.add(inv.getMaxNeedInventory());
rowData.add(inv.getCurrentInventory());
rowData.add(inv.getShortageDisks());
rowData.add(inv.getShortagePercentage());
// 动态列(按表头顺序)
Map<String, TowerInventory.MaxAndMinInventory> map = inv.getMaxAndMinInventoryMap();
for (String line : sortedLineNames) {
TowerInventory.MaxAndMinInventory mm = (map != null) ? map.get(line) : null;
rowData.add((mm != null) ? mm.getMax() : "");
rowData.add((mm != null) ? mm.getMin() : "");
}
datas.add(rowData);
}
// 3. 下载 Excel
try {
FileUtil.downloadExcel(headers, datas, response);
} catch (IOException e) {
log.error("库存缺料报表导出异常", e);
}
}
/**
* 获取 Tower 库存明细(可用于接口和导出)
*/
public List<TowerInventory> getTowerInventoryDetail(String partNumber) {
List<TowerInventory> towerInventoryList = new ArrayList<>();
// 从缓存获取线体用量
Map<String, ReelConsumption> cacheNeedInventoryMap =
dataCache.getCache(Constants.Cache_reelConsumption);
if (cacheNeedInventoryMap == null) {
cacheNeedInventoryMap = new HashMap<>();
}
// 获取开线数量
Map<String, Integer> openLineQuantityMap =
dataCache.getCache(Constants.CACHE_wireQtyMaint);
if (openLineQuantityMap != null && !openLineQuantityMap.isEmpty()) {
for (ReelConsumption consumption : cacheNeedInventoryMap.values()) {
// 如果传了料号,只返回匹配的
if (StringUtils.isNotBlank(partNumber) &&
!partNumber.equals(consumption.getPartNumber())) {
continue;
}
TowerInventory towerInventory = new TowerInventory();
towerInventory.setPartNumber(consumption.getPartNumber());
Map<String, TowerInventory.MaxAndMinInventory> maxAndMinInventoryMap =
Optional.ofNullable(towerInventory.getMaxAndMinInventoryMap())
.orElse(new HashMap<>());
Map<String, ReelConsumptionInventory> inventoryMap = consumption.getInventoryMap();
// 按线体计算最大最小库存
for (String line : openLineQuantityMap.keySet()) {
ReelConsumptionInventory reelConsumptionInventory = inventoryMap.get(line);
if (reelConsumptionInventory != null) {
Integer lineNum = openLineQuantityMap.get(line);
int minInventory = reelConsumptionInventory.getMin() * lineNum;
int maxInventory = reelConsumptionInventory.getMax() * lineNum;
TowerInventory.MaxAndMinInventory maxAndMinInventory =
new TowerInventory.MaxAndMinInventory();
maxAndMinInventory.setMax(maxInventory);
maxAndMinInventory.setMin(minInventory);
maxAndMinInventoryMap.put(line, maxAndMinInventory);
} else {
maxAndMinInventoryMap.put(line, null);
}
}
towerInventory.setMaxAndMinInventoryMap(maxAndMinInventoryMap);
towerInventoryList.add(towerInventory);
}
}
// 获取当前库存
Map<String, Integer> allPartNumberInventory = getAllPartNumberInventory();
// 计算缺料
if (!towerInventoryList.isEmpty()) {
for (TowerInventory towerInventory : towerInventoryList) {
int minNeedInventory = 0;
int maxNeedInventory = 0;
Map<String, TowerInventory.MaxAndMinInventory> maxAndMinInventoryMap =
towerInventory.getMaxAndMinInventoryMap();
if (maxAndMinInventoryMap != null) {
for (TowerInventory.MaxAndMinInventory maxAndMinInventory : maxAndMinInventoryMap.values()) {
if (maxAndMinInventory != null) {
minNeedInventory += maxAndMinInventory.getMin();
maxNeedInventory += maxAndMinInventory.getMax();
}
}
}
towerInventory.setMinNeedInventory(minNeedInventory);
towerInventory.setMaxNeedInventory(maxNeedInventory);
// 当前库存
Integer count = allPartNumberInventory.getOrDefault(towerInventory.getPartNumber(), 0);
towerInventory.setCurrentInventory(count);
// 缺料计算
if (minNeedInventory == 0) {
towerInventory.setShortageDisks(0);
towerInventory.setShortagePercentage("0%");
} else {
int shortageDisks = Math.max(0, minNeedInventory - count);
towerInventory.setShortageDisks(shortageDisks);
double percentage = (double) shortageDisks / minNeedInventory * 100;
towerInventory.setShortagePercentage(String.format("%.2f%%", percentage));
}
}
// 按料号排序
towerInventoryList = towerInventoryList.stream()
.sorted(Comparator.comparing(TowerInventory::getPartNumber))
.collect(Collectors.toList());
}
return towerInventoryList;
}
public Map<String, Integer> getAllPartNumberInventory() {
Map<String, Integer> resultMap = new HashMap<>();
for (Storage storage : dataCache.getAllStorage().values()) {
Map<String, InventoryItem> inventory = dataCache.getStorageInventory(storage.getCid());
for (InventoryItem item : inventory.values()) {
Integer count = resultMap.get(item.getPartNumber());
if (count == null) {
count = 0;
}
count = count + item.getStockReel();
resultMap.put(item.getPartNumber(), count);
}
}
return resultMap;
}
}
\ No newline at end of file
package com.neotel.smfcore.custom.lizhen.report.bean.dto;
import lombok.Data;
import java.util.Map;
@Data
public class TowerInventory {
private String partNumber;
private Map<String, MaxAndMinInventory> maxAndMinInventoryMap;
private int minNeedInventory;
private int maxNeedInventory;
private int currentInventory;
private int shortageDisks; //缺料盘数
private String shortagePercentage; //缺料百分比
@Data
public static class MaxAndMinInventory {
private int min;
private int max;
}
}
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!