yuansh hace 2 semanas
padre
commit
d0f309993d

+ 609 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/controller/PurSuggestedPriceController.java

@@ -0,0 +1,609 @@
+package org.jeecg.modules.otherCode.controller;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.io.InputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.modules.cuspCode.entity.CuspSupplierProfile;
+import org.jeecg.modules.cuspCode.service.ICuspSupplierProfileService;
+import org.jeecg.modules.otherCode.entity.PurSuggestedPrice;
+import org.jeecg.modules.otherCode.service.IPurSuggestedPriceService;
+import org.jeecg.modules.baseCode.entity.BaseProductArchive;
+import org.jeecg.modules.baseCode.service.IBaseProductArchiveService;
+import org.apache.commons.lang.StringUtils;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+
+import org.jeecgframework.poi.excel.ExcelImportUtil;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+
+ /**
+ * @Description: 供应商指导价
+ * @Author: jeecg-boot
+ * @Date:   2026-04-21
+ * @Version: V1.0
+ */
+@Api(tags="供应商指导价")
+@RestController
+@RequestMapping("/otherCode/purSuggestedPrice")
+@Slf4j
+public class PurSuggestedPriceController extends JeecgController<PurSuggestedPrice, IPurSuggestedPriceService> {
+	@Autowired
+	private IPurSuggestedPriceService purSuggestedPriceService;
+	@Autowired
+	private IBaseProductArchiveService baseProductArchiveService;
+	 @Autowired
+	 private ICuspSupplierProfileService cuspSupplierProfileService;
+	
+	/**
+	 * 分页列表查询
+	 *
+	 * @param purSuggestedPrice
+	 * @param pageNo
+	 * @param pageSize
+	 * @param req
+	 * @return
+	 */
+	//@AutoLog(value = "供应商指导价-分页列表查询")
+	@ApiOperation(value="供应商指导价-分页列表查询", notes="供应商指导价-分页列表查询")
+	@GetMapping(value = "/list")
+	public Result<IPage<PurSuggestedPrice>> queryPageList(PurSuggestedPrice purSuggestedPrice,
+								   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
+								   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
+								   HttpServletRequest req) {
+        QueryWrapper<PurSuggestedPrice> queryWrapper = QueryGenerator.initQueryWrapper(purSuggestedPrice, req.getParameterMap());
+		Page<PurSuggestedPrice> page = new Page<PurSuggestedPrice>(pageNo, pageSize);
+		queryWrapper.orderByDesc("annual");
+		queryWrapper.orderByDesc("supplier");
+		IPage<PurSuggestedPrice> pageList = purSuggestedPriceService.page(page, queryWrapper);
+		return Result.OK(pageList);
+	}
+	
+	/**
+	 *   添加
+	 *
+	 * @param purSuggestedPrice
+	 * @return
+	 */
+	@AutoLog(value = "供应商指导价-添加")
+	@ApiOperation(value="供应商指导价-添加", notes="供应商指导价-添加")
+	@RequiresPermissions("otherCode:pur_suggested_price:add")
+	@PostMapping(value = "/add")
+	public Result<String> add(@RequestBody PurSuggestedPrice purSuggestedPrice) {
+
+
+		QueryWrapper<PurSuggestedPrice> queryWrapper = new QueryWrapper<>();
+		queryWrapper.eq("supplier", purSuggestedPrice.getSupplier()).eq("material_id", purSuggestedPrice.getMaterialId())
+				.eq("annual", purSuggestedPrice.getAnnual()).last("limit 1");
+		PurSuggestedPrice existed = service.getOne(queryWrapper, false);
+		if (existed == null) {
+			purSuggestedPriceService.save(purSuggestedPrice);
+		}else{
+
+			return Result.OK("物料重复!");
+		}
+
+		return Result.OK("添加成功!");
+	}
+	
+	/**
+	 *  编辑
+	 *
+	 * @param purSuggestedPrice
+	 * @return
+	 */
+	@AutoLog(value = "供应商指导价-编辑")
+	@ApiOperation(value="供应商指导价-编辑", notes="供应商指导价-编辑")
+	@RequiresPermissions("otherCode:pur_suggested_price:edit")
+	@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
+	public Result<String> edit(@RequestBody PurSuggestedPrice purSuggestedPrice) {
+
+		QueryWrapper<PurSuggestedPrice> queryWrapper = new QueryWrapper<>();
+		queryWrapper.eq("supplier", purSuggestedPrice.getSupplier())
+				.eq("material_id", purSuggestedPrice.getMaterialId())
+				.notIn("id", purSuggestedPrice.getId())
+				.eq("annual", purSuggestedPrice.getAnnual()).last("limit 1");
+		PurSuggestedPrice existed = service.getOne(queryWrapper, false);
+		if (existed == null) {
+			purSuggestedPriceService.updateById(purSuggestedPrice);
+		}else{
+
+			return Result.OK("物料重复!");
+		}
+
+		return Result.OK("编辑成功!");
+	}
+	
+	/**
+	 *   通过id删除
+	 *
+	 * @param id
+	 * @return
+	 */
+	@AutoLog(value = "供应商指导价-通过id删除")
+	@ApiOperation(value="供应商指导价-通过id删除", notes="供应商指导价-通过id删除")
+	@RequiresPermissions("otherCode:pur_suggested_price:delete")
+	@DeleteMapping(value = "/delete")
+	public Result<String> delete(@RequestParam(name="id",required=true) String id) {
+		purSuggestedPriceService.removeById(id);
+		return Result.OK("删除成功!");
+	}
+	
+	/**
+	 *  批量删除
+	 *
+	 * @param ids
+	 * @return
+	 */
+	@AutoLog(value = "供应商指导价-批量删除")
+	@ApiOperation(value="供应商指导价-批量删除", notes="供应商指导价-批量删除")
+	@RequiresPermissions("otherCode:pur_suggested_price:deleteBatch")
+	@DeleteMapping(value = "/deleteBatch")
+	public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
+		this.purSuggestedPriceService.removeByIds(Arrays.asList(ids.split(",")));
+		return Result.OK("批量删除成功!");
+	}
+	
+	/**
+	 * 通过id查询
+	 *
+	 * @param id
+	 * @return
+	 */
+	//@AutoLog(value = "供应商指导价-通过id查询")
+	@ApiOperation(value="供应商指导价-通过id查询", notes="供应商指导价-通过id查询")
+	@GetMapping(value = "/queryById")
+	public Result<PurSuggestedPrice> queryById(@RequestParam(name="id",required=true) String id) {
+		PurSuggestedPrice purSuggestedPrice = purSuggestedPriceService.getById(id);
+		if(purSuggestedPrice==null) {
+			return Result.error("未找到对应数据");
+		}
+		return Result.OK(purSuggestedPrice);
+	}
+
+    /**
+    * 导出excel
+    *
+    * @param request
+    * @param purSuggestedPrice
+    */
+    @RequiresPermissions("otherCode:pur_suggested_price:exportXls")
+    @RequestMapping(value = "/exportXls")
+    public ModelAndView exportXls(HttpServletRequest request, PurSuggestedPrice purSuggestedPrice) {
+        return super.exportXls(request, purSuggestedPrice, PurSuggestedPrice.class, "供应商指导价");
+    }
+
+    /**
+      * 通过excel导入数据
+    *
+    * @param request
+    * @param response
+    * @return
+    */
+    @RequiresPermissions("otherCode:pur_suggested_price:importExcel")
+    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response,PurSuggestedPrice purSuggestedPrice) {
+		System.out.println(purSuggestedPrice);
+		System.out.println(purSuggestedPrice);
+        return this.importExcel(request, response, PurSuggestedPrice.class,purSuggestedPrice);
+    }
+
+	
+    /**
+     * 通过excel导入数据
+     *
+     * @param request
+     * @param response
+     * @return
+     */
+    protected Result<?> importExcel(HttpServletRequest request, HttpServletResponse response, Class<PurSuggestedPrice> clazz,PurSuggestedPrice purSuggestedPrice) {
+        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
+        Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
+        for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
+            // 获取上传文件对象
+            MultipartFile file = entity.getValue();
+            ImportParams params = new ImportParams();
+            params.setTitleRows(1);
+            params.setHeadRows(1);
+            params.setNeedSave(true);
+            try {
+                TitleUnitInfo titleUnitInfo = parseTitleUnit(file);
+                List<PurSuggestedPrice> list;
+                try (InputStream inputStream = file.getInputStream()) {
+                    list = ExcelImportUtil.importExcel(inputStream, clazz, params);
+                }
+                //循环赋值title和unit
+//                fillTitleAndUnit(list, titleUnitInfo);
+
+                //同步档案中文名称
+//                fillMaterialInfoAndSyncArchiveName(list);
+
+				//保留最高价格,循环赋值title和unit
+                List<PurSuggestedPrice> deduplicatedList = keepMaxPriceByMaterialAndSparePart(list, titleUnitInfo,purSuggestedPrice);
+                //update-begin-author:taoyan date:20190528 for:批量插入数据
+                long start = System.currentTimeMillis();
+                int num = replaceAndSaveBySupplierAndMaterialId(deduplicatedList,purSuggestedPrice);
+                //400条 saveBatch消耗时间1592毫秒  循环插入消耗时间1947毫秒
+                //1200条  saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒
+                log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
+                //update-end-author:taoyan date:20190528 for:批量插入数据
+                return Result.ok("文件导入成功!原始数据行数:" + list.size() + ",实际导入:" + num);
+            } catch (Exception e) {
+                //update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示
+                String msg = e.getMessage();
+                log.error(msg, e);
+                if(msg!=null && msg.indexOf("Duplicate entry")>=0){
+                    return Result.error("文件导入失败:有重复数据!");
+                }else{
+                    return Result.error("文件导入失败:" + e.getMessage());
+                }
+                //update-end-author:taoyan date:20211124 for: 导入数据重复增加提示
+            } finally {
+                // 文件流在各自 try-with-resources 中已关闭
+            }
+        }
+        return Result.error("文件导入失败!");
+    }
+
+    private int replaceAndSaveBySupplierAndMaterialId(List<PurSuggestedPrice> list, PurSuggestedPrice pur) {
+        String overwrite = pur == null ? null : pur.getOverwrite();
+        if (list == null || list.isEmpty()) {
+            return 0;
+        }
+        if ("0".equals(overwrite)) {
+            List<PurSuggestedPrice> toSaveList = new ArrayList<>();
+            for (PurSuggestedPrice item : list) {
+                if (item == null) {
+                    continue;
+                }
+                if (StringUtils.isBlank(item.getSupplier()) || StringUtils.isBlank(item.getMaterialId())) {
+                    toSaveList.add(item);
+                    continue;
+                }
+                QueryWrapper<PurSuggestedPrice> queryWrapper = new QueryWrapper<>();
+                queryWrapper.eq("supplier", item.getSupplier()).eq("material_id", item.getMaterialId())
+						.eq("annual", item.getAnnual()).last("limit 1");
+                PurSuggestedPrice existed = service.getOne(queryWrapper, false);
+                if (existed == null) {
+                    toSaveList.add(item);
+                }
+            }
+            if (!toSaveList.isEmpty()) {
+                service.saveBatch(toSaveList);
+            }
+            return toSaveList.size();
+        }
+
+        QueryWrapper<PurSuggestedPrice> existedQueryWrapper = new QueryWrapper<>();
+        boolean hasCondition = false;
+        for (PurSuggestedPrice item : list) {
+            if (item == null || StringUtils.isBlank(item.getSupplier()) || StringUtils.isBlank(item.getMaterialId())) {
+                continue;
+            }
+            if (!hasCondition) {
+                existedQueryWrapper.eq("supplier", item.getSupplier()).eq("material_id", item.getMaterialId()).eq("annual", item.getAnnual());
+                hasCondition = true;
+            } else {
+                existedQueryWrapper.or(wrapper -> wrapper.eq("supplier", item.getSupplier())
+						.eq("material_id", item.getMaterialId()).eq("annual", item.getAnnual()));
+            }
+        }
+        if (hasCondition) {
+            List<PurSuggestedPrice> existedList = service.list(existedQueryWrapper);
+            if (existedList != null && !existedList.isEmpty()) {
+                List<String> ids = existedList.stream().map(PurSuggestedPrice::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList());
+                if (!ids.isEmpty()) {
+                    service.removeByIds(ids);
+                }
+            }
+        }
+        service.saveBatch(list);
+        return list.size();
+    }
+
+    private TitleUnitInfo parseTitleUnit(MultipartFile file) throws Exception {
+        try (InputStream inputStream = file.getInputStream(); Workbook workbook = WorkbookFactory.create(inputStream)) {
+            Sheet sheet = workbook.getSheetAt(0);
+            if (sheet == null) {
+                return new TitleUnitInfo("", "");
+            }
+            Row row = sheet.getRow(0);
+            if (row == null) {
+                return new TitleUnitInfo("", "");
+            }
+            DataFormatter dataFormatter = new DataFormatter();
+            StringBuilder titleRowBuilder = new StringBuilder();
+            short lastCellNum = row.getLastCellNum();
+            if (lastCellNum < 0) {
+                return new TitleUnitInfo("", "");
+            }
+            for (int i = 0; i < lastCellNum; i++) {
+                Cell cell = row.getCell(i);
+                String text = dataFormatter.formatCellValue(cell);
+                if (StringUtils.isNotBlank(text)) {
+                    titleRowBuilder.append(text.trim());
+                }
+            }
+            String titleRow = titleRowBuilder.toString().replace(" ", "");
+            if (StringUtils.isBlank(titleRow)) {
+                return new TitleUnitInfo("", "");
+            }
+            String title = titleRow;
+            String unit = "";
+            int unitIndex = titleRow.indexOf("单位:");
+            if (unitIndex >= 0) {
+                title = titleRow.substring(0, unitIndex).trim();
+                unit = titleRow.substring(unitIndex + "单位:".length()).trim();
+            }
+            return new TitleUnitInfo(title, unit);
+        }
+    }
+
+    private void fillTitleAndUnit(List<PurSuggestedPrice> list, TitleUnitInfo titleUnitInfo) {
+        if (list == null || list.isEmpty() || titleUnitInfo == null) {
+            return;
+        }
+        for (PurSuggestedPrice item : list) {
+            if (item == null) {
+                continue;
+            }
+            item.setTitle(titleUnitInfo.getTitle());
+            item.setUnit(titleUnitInfo.getUnit());
+        }
+    }
+
+    private void validatePrice(List<PurSuggestedPrice> list) {
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+        for (int i = 0; i < list.size(); i++) {
+            PurSuggestedPrice item = list.get(i);
+            if (item == null || item.getPrice() == null) {
+                throw new RuntimeException("第" + (i + 1) + "行价格不能为空,且必须是数字格式");
+            }
+        }
+    }
+
+    private void fillMaterialInfoAndSyncArchiveName(List<PurSuggestedPrice> list) {
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+        Set<String> partnoSet = list.stream()
+                .map(PurSuggestedPrice::getSparePartNumber)
+                .filter(StringUtils::isNotBlank)
+                .collect(Collectors.toSet());
+        if (partnoSet.isEmpty()) {
+            return;
+        }
+        QueryWrapper<BaseProductArchive> queryWrapper = new QueryWrapper<>();
+        queryWrapper.in("partno", partnoSet);
+        queryWrapper.eq("del_flag", 0);
+        List<BaseProductArchive> archiveList = baseProductArchiveService.list(queryWrapper);
+        if (archiveList == null || archiveList.isEmpty()) {
+            return;
+        }
+        Map<String, BaseProductArchive> archiveMap = archiveList.stream()
+                .filter(item -> StringUtils.isNotBlank(item.getPartno()))
+                .collect(Collectors.toMap(BaseProductArchive::getPartno, item -> item, (left, right) -> left));
+        List<BaseProductArchive> toUpdateArchiveList = new ArrayList<>();
+        Set<String> updateArchiveIdSet = new HashSet<>();
+        for (PurSuggestedPrice item : list) {
+            if (item == null || StringUtils.isBlank(item.getSparePartNumber())) {
+                continue;
+            }
+            BaseProductArchive archive = archiveMap.get(item.getSparePartNumber());
+            if (archive == null) {
+                continue;
+            }
+            String importMaterialName = item.getMaterialName();
+            item.setMaterialId(archive.getId());
+            item.setMaterialCode(archive.getCode());
+            item.setMaterialName(archive.getChineseName());
+            if (StringUtils.isBlank(archive.getChineseName()) && StringUtils.isNotBlank(importMaterialName)
+                    && updateArchiveIdSet.add(archive.getId())) {
+                BaseProductArchive updateArchive = new BaseProductArchive();
+                updateArchive.setId(archive.getId());
+                updateArchive.setChineseName(importMaterialName);
+                toUpdateArchiveList.add(updateArchive);
+                archive.setChineseName(importMaterialName);
+                item.setMaterialName(importMaterialName);
+            }
+        }
+        if (!toUpdateArchiveList.isEmpty()) {
+            baseProductArchiveService.updateBatchById(toUpdateArchiveList);
+        }
+    }
+
+
+    //如果系统中没有该产品档案,则无需新增产品档案
+    private List<PurSuggestedPrice> keepMaxPriceByMaterialAndSparePart(List<PurSuggestedPrice> list, TitleUnitInfo titleUnitInfo,
+																	   PurSuggestedPrice purSuggestedPrice) {
+        if (list == null || list.isEmpty()) {
+            return list;
+        }
+
+		Set<String> partnoSet = list.stream()
+				.map(PurSuggestedPrice::getSparePartNumber)
+				.filter(StringUtils::isNotBlank)
+				.collect(Collectors.toSet());
+
+		QueryWrapper<BaseProductArchive> queryWrapper = new QueryWrapper<>();
+		queryWrapper.in("partno", partnoSet);
+		queryWrapper.eq("del_flag", 0);
+		List<BaseProductArchive> archiveList = baseProductArchiveService.list(queryWrapper);
+		if (archiveList == null || archiveList.isEmpty()) {
+			throw new RuntimeException("导入数据中没有可匹配数据");
+		}
+
+		List<BaseProductArchive> toUpdateArchiveList = new ArrayList<>();
+		Set<String> updateArchiveIdSet = new HashSet<>();
+
+		Map<String, BaseProductArchive> archiveMap = archiveList.stream()
+				.filter(item -> StringUtils.isNotBlank(item.getPartno()))
+				.collect(Collectors.toMap(BaseProductArchive::getPartno, item -> item, (left, right) -> left));
+
+        String getSupplier = purSuggestedPrice.getSupplier();
+        String getUnit = purSuggestedPrice.getUnit();
+        String getAnnual = purSuggestedPrice.getAnnual();
+		CuspSupplierProfile cuspSupplierProfile = cuspSupplierProfileService.getById(getSupplier);
+
+        String title = "";
+        String unit = "";
+        if (titleUnitInfo != null) {
+			title = titleUnitInfo.getTitle();
+//			unit = titleUnitInfo.getUnit();
+        }
+        Map<String, PurSuggestedPrice> deduplicatedMap = new LinkedHashMap<>();
+        for (PurSuggestedPrice item : list) {
+            if (item == null) {
+                continue;
+            }
+            if (StringUtils.isBlank(item.getSparePartNumber())) {
+                continue;
+            }
+
+			BaseProductArchive archive = archiveMap.get(item.getSparePartNumber());
+			if (archive == null){
+				continue;
+			}
+
+			if(StringUtils.isNotBlank(item.getMaterialName())) {
+				String importMaterialName = item.getMaterialName();
+				item.setMaterialId(archive.getId());
+//				item.setMaterialCode(archive.getCode());
+//				item.setMaterialName(archive.getChineseName());
+				if (StringUtils.isBlank(archive.getChineseName()) && StringUtils.isNotBlank(importMaterialName)
+						&& updateArchiveIdSet.add(archive.getId())) {
+					BaseProductArchive updateArchive = new BaseProductArchive();
+					updateArchive.setId(archive.getId());
+					updateArchive.setChineseName(importMaterialName);
+					toUpdateArchiveList.add(updateArchive);
+					archive.setChineseName(importMaterialName);
+					item.setMaterialName(importMaterialName);
+				}
+			}
+
+			item.setSupplierName(cuspSupplierProfile.getName());
+			item.setSupplier(getSupplier);
+			item.setUnit(getUnit);
+			item.setAnnual(getAnnual);
+
+			item.setTitle(title);
+//			item.setUnit(unit);
+            String key = buildDeduplicateKey(item);
+            PurSuggestedPrice existed = deduplicatedMap.get(key);
+            if (existed == null || comparePrice(item, existed) > 0) {
+                deduplicatedMap.put(key, item);
+            }
+        }
+
+		if (!toUpdateArchiveList.isEmpty()) {
+			baseProductArchiveService.updateBatchById(toUpdateArchiveList);
+		}
+
+        return new ArrayList<>(deduplicatedMap.values());
+    }
+
+
+    private List<PurSuggestedPrice> keepMaxPriceByMaterialAndSparePart2(List<PurSuggestedPrice> list, TitleUnitInfo titleUnitInfo) {
+        if (list == null || list.isEmpty()) {
+            return list;
+        }
+
+        String title = "";
+        String unit = "";
+        if (titleUnitInfo != null) {
+			title = titleUnitInfo.getTitle();
+			unit = titleUnitInfo.getUnit();
+        }
+        Map<String, PurSuggestedPrice> deduplicatedMap = new LinkedHashMap<>();
+        for (PurSuggestedPrice item : list) {
+            if (item == null) {
+                continue;
+            }
+			item.setTitle(title);
+			item.setUnit(unit);
+            String key = buildDeduplicateKey(item);
+            PurSuggestedPrice existed = deduplicatedMap.get(key);
+            if (existed == null || comparePrice(item, existed) > 0) {
+                deduplicatedMap.put(key, item);
+            }
+        }
+        return new ArrayList<>(deduplicatedMap.values());
+    }
+
+    private String buildDeduplicateKey(PurSuggestedPrice item) {
+        String productKey = item.getMaterialCode();
+        if (StringUtils.isBlank(productKey)) {
+            productKey = item.getMaterialName();
+        }
+        if (productKey == null) {
+            productKey = "";
+        }
+        String sparePartNumber = item.getSparePartNumber();
+        if (sparePartNumber == null) {
+            sparePartNumber = "";
+        }
+        return productKey + "||" + sparePartNumber;
+    }
+
+    private int comparePrice(PurSuggestedPrice left, PurSuggestedPrice right) {
+        if (Objects.equals(left.getPrice(), right.getPrice())) {
+            return 0;
+        }
+        if (left.getPrice() == null) {
+            return -1;
+        }
+        if (right.getPrice() == null) {
+            return 1;
+        }
+        return left.getPrice().compareTo(right.getPrice());
+    }
+
+    private static class TitleUnitInfo {
+        private final String title;
+        private final String unit;
+
+        private TitleUnitInfo(String title, String unit) {
+            this.title = title;
+            this.unit = unit;
+        }
+
+        private String getTitle() {
+            return title;
+        }
+
+        private String getUnit() {
+            return unit;
+        }
+    }
+}

+ 123 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/entity/PurSuggestedPrice.java

@@ -0,0 +1,123 @@
+package org.jeecg.modules.otherCode.entity;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.math.BigDecimal;
+
+import com.baomidou.mybatisplus.annotation.*;
+import org.jeecg.common.constant.ProvinceCityArea;
+import org.jeecg.common.util.SpringContextUtils;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.jeecg.common.aspect.annotation.Dict;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * @Description: 供应商指导价
+ * @Author: jeecg-boot
+ * @Date:   2026-04-21
+ * @Version: V1.0
+ */
+@Data
+@TableName("pur_suggested_price")
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value="pur_suggested_price对象", description="供应商指导价")
+public class PurSuggestedPrice implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+	/**主键*/
+	@TableId(type = IdType.ASSIGN_ID)
+    @ApiModelProperty(value = "主键")
+    private String id;
+	/**创建人*/
+    @ApiModelProperty(value = "创建人")
+    private String createBy;
+	/**创建日期*/
+	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "创建日期")
+    private Date createTime;
+	/**更新人*/
+    @ApiModelProperty(value = "更新人")
+    private String updateBy;
+	/**更新日期*/
+	@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "更新日期")
+    private Date updateTime;
+	/**所属部门*/
+    @ApiModelProperty(value = "所属部门")
+    private String sysOrgCode;
+	/**状态*/
+//	@Excel(name = "状态", width = 15, dicCode = "yes_or_no")
+	@Dict(dicCode = "yes_or_no")
+    @ApiModelProperty(value = "状态")
+    private Integer status;
+	/**删除状态*/
+//	@Excel(name = "删除状态", width = 15)
+    @ApiModelProperty(value = "删除状态")
+    @TableLogic
+    private Integer delFlag;
+	/**序号*/
+	@Excel(name = "序号", width = 15)
+    @ApiModelProperty(value = "序号")
+    private Double serialNumber;
+	/**物料编码*/
+	@Excel(name = "物料编码", width = 15)
+    @ApiModelProperty(value = "物料编码")
+    private String materialCode;
+	/**物料名称*/
+	@Excel(name = "名称", width = 15)
+    @ApiModelProperty(value = "物料名称")
+    private String materialName;
+	/**物料*/
+//	@Excel(name = "物料", width = 15)
+    @ApiModelProperty(value = "物料")
+    private String materialId;
+	/**备件号*/
+	@Excel(name = "图号", width = 15)
+    @ApiModelProperty(value = "备件号")
+    private String sparePartNumber;
+	/**价格*/
+	@Excel(name = "价格", width = 15)
+    @ApiModelProperty(value = "价格")
+    private BigDecimal price;
+	/**年度*/
+	@Excel(name = "年度", width = 15)
+    @ApiModelProperty(value = "年度")
+    private String annual;
+	/**供应商*/
+//	@Excel(name = "供应商", width = 15)
+    @ApiModelProperty(value = "供应商")
+    private String supplier;
+	/**供应商名称*/
+	@Excel(name = "供应商名称", width = 15)
+    @ApiModelProperty(value = "供应商名称")
+    private String supplierName;
+	/**标题*/
+	@Excel(name = "标题", width = 15)
+    @ApiModelProperty(value = "标题")
+    private String title;
+	/**单位*/
+	@Excel(name = "单位", width = 15)
+    @ApiModelProperty(value = "单位")
+    private String unit;
+	/**版本号*/
+//	@Excel(name = "版本号", width = 15)
+    @ApiModelProperty(value = "版本号")
+    private Integer versionNumber;
+	/**编码*/
+//	@Excel(name = "编码", width = 15)
+    @ApiModelProperty(value = "编码")
+    private String code;
+	//是否覆盖 1是 0否
+    @TableField(exist = false)
+    private String overwrite;
+}

+ 17 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/mapper/PurSuggestedPriceMapper.java

@@ -0,0 +1,17 @@
+package org.jeecg.modules.otherCode.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.otherCode.entity.PurSuggestedPrice;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @Description: 供应商指导价
+ * @Author: jeecg-boot
+ * @Date:   2026-04-21
+ * @Version: V1.0
+ */
+public interface PurSuggestedPriceMapper extends BaseMapper<PurSuggestedPrice> {
+
+}

+ 5 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/mapper/xml/PurSuggestedPriceMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.jeecg.modules.otherCode.mapper.PurSuggestedPriceMapper">
+
+</mapper>

+ 14 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/service/IPurSuggestedPriceService.java

@@ -0,0 +1,14 @@
+package org.jeecg.modules.otherCode.service;
+
+import org.jeecg.modules.otherCode.entity.PurSuggestedPrice;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @Description: 供应商指导价
+ * @Author: jeecg-boot
+ * @Date:   2026-04-21
+ * @Version: V1.0
+ */
+public interface IPurSuggestedPriceService extends IService<PurSuggestedPrice> {
+
+}

+ 19 - 0
srm-module-code/src/main/java/org/jeecg/modules/otherCode/service/impl/PurSuggestedPriceServiceImpl.java

@@ -0,0 +1,19 @@
+package org.jeecg.modules.otherCode.service.impl;
+
+import org.jeecg.modules.otherCode.entity.PurSuggestedPrice;
+import org.jeecg.modules.otherCode.mapper.PurSuggestedPriceMapper;
+import org.jeecg.modules.otherCode.service.IPurSuggestedPriceService;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+/**
+ * @Description: 供应商指导价
+ * @Author: jeecg-boot
+ * @Date:   2026-04-21
+ * @Version: V1.0
+ */
+@Service
+public class PurSuggestedPriceServiceImpl extends ServiceImpl<PurSuggestedPriceMapper, PurSuggestedPrice> implements IPurSuggestedPriceService {
+
+}

+ 4 - 2
srm-module-code/src/main/java/org/jeecg/modules/purCode/controller/PurInquiryFormController.java

@@ -506,8 +506,9 @@ public class PurInquiryFormController {
                         a.setDelivery(o.getDelivery());
 //						a.setDeliveryTime(o.getHeadDeliveryTime());
                         a.setTaxPrice(o.getTaxPrice() == null ? BigDecimal.ZERO : o.getTaxPrice());
-                        BigDecimal bd = BigDecimal.valueOf(a.getQuantity());
-                        a.setTaxAmount(o.getTaxPrice() == null ? BigDecimal.ZERO : o.getTaxPrice().multiply(bd));
+//                        BigDecimal bd = BigDecimal.valueOf(a.getQuantity());
+//                        a.setTaxAmount(o.getTaxPrice() == null ? BigDecimal.ZERO : o.getTaxPrice().multiply(a.getQuantity()));
+                        a.setTaxAmount(o.getTaxAmount());
                         a.setSourceCode(o.getBillCode());
                         a.setSourceId(a.getId());
                         a.setSourceId2(o.getId());
@@ -517,6 +518,7 @@ public class PurInquiryFormController {
                         a.setDeliveryDayChild(o.getDeliveryDayChild());
                         a.setQualityGrade(o.getQualityGrade());
 
+                        a.setQuantity(o.getQuantity());
                         a.setTaxPriceUsd(o.getTaxPriceUsd());
                         a.setTaxAmountUsd(o.getTaxAmountUsd());
                         a.setExchangeRateUsd(o.getExchangeRateUsd());

+ 1 - 1
srm-module-code/src/main/java/org/jeecg/modules/purCode/entity/PurInquiryFormProduct.java

@@ -127,7 +127,7 @@ public class PurInquiryFormProduct implements Serializable {
 	/**数量*/
 	@Excel(name = "数量", width = 15)
     @ApiModelProperty(value = "数量")
-    private Double quantity;
+    private BigDecimal quantity;
 	/**单位*/
 	@Excel(name = "单位", width = 15)
     @ApiModelProperty(value = "单位")

+ 7 - 0
srm-module-code/src/main/java/org/jeecg/modules/purCode/entity/PurPurchaseQuotationProduct.java

@@ -166,4 +166,11 @@ public class PurPurchaseQuotationProduct implements Serializable {
     private java.math.BigDecimal taxAmountOriginal;
     @ApiModelProperty(value = "折扣")
     private java.math.BigDecimal discount;
+
+    @ApiModelProperty(value = "协议价")
+    private java.math.BigDecimal suggestedPrice;
+    @ApiModelProperty(value = "协议价单位")
+    private String suggestedUnit;
+    @ApiModelProperty(value = "协议价来源")
+    private String suggestedId;
 }

+ 82 - 1
srm-module-code/src/main/java/org/jeecg/modules/purCode/service/impl/PurPurchaseQuotationServiceImpl.java

@@ -1,7 +1,9 @@
 package org.jeecg.modules.purCode.service.impl;
 
-import org.apache.ibatis.annotations.Param;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.jeecg.common.util.DateUtils;
+import org.jeecg.modules.otherCode.entity.PurSuggestedPrice;
+import org.jeecg.modules.otherCode.service.IPurSuggestedPriceService;
 import org.jeecg.modules.purCode.entity.PurPurchaseQuotation;
 import org.jeecg.modules.purCode.entity.PurPurchaseQuotationShip;
 import org.jeecg.modules.purCode.entity.PurPurchaseQuotationProduct;
@@ -15,8 +17,13 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import java.io.Serializable;
 import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * @Description: 采购报价单
@@ -33,6 +40,8 @@ public class PurPurchaseQuotationServiceImpl extends ServiceImpl<PurPurchaseQuot
 	private PurPurchaseQuotationShipMapper purPurchaseQuotationShipMapper;
 	@Autowired
 	private PurPurchaseQuotationProductMapper purPurchaseQuotationProductMapper;
+	@Autowired
+	private IPurSuggestedPriceService purSuggestedPriceService;
 
 
 	@Override
@@ -43,6 +52,7 @@ public class PurPurchaseQuotationServiceImpl extends ServiceImpl<PurPurchaseQuot
 	@Override
 	@Transactional(rollbackFor = Exception.class)
 	public void saveMain(PurPurchaseQuotation purPurchaseQuotation, List<PurPurchaseQuotationShip> purPurchaseQuotationShipList,List<PurPurchaseQuotationProduct> purPurchaseQuotationProductList) {
+		fillSuggestedInfo(purPurchaseQuotation, purPurchaseQuotationProductList);
 
 		BigDecimal totalAmount = BigDecimal.ZERO;
 		if(purPurchaseQuotationProductList !=null && purPurchaseQuotationProductList.size()>0) {
@@ -82,6 +92,7 @@ public class PurPurchaseQuotationServiceImpl extends ServiceImpl<PurPurchaseQuot
 	@Override
 	@Transactional(rollbackFor = Exception.class)
 	public void updateMain(PurPurchaseQuotation purPurchaseQuotation,List<PurPurchaseQuotationShip> purPurchaseQuotationShipList,List<PurPurchaseQuotationProduct> purPurchaseQuotationProductList) {
+		fillSuggestedInfo(purPurchaseQuotation, purPurchaseQuotationProductList);
 
 		BigDecimal totalAmount = BigDecimal.ZERO;
 		if(purPurchaseQuotationProductList !=null && purPurchaseQuotationProductList.size()>0) {
@@ -141,5 +152,75 @@ public class PurPurchaseQuotationServiceImpl extends ServiceImpl<PurPurchaseQuot
 			purPurchaseQuotationMapper.deleteById(id);
 		}
 	}
+
+	private void fillSuggestedInfo(PurPurchaseQuotation purPurchaseQuotation, List<PurPurchaseQuotationProduct> purPurchaseQuotationProductList) {
+		if (purPurchaseQuotationProductList == null || purPurchaseQuotationProductList.isEmpty()) {
+			return;
+		}
+		String supplierId = purPurchaseQuotation == null ? null : purPurchaseQuotation.getQuotationSuppiler();
+		String annual = getAnnualByBillDate(purPurchaseQuotation == null ? null : purPurchaseQuotation.getBillDate());
+		if (supplierId == null || supplierId.trim().isEmpty() || annual == null) {
+			for (PurPurchaseQuotationProduct product : purPurchaseQuotationProductList) {
+				if (product == null) {
+					continue;
+				}
+				product.setSuggestedPrice(null);
+				product.setSuggestedUnit(null);
+				product.setSuggestedId(null);
+			}
+			return;
+		}
+		Set<String> productIdSet = new HashSet<>();
+		for (PurPurchaseQuotationProduct product : purPurchaseQuotationProductList) {
+			if (product == null) {
+				continue;
+			}
+			String productId = product.getProductId();
+			if (productId != null && !productId.trim().isEmpty()) {
+				productIdSet.add(productId);
+			}
+		}
+		if (productIdSet.isEmpty()) {
+			return;
+		}
+		QueryWrapper<PurSuggestedPrice> queryWrapper = new QueryWrapper<>();
+		queryWrapper.eq("supplier", supplierId)
+				.eq("annual", annual)
+				.in("material_id", productIdSet);
+		List<PurSuggestedPrice> suggestedPriceList = purSuggestedPriceService.list(queryWrapper);
+		Map<String, PurSuggestedPrice> suggestedPriceMap = new HashMap<>();
+		if (suggestedPriceList != null && !suggestedPriceList.isEmpty()) {
+			for (PurSuggestedPrice suggestedPrice : suggestedPriceList) {
+				if (suggestedPrice == null || suggestedPrice.getMaterialId() == null) {
+					continue;
+				}
+				suggestedPriceMap.putIfAbsent(suggestedPrice.getMaterialId(), suggestedPrice);
+			}
+		}
+		for (PurPurchaseQuotationProduct product : purPurchaseQuotationProductList) {
+			if (product == null) {
+				continue;
+			}
+			PurSuggestedPrice suggestedPrice = suggestedPriceMap.get(product.getProductId());
+			if (suggestedPrice == null) {
+				product.setSuggestedPrice(null);
+				product.setSuggestedUnit(null);
+				product.setSuggestedId(null);
+				continue;
+			}
+			product.setSuggestedPrice(suggestedPrice.getPrice());
+			product.setSuggestedUnit(suggestedPrice.getUnit());
+			product.setSuggestedId(suggestedPrice.getId());
+		}
+	}
+
+	private String getAnnualByBillDate(java.util.Date billDate) {
+		if (billDate == null) {
+			return null;
+		}
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(billDate);
+		return String.valueOf(calendar.get(Calendar.YEAR));
+	}
 	
 }

+ 5 - 0
srm-module-code/src/main/java/org/jeecg/modules/saleCode/controller/SaleQuotationController.java

@@ -188,6 +188,9 @@ public class SaleQuotationController {
 
                 List<String> codes = saleQuotationList.stream().map(SaleInquiryForm::getInquiryProject).collect(Collectors.toList());
                 queryWrapper.in("quotation_Project",codes);
+            }else{
+
+                return Result.OK(null);
             }
         }
 
@@ -209,6 +212,8 @@ public class SaleQuotationController {
 
                 List<String> ids = productList.stream().map(SaleQuotationProduct::getHeadId).collect(Collectors.toList());
                 queryWrapper.in("id",ids);
+            }else{
+                return Result.OK(null);
             }
         }