diff --git a/src/main/java/com/xujie/sys/common/utils/ExcelTemplateYB.java b/src/main/java/com/xujie/sys/common/utils/ExcelTemplateYB.java new file mode 100644 index 00000000..3850d624 --- /dev/null +++ b/src/main/java/com/xujie/sys/common/utils/ExcelTemplateYB.java @@ -0,0 +1,750 @@ +package com.xujie.sys.common.utils; + +import lombok.Setter; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.*; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ExcelTemplateYB { + + final private static Pattern PATTERN_HDR_FIELD = Pattern.compile("\\$\\{(.*?)}", Pattern.MULTILINE); + final private static Pattern PATTERN_DTL_FIELD = Pattern.compile("#\\{(.*?)}", Pattern.MULTILINE); + + private XSSFWorkbook workbook; + private Map variables = new HashMap<>(); + private List> listVariables = new ArrayList<>(); + // 存储需要合并的单元格区域信息:[列表起始索引, 列表结束索引, 列索引] + private List mergeRegions = new ArrayList<>(); + // 存储需要设置行高的行信息:[起始行索引, 结束行索引, 行高] + private List rowHeights = new ArrayList<>(); + // 是否下移形状格式 + @Setter + private boolean moveShape = false; + // 是否下移印章(印章是图片) + @Setter + private boolean moveSeal = false; + // 是否设置单元格样式 + @Setter + private boolean cellStyle = false; + // 是否设置单元格样式 针对RFID不需要序号定制 + @Setter + private boolean cellStyle2 = false; + // 是否设置合并单元格样式 + @Setter + private boolean rangeStyle = false; + // 价格靠右 发票 + @Setter + private boolean priceRight = false; + // 价格靠右 发票 + @Setter + private boolean invoiceLie = false; + // 数字靠右 箱单 + @Setter + private boolean intRight = false; + // 报关单 + @Setter + private boolean delRight = false; + // 箱单 + @Setter + private boolean boxFlag = false; + + private ExcelTemplateYB(){} + + public static ExcelTemplateYB load(InputStream template) throws IOException { + ExcelTemplateYB excelTemplate = new ExcelTemplateYB(); + excelTemplate.workbook = new XSSFWorkbook(template); + return excelTemplate; + } + + public void addVar(String key, Object value){ + variables.put(key, value); + } + + public void addVarAll(Map values){ + variables.putAll(values); + } + + public void addListVar(Map row){ + listVariables.add(row); + } + + public void addListVarAll(Collection rows){ + listVariables.addAll(rows); + } + + /** + * 添加需要合并的单元格区域(基于列表索引) + * @param startListIndex 列表起始索引(相对于listVariables) + * @param endListIndex 列表结束索引(相对于listVariables) + * @param colIndex 列索引 + */ + public void addMergeRegion(int startListIndex, int endListIndex, int colIndex) { + if (startListIndex <= endListIndex) { + mergeRegions.add(new int[]{startListIndex, endListIndex, colIndex, colIndex, 0}); // 格式:[startRow, endRow, startCol, endCol, alignType] + } + } + + /** + * 添加合并区域,并指定对齐方式 + * @param startListIndex 起始行索引 + * @param endListIndex 结束行索引 + * @param colIndex 列索引 + * @param alignType 对齐方式:0=右对齐,1=左对齐,2=居中 + */ + public void addMergeRegion(int startListIndex, int endListIndex, int colIndex, int alignType) { + if (startListIndex <= endListIndex) { + mergeRegions.add(new int[]{startListIndex, endListIndex, colIndex, colIndex, alignType}); + } + } + + /** + * 添加跨列的合并区域(支持横向和纵向同时合并) + * @param startListIndex 起始行索引 + * @param endListIndex 结束行索引 + * @param startColIndex 起始列索引 + * @param endColIndex 结束列索引 + * @param alignType 对齐方式:0=右对齐,1=左对齐,2=居中 + */ + public void addMergeRegion(int startListIndex, int endListIndex, int startColIndex, int endColIndex, int alignType) { + if (startListIndex <= endListIndex && startColIndex <= endColIndex) { + mergeRegions.add(new int[]{startListIndex, endListIndex, startColIndex, endColIndex, alignType}); + } + } + + /** + * 设置行高 + * @param startListIndex 起始行索引(相对于listVariables) + * @param endListIndex 结束行索引(相对于listVariables) + * @param height 行高(单位:点) + */ + public void setRowHeight(int startListIndex, int endListIndex, int height) { + if (startListIndex <= endListIndex) { + rowHeights.add(new int[]{startListIndex, endListIndex, height}); + } + } + + public void clearAll(){ + variables.clear(); + listVariables.clear(); + mergeRegions.clear(); + rowHeights.clear(); + moveShape = false; + moveSeal = false; + cellStyle = false; + cellStyle2 = false; + rangeStyle = false; + priceRight = false; + intRight = false; + delRight = false; + } + + private boolean findAndRemoveMergedRegion(XSSFSheet sheet, int rowIndex) { + for (int mi = 0; mi < sheet.getMergedRegions().size(); mi++) { + if (sheet.getMergedRegions().get(mi).getFirstRow() == rowIndex) { + sheet.removeMergedRegion(mi); + return true; + } + } + return false; + } + + public XSSFWorkbook render(int index) throws IOException { + XSSFSheet sheet = workbook.getSheetAt(index); + CellCopyPolicy copyPolicy = new CellCopyPolicy(); + int dtlRowIndex = -1; + // find detail rows + for (int i = 0; i < sheet.getLastRowNum(); i++) { + XSSFRow row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + XSSFCell c = sheet.getRow(i).getCell(j); + if (c == null) { + continue; + } + String cellValue = c.toString(); + Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue); + if (matcherDtl.find()) { + dtlRowIndex = i; + break; + } + } + if (dtlRowIndex >= 0) { + break; + } + } + if (dtlRowIndex < sheet.getLastRowNum() && listVariables.size() > 1) { + //循环列表以下区域整体下移 + int _rows = sheet.getLastRowNum(); + for(int i = _rows; i > dtlRowIndex; i--){ + sheet.copyRows(i, i, i + listVariables.size() -1 , copyPolicy); + } + for(int i = 0; i < listVariables.size() - 1; i++) { + int destRowIndex = dtlRowIndex + 1 + i; + sheet.createRow(destRowIndex); //clear destination row + while(findAndRemoveMergedRegion(sheet, destRowIndex)){ + continue; + } + sheet.copyRows(dtlRowIndex, dtlRowIndex, destRowIndex, copyPolicy); + } + } + // 遍历所有形状 将列表下方的形状坐标也下移 + if (moveShape) { + int listStartRow = 24; // Excel 第25行(0-based) + int moveOffset = listVariables.size() - 1; + + for (POIXMLDocumentPart part : sheet.getRelations()) { + if (!(part instanceof XSSFDrawing)) { + continue; + } + + XSSFDrawing drawing = (XSSFDrawing) part; + for (XSSFShape shape : drawing.getShapes()) { + if (!(shape instanceof XSSFSimpleShape)) { + continue; + } + + XSSFSimpleShape simpleShape = (XSSFSimpleShape) shape; + ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor(); + + if (anchor != null && anchor.getRow1() >= listStartRow) { + anchor.setRow1(anchor.getRow1() + moveOffset); + if (anchor instanceof XSSFClientAnchor) { + XSSFClientAnchor xAnchor = (XSSFClientAnchor) anchor; + try { + xAnchor.setRow2(xAnchor.getRow2() + moveOffset); + } catch (Exception ignore) { + // row2 不存在,忽略 + } + } + } + } + } + } + + + + // 遍历所有图片 将列表下方的形状坐标也下移 + if (moveSeal) { + List relations = sheet.getRelations(); + for (POIXMLDocumentPart part : relations) { + if (part instanceof XSSFDrawing) { + XSSFDrawing drawing = (XSSFDrawing) part; + List shapes = drawing.getShapes(); + for (XSSFShape shape : shapes) { + // 只处理图片类型的形状 + if (shape instanceof XSSFPicture) { + XSSFPicture picture = (XSSFPicture) shape; + // 调整行坐标实现下移 + ClientAnchor anchor = (ClientAnchor) picture.getAnchor(); + if (anchor.getRow1() > 10) { // 只下移位于10行之后的图片 + anchor.setRow1(anchor.getRow1() + listVariables.size() - 1); + anchor.setRow2(anchor.getRow2() + listVariables.size() - 1); + } + + } + } + } + } + } + + Set dtlRows = new LinkedHashSet<>(); + List boxRows = new ArrayList<>(); + Set poNoCols = new HashSet<>(); + //整体填值 + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + XSSFRow row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + XSSFCell c = sheet.getRow(i).getCell(j); + if (c == null) { + continue; + } + + String cellValue = c.toString(); + if ("#{pn}".equals(cellValue) || "#{levy}".equals(cellValue) || "#{artNo}".equals(cellValue)) { + dtlRows.add(i); + } + if ("#{cmcInvoice}".equals(cellValue)) { + boxRows.add(i); + } + // 仅记录明细模板行上的po_no列,避免同名占位符误伤其它数字列 + if (i == dtlRowIndex && "#{po_no}".equalsIgnoreCase(cellValue)) { + poNoCols.add(j); + } + Matcher matcherHdr = PATTERN_HDR_FIELD.matcher(cellValue); + String result = c.getStringCellValue(); + while (matcherHdr.find()) { + for (int ri = 1; ri <= matcherHdr.groupCount(); ri++) { + String field = matcherHdr.group(ri); + Object value = variables.getOrDefault(field, ""); + result = result.replace(matcherHdr.group(0), String.valueOf(value)); + } + if ("${phone1}".equals(cellValue) || "${phone2}".equals(cellValue) || "${hs_code}".equals(cellValue)) { + c.setCellValue(result); // 字符串 + } else { + try { + // 更严格的数字检查 + String cleanResult = result.replace(",", "").trim(); + if (!cleanResult.isEmpty() && !cleanResult.equals("-") && !cleanResult.equals(".")) { + double num = Double.parseDouble(cleanResult); + if (Double.isFinite(num)) { + c.setCellValue(num); // 数值 + // goods_total_qty 根据实际小数位动态设置格式 + if ("${goods_total_qty}".equals(cellValue)) { + XSSFCellStyle fmtStyle = workbook.createCellStyle(); + fmtStyle.cloneStyleFrom(c.getCellStyle()); + DataFormat dataFormat = workbook.createDataFormat(); + fmtStyle.setDataFormat(dataFormat.getFormat(buildDecimalFormat(num))); + c.setCellStyle(fmtStyle); + } + } else { + c.setCellValue(result); // 字符串 + } + } else { + c.setCellValue(result); // 字符串 + } + } catch (NumberFormatException e) { + c.setCellValue(result); // 字符串 + } + } + } + + Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue); + while (matcherDtl.find()) { + for (int ri = 1; ri <= matcherDtl.groupCount(); ri++) { + String field = matcherDtl.group(ri); + // 检查数组边界,避免越界异常 + int listIndex = i - dtlRowIndex; + if (listIndex >= 0 && listIndex < listVariables.size()) { + c.setCellValue(cellValue.replace(matcherDtl.group(0), String.valueOf(listVariables.get(listIndex).getOrDefault(field, "")))); + } + } + } + + // 设置样式 + if (cellStyle && dtlRowIndex >= 0 && i >= dtlRowIndex && i < dtlRowIndex + listVariables.size()) { + XSSFCellStyle style = c.getCellStyle(); + style.setBorderBottom(BorderStyle.NONE); + style.setBorderTop(BorderStyle.NONE); + style.setWrapText(true); + style.setAlignment(HorizontalAlignment.LEFT); + c.setCellStyle(style); + if (rangeStyle && j < 4) { + for (int mi = 0; mi < 3; mi++) { + XSSFCell nextc = sheet.getRow(i).getCell(c.getColumnIndex()+mi+1); + if (nextc == null) { + continue; + } + nextc.setCellStyle(style); + } + } + } + } + } + // 设置明细样式,因为明细是动态添加的,会有样式和模版偏差,此处主要是第2、3列的样式需要设置 + if (cellStyle) { + for (Integer dtlRow : dtlRows) { + XSSFCell c1 = sheet.getRow(dtlRow).getCell(cellStyle2?0:1); + if (c1 == null) { + continue; + } + XSSFCellStyle style = workbook.createCellStyle(); + style.setBorderRight(BorderStyle.NONE); + style.setBorderLeft(BorderStyle.MEDIUM); // 实线 + style.setBorderBottom(BorderStyle.NONE); + style.setBorderTop(BorderStyle.NONE); + Font font = workbook.createFont(); + font.setFontName("Arial"); // 设置字体 + font.setFontHeightInPoints((short) 10); // 设置字号 + style.setFont(font); + style.setVerticalAlignment(VerticalAlignment.TOP); + style.setWrapText(true); + c1.setCellStyle(style); + + XSSFCell c2 = sheet.getRow(dtlRow).getCell(cellStyle2?1:2); + if (c2 == null) { + continue; + } + XSSFCellStyle style2 = workbook.createCellStyle(); + style2.setBorderRight(BorderStyle.MEDIUM); + style2.setBorderLeft(BorderStyle.NONE); + style2.setBorderBottom(BorderStyle.NONE); + style2.setBorderTop(BorderStyle.NONE); + Font font2 = workbook.createFont(); + font2.setFontName("Arial"); // 设置字体 + font2.setFontHeightInPoints((short) 10); // 设置字号 + style2.setFont(font2); + style2.setVerticalAlignment(VerticalAlignment.TOP); + style2.setAlignment(HorizontalAlignment.LEFT); + style2.setWrapText(true); + c2.setCellStyle(style2); + if (priceRight) { //仅供发票excel使用,第6、7、8是价格列居右 + for (int i = 6; i < (invoiceLie?11:9); i++) { + XSSFRow row = sheet.getRow(dtlRow); + if (row == null) { + continue; + } + XSSFCell c7 = row.getCell(i); + if (c7 == null) { + continue; + } + + // 尝试把字符串转成数值 + if (c7.getCellType() == CellType.STRING) { + String strVal = c7.getStringCellValue(); + if (strVal != null && !strVal.trim().isEmpty()) { + try { + double num = Double.parseDouble(strVal.replace(",", "")); + c7.setCellValue(num); // 转换为数值写回 + } catch (NumberFormatException e) { + // 如果不是数字就保留原字符串 + System.out.println("非数字,保持原值: " + strVal); + } + } + } + + // 创建样式 + XSSFCellStyle style7 = workbook.createCellStyle(); + style7.setBorderRight(BorderStyle.MEDIUM); + style7.setBorderLeft(BorderStyle.NONE); + style7.setBorderBottom(BorderStyle.NONE); + style7.setBorderTop(BorderStyle.NONE); + + Font font7 = workbook.createFont(); + font7.setFontName("Arial"); + font7.setFontHeightInPoints((short) 10); + style7.setFont(font7); + + style7.setVerticalAlignment(VerticalAlignment.TOP); + style7.setAlignment(HorizontalAlignment.RIGHT); + + // 设置千分位格式(根据单元格实际小数位动态决定格式) + DataFormat dataFormat = workbook.createDataFormat(); + String numFmt; + if (c7.getCellType() == CellType.NUMERIC) { + numFmt = buildDecimalFormat(c7.getNumericCellValue()); + } else { + numFmt = "#,##0.00"; + } + style7.setDataFormat(dataFormat.getFormat(numFmt)); + + c7.setCellStyle(style7); + } + } + if (intRight) { //仅供箱单excel使用 + // 覆盖到第10列(索引9),保证9、10列按数字处理 + for (int i = 4; i < 11; i++) { + XSSFRow row = sheet.getRow(dtlRow); + if (row == null) { + continue; + } + XSSFCell c7 = row.getCell(i); + if (c7 == null) { + continue; + } + boolean isPoNoColumn = poNoCols.contains(i); + BigDecimal numericValue = null; + // 尝试把字符串转成数值 + if (!isPoNoColumn && c7.getCellType() == CellType.STRING) { + String strVal = c7.getStringCellValue(); + BigDecimal parsed = parseNumericString(strVal); + if (parsed != null) { + numericValue = parsed; + c7.setCellValue(parsed.doubleValue()); // 转换为数值写回 + } + } else if (!isPoNoColumn && c7.getCellType() == CellType.NUMERIC) { + numericValue = BigDecimal.valueOf(c7.getNumericCellValue()); + } + + // 创建样式 + XSSFCellStyle style7 = workbook.createCellStyle(); + style7.setBorderRight(BorderStyle.MEDIUM); + style7.setBorderLeft(i==4?BorderStyle.MEDIUM:BorderStyle.NONE); + style7.setBorderBottom(BorderStyle.NONE); + style7.setBorderTop(BorderStyle.NONE); + + // 判断单元格的值是否小于等于0,如果小于等于0则加粗 + boolean isBold = false; + if (numericValue != null && numericValue.compareTo(BigDecimal.ZERO) <= 0) { + isBold = true; + } + + Font font7 = workbook.createFont(); + font7.setFontName("Arial"); + font7.setFontHeightInPoints((short) 10); + if (isBold) { + font7.setBold(true); + } + style7.setFont(font7); + + style7.setVerticalAlignment(VerticalAlignment.TOP); + style7.setAlignment(HorizontalAlignment.CENTER); + DataFormat dataFormat = workbook.createDataFormat(); + if (isPoNoColumn) { + // po_no 是编号列,必须按文本处理,不能套用千分位 + style7.setDataFormat(dataFormat.getFormat("@")); + } else { + // 设置千分位格式(根据单元格实际小数位动态决定格式) + String numFmt2; + if (numericValue != null) { + // 去掉末尾0后按实际小数位显示:20.000000 -> 20,0.150000 -> 0.15 + numFmt2 = buildDecimalFormatByDecimal(numericValue); + } else if (c7.getCellType() == CellType.NUMERIC) { + numFmt2 = buildDecimalFormat(c7.getNumericCellValue()); + } else { + numFmt2 = "#,##0"; + } + style7.setDataFormat(dataFormat.getFormat(numFmt2)); + } + c7.setCellStyle(style7); + } + + // net_weight列(J列,索引9)保持顶部右对齐 + /*XSSFRow row = sheet.getRow(dtlRow); + if (row != null) { + XSSFCell netWeightCell = row.getCell(9); + if (netWeightCell != null) { + XSSFCellStyle netWeightStyle = workbook.createCellStyle(); + netWeightStyle.cloneStyleFrom(netWeightCell.getCellStyle()); + netWeightStyle.setVerticalAlignment(VerticalAlignment.TOP); + netWeightStyle.setAlignment(HorizontalAlignment.RIGHT); + netWeightCell.setCellStyle(netWeightStyle); + } + }*/ + } + } + } + if (delRight) { + for (Integer dtlRow : dtlRows) { + for (int i = 0; i < 17; i++) { + XSSFRow row = sheet.getRow(dtlRow); + if (row == null) { + continue; + } + XSSFCell c7 = row.getCell(i); + if (c7 == null) { + continue; + } + // 创建样式 + XSSFCellStyle style = workbook.createCellStyle(); + style.setBorderRight(i==16?BorderStyle.THIN:BorderStyle.NONE); + style.setBorderLeft(i==0?BorderStyle.THIN:BorderStyle.NONE); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderTop(BorderStyle.NONE); + Font font = workbook.createFont(); + font.setFontName("Arial"); + font.setFontHeightInPoints((short) 10); + style.setFont(font); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setAlignment(HorizontalAlignment.LEFT); + c7.setCellStyle(style); + if (i==6 || i==11) { + // 尝试把字符串转成数值 + if (c7.getCellType() == CellType.STRING) { + String strVal = c7.getStringCellValue(); + if (strVal != null && !strVal.trim().isEmpty()) { + try { + double num = Double.parseDouble(strVal.replace(",", "")); + c7.setCellValue(num); // 转换为数值写回 + } catch (NumberFormatException e) { + // 如果不是数字就保留原字符串 + System.out.println("非数字,保持原值: " + strVal); + } + } + } + // 创建样式 + XSSFCellStyle style7 = workbook.createCellStyle(); + style7.setBorderRight(BorderStyle.NONE); + style7.setBorderLeft(BorderStyle.NONE); + style7.setBorderBottom(BorderStyle.THIN); + style7.setBorderTop(BorderStyle.NONE); + Font font7 = workbook.createFont(); + font7.setFontName("Arial"); + font7.setFontHeightInPoints((short) 10); + style7.setFont(font7); + style7.setVerticalAlignment(VerticalAlignment.CENTER); + style7.setAlignment(HorizontalAlignment.RIGHT); + // 设置千分位格式(根据单元格实际小数位动态决定格式) + DataFormat dataFormat = workbook.createDataFormat(); + String numFmt3 = (c7.getCellType() == CellType.NUMERIC) + ? buildDecimalFormat(c7.getNumericCellValue()) + : "#,##0.00"; + style7.setDataFormat(dataFormat.getFormat(numFmt3)); + c7.setCellStyle(style7); + } + } + } + } + if (boxFlag) { + for (Integer dtlRow : boxRows) { + for (int i = 1; i < 6; i++) { + if (i==1 || i==4 || i==5) { + XSSFRow row = sheet.getRow(dtlRow); + if (row == null) { + continue; + } + XSSFCell c7 = row.getCell(i); + if (c7 == null) { + continue; + } + // 尝试把字符串转成数值 + if (c7.getCellType() == CellType.STRING) { + String strVal = c7.getStringCellValue(); + if (strVal != null && !strVal.trim().isEmpty()) { + try { + double num = Double.parseDouble(strVal.replace(",", "")); + c7.setCellValue(num); // 转换为数值写回 + } catch (NumberFormatException e) { + // 如果不是数字就保留原字符串 + System.out.println("非数字,保持原值: " + strVal); + } + } + } + // 创建样式 + XSSFCellStyle style7 = workbook.createCellStyle(); + style7.setBorderRight(BorderStyle.THIN); + style7.setBorderLeft(BorderStyle.THIN); + style7.setBorderBottom(BorderStyle.THIN); + style7.setBorderTop(BorderStyle.THIN); + + Font font7 = workbook.createFont(); + font7.setFontName("DengXian"); // 等线 + font7.setFontHeightInPoints((short) 11); // 11号 + style7.setFont(font7); + + style7.setVerticalAlignment(VerticalAlignment.CENTER); + style7.setAlignment(HorizontalAlignment.RIGHT); + + // 设置千分位格式(根据单元格实际小数位动态决定格式) + DataFormat dataFormat = workbook.createDataFormat(); + String numFmt4 = (c7.getCellType() == CellType.NUMERIC) + ? buildDecimalFormat(c7.getNumericCellValue()) + : "#,##0"; + style7.setDataFormat(dataFormat.getFormat(numFmt4)); + c7.setCellStyle(style7); + } + } + } + } + + // 处理合并单元格 - 将列表索引转换为实际行号并合并 + if (dtlRowIndex >= 0 && !mergeRegions.isEmpty()) { + for (int[] region : mergeRegions) { + int startRow = dtlRowIndex + region[0]; + int endRow = dtlRowIndex + region[1]; + int startCol = region[2]; + int endCol = region.length >= 4 ? region[3] : region[2]; // 支持跨列,如果没有指定endCol则等于startCol + boolean needMerge = startRow < endRow || startCol < endCol; + if (needMerge) { + // 创建新的合并区域(支持横向和纵向合并) + CellRangeAddress mergeRange = new CellRangeAddress(startRow, endRow, startCol, endCol); + + // 检查并删除与新合并区域冲突的已存在合并区域 + List toRemove = new ArrayList<>(); + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { + CellRangeAddress existingRegion = sheet.getMergedRegion(i); + if (existingRegion != null && mergeRange.intersects(existingRegion)) { + toRemove.add(i); + } + } + // 从后往前删除,避免索引变化 + for (int i = toRemove.size() - 1; i >= 0; i--) { + sheet.removeMergedRegion(toRemove.get(i)); + } + + // 添加新的合并区域 + sheet.addMergedRegion(mergeRange); + } + + // 根据是否实际合并设置对齐方式: + // 1) 已合并:垂直居中 + 水平居中 + // 2) 未合并(单格):垂直顶对齐 + 水平居中 + XSSFRow row = sheet.getRow(startRow); + if (row != null) { + XSSFCell cell = row.getCell(startCol); + if (cell != null) { + XSSFCellStyle mergeStyle = workbook.createCellStyle(); + mergeStyle.cloneStyleFrom(cell.getCellStyle()); + mergeStyle.setVerticalAlignment(needMerge ? VerticalAlignment.CENTER : VerticalAlignment.TOP); + mergeStyle.setAlignment(HorizontalAlignment.CENTER); + cell.setCellStyle(mergeStyle); + } + } + } + } + + // 处理行高设置 + if (dtlRowIndex >= 0 && !rowHeights.isEmpty()) { + for (int[] heightInfo : rowHeights) { + int startRow = dtlRowIndex + heightInfo[0]; + int endRow = dtlRowIndex + heightInfo[1]; + int height = heightInfo[2]; + + for (int rowIdx = startRow; rowIdx <= endRow; rowIdx++) { + XSSFRow row = sheet.getRow(rowIdx); + if (row != null) { + // 设置行高(Excel行高单位是1/20点,所以需要乘以20) + row.setHeight((short) (height * 20)); + } + } + } + } + + return workbook; + } + + private BigDecimal parseNumericString(String value) { + if (value == null) { + return null; + } + String clean = value.replace(",", "").trim(); + if (clean.isEmpty() || "-".equals(clean) || ".".equals(clean)) { + return null; + } + try { + return new BigDecimal(clean); + } catch (NumberFormatException e) { + return null; + } + } + + private String buildDecimalFormatByDecimal(BigDecimal value) { + if (value == null) { + return "#,##0"; + } + int scale = Math.max(0, value.stripTrailingZeros().scale()); + if (scale <= 0) { + return "#,##0"; + } + return "#,##0." + "0".repeat(scale); + } + + /** + * 根据数值的实际小数位数动态生成 Excel 数字格式串(带千分位)。 + * 例:1.0 → "#,##0",12.34 → "#,##0.00",0.00123 → "#,##0.00000" + * 使用 Double.toString() 转 BigDecimal 再去尾零,避免浮点误差导致位数虚高。 + */ + private String buildDecimalFormat(double value) { + if (value == 0) { + return "#,##0"; + } + BigDecimal bd = new BigDecimal(Double.toString(Math.abs(value))).stripTrailingZeros(); + int scale = Math.max(0, bd.scale()); + if (scale == 0) { + return "#,##0"; + } + return "#,##0." + "0".repeat(scale); + } +} diff --git a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelTXServiceImpl.java b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelTXServiceImpl.java index 7d328666..9b94b722 100644 --- a/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelTXServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelTXServiceImpl.java @@ -6,6 +6,7 @@ import com.aspose.cells.PaperSizeType; import com.aspose.cells.SaveFormat; import com.xujie.sys.common.utils.DateUtils; import com.xujie.sys.common.utils.ExcelTemplateTX; +import com.xujie.sys.common.utils.ExcelTemplateYB; import com.xujie.sys.modules.ecss.data.*; import com.xujie.sys.modules.ecss.dto.SheetErrorInfo; import com.fasterxml.jackson.core.type.TypeReference; @@ -913,11 +914,20 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { try { EcssCoDelNotifyHeaderData notifyHeader = coDelMapper.getEcssCoDelNotifyHeader(data.getSite(), data.getDelNo()); // RFID和RF不需要序号列(declaration-packingList-template.xlsx),其他需要序列号(declaration-packingList2-template.xlsx) - String xlsx = "templates/TX/declaration-packingList-template.xlsx"; - ExcelTemplateTX template = ExcelTemplateTX.load(new ClassPathResource(xlsx).getInputStream()); - exportPackingList(data, template, notifyHeader, 0); - try (XSSFWorkbook workbook = template.render(0)) { - workbook.write(response.getOutputStream()); + if ("04-MHM".equalsIgnoreCase(notifyHeader.getBuNo())) { + String xlsx = "templates/TX/declaration-packingList-template.xlsx"; + ExcelTemplateTX template = ExcelTemplateTX.load(new ClassPathResource(xlsx).getInputStream()); + exportPackingList(data, template, notifyHeader, 0); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } + } else { + String xlsx = "templates/YB/declaration-packingList-template.xlsx"; + ExcelTemplateYB template = ExcelTemplateYB.load(new ClassPathResource(xlsx).getInputStream()); + exportPackingListYB(data, template, notifyHeader, 0); + try (XSSFWorkbook workbook = template.render(0)) { + workbook.write(response.getOutputStream()); + } } response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment; filename=\"danzheng.xlsx\""); @@ -1434,7 +1444,8 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); template.addVar("invoiceTitle", data.getContractFlag()!=null && data.getContractFlag()? "合同" : "INVOICE No."); template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); - template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); + template.addVar("dateStr", notifyHeader.getReadyDate() == null ? "" : + new java.text.SimpleDateFormat("d-MMM-yyyy", java.util.Locale.ENGLISH).format(notifyHeader.getReadyDate())); List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); String currency = "CNY"; if (!detailList.isEmpty()) { @@ -1453,9 +1464,6 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); Map notifyDetailMap = notifyDetailList.stream().collect( Collectors.toMap(EcssCoDelNotifyDetailData::getPartNo,e->e)); - // 装箱明细 - 按发货通知单明细item_no分组(用于获取每行的重量) - List palletDetailList = coDelMapper.exportCoDelPalletDetailGroupByItemNo(notifyHeader); - //Map palletMap = palletDetailList.stream().collect(Collectors.toMap( o -> o.get("item_no"), o -> o)); // 装箱数据 List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader);// 总托数 Integer totalPlt = palletHeaderDataList.stream() @@ -1481,8 +1489,6 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { eorder.put("row_num", i + 1); EcssCoDelNotifyDetailData nodifyData = notifyDetailMap.get(partNo); // 根据item_no获取该明细行对应的装箱重量 - //Map pm = itemNo != null ? palletMap.get(itemNo) : null; - //int totalQty = pm!=null && pm.get("total_qty")!=null?Integer.parseInt(pm.get("total_qty").toString()):0; String lossratio = ""; if (nodifyData!=null && nodifyData.getLossratio()!=null && !StringUtils.isBlank(nodifyData.getLossratio())) { // 关务物料属性 @@ -1520,10 +1526,6 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { allPrice = allPrice.add( ((BigDecimal)eorder.get("ttl_amount"))); if (data.getShowWeight()!=null && data.getShowWeight()) { // 根据发货通知单明细查找装箱明细,获取重量和净重,保留2位小数 - /* eorder.put("grossWeight", pm != null && pm.get("gross_weight") != null ? - new BigDecimal(pm.get("gross_weight").toString()).setScale(2, RoundingMode.HALF_UP) : ""); - eorder.put("netWeight", pm != null && pm.get("net_weight") != null ? - new BigDecimal(pm.get("net_weight").toString()).setScale(2, RoundingMode.HALF_UP) : "");*/ } } // RFID需要的 @@ -1567,7 +1569,6 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { private void exportPackingList(EcssDeclarationHeaderData data, ExcelTemplateTX template, EcssCoDelNotifyHeaderData notifyHeader,int type) { - EcssDeclarationHeaderData ecHeader = coDelMapper.getDeclarationHeader(data); List notifyDetailList; if (type==0) { coDelMapper.updateEcssDeclarationHeader(data); @@ -1592,15 +1593,13 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); - template.addVar("dateStr", DateUtils.format(notifyHeader.getReadyDate(), "yyyy-MM-dd")); + template.addVar("dateStr", notifyHeader.getReadyDate() == null ? "" : + new java.text.SimpleDateFormat("d-MMM-yyyy", java.util.Locale.ENGLISH).format(notifyHeader.getReadyDate())); template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); Map poNoMap = new HashMap<>(); // 装箱数据 List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); - // 最高的栈板 - /*Optional palletHeaderData = palletHeaderDataList.isEmpty()? Optional.empty() - :palletHeaderDataList.stream().max(Comparator.comparing(EcssCoDelPalletHeaderData::getHeight));*/ // 总托数 Integer totalPlt = palletHeaderDataList.stream() .map(EcssCoDelPalletHeaderData::getPalletQty) @@ -1811,12 +1810,255 @@ public class CoDelExcelTXServiceImpl implements CoDelExcelTXService { totalVolume = calculateVolumeByMaterials(notifyHeader); } template.addVar("Measurement", totalVolume); - /* if (data.getHighPalletFlag()!=null && data.getHighPalletFlag() && palletHeaderData.isPresent()) { - template.addVar("volume", palletHeaderData.map(ecssCoDelPalletHeaderData -> - ecssCoDelPalletHeaderData.getLength().setScale(2, RoundingMode.HALF_UP) - + "*" + ecssCoDelPalletHeaderData.getWidth().setScale(2, RoundingMode.HALF_UP) + "*" - + ecssCoDelPalletHeaderData.getHeight().setScale(2, RoundingMode.HALF_UP)).orElse("")); - }*/ + template.addListVarAll(exportList); + } + + private void exportPackingListYB(EcssDeclarationHeaderData data, ExcelTemplateYB template, + EcssCoDelNotifyHeaderData notifyHeader,int type) { + List notifyDetailList; + if (type==0) { + coDelMapper.updateEcssDeclarationHeader(data); + notifyDetailList = data.getNotifyPartDetailList(); + for (EcssCoDelNotifyDetailData nData:notifyDetailList){ + coDelMapper.updateEcssCoDelNotifyDetail(nData); + } + } + template.setCellStyle(true); + template.setRangeStyle(true); + template.setMoveSeal(true); + template.setIntRight(true); + if (notifyHeader.getBuNo().equals("03-RFID") || notifyHeader.getBuNo().equals("01-Label")){ + template.setCellStyle2(true); + } + template.addVar("remark", data.getXdremark()); + template.addVar("localShipper", notifyHeader.getCustomerName()); + template.addVar("localShipAddress", notifyHeader.getLocalShipAddress()); + template.addVar("ccusname", stringInput(notifyHeader.getOverseasShipper())); + template.addVar("cDeliverAdd", stringInput(notifyHeader.getOverseasAddress())); + template.addVar("dateStr", notifyHeader.getReadyDate() == null ? "" : + new java.text.SimpleDateFormat("d-MMM-yyyy", java.util.Locale.ENGLISH).format(notifyHeader.getReadyDate())); + template.addVar("cmc_invoice", notifyHeader.getCmcInvoice()); + template.addVar("shippingMode", stringInput(notifyHeader.getShippingMode())); + Map poNoMap = new HashMap<>(); + // 装箱数据 + List palletHeaderDataList = coDelMapper.searchEcssCoDelPalletHeaderData(notifyHeader); + // 总托数 + Integer totalPlt = palletHeaderDataList.stream() + .map(EcssCoDelPalletHeaderData::getPalletQty) + .filter(Objects::nonNull) // 防止空指针 + .reduce(0, Integer::sum); + // 发货通知单明细 + List detailList = coDelMapper.exportEcssCoDelNotifyDetail(data); + // 获取poNo + for (int i = 0; i < detailList.size(); i++) { + Map eorder = detailList.get(i); + poNoMap.put(eorder.get("customerPO"), eorder.get("customerPO")); + } + StringBuilder ponos = new StringBuilder(); + poNoMap.forEach((key, value) -> ponos.append(key + " ")); + template.addVar("poNo", ponos); + List list = coDelMapper.selectBoxList(notifyHeader); + // DB中item_no可能是字符串区间(如"127~127"、"2~26"),直接查询顺序可能不稳定,导出前做自然排序 + sortBoxListBySeqAndItemNo(list); + BigDecimal totalCartons = BigDecimal.valueOf(0.0); + BigDecimal grossWeight = BigDecimal.valueOf(0.0); + BigDecimal netWeight = BigDecimal.valueOf(0.0); + BigDecimal rolls = BigDecimal.valueOf(0.0); + BigDecimal totalQty = BigDecimal.valueOf(0.0); + // 托盘序号 -> 托盘数(用于托盘数列展示) + Map palletQtyMap = palletHeaderDataList.stream() + .filter(palletHeader -> palletHeader.getSeqNo() != null) + .collect(Collectors.toMap( + palletHeader -> palletHeader.getSeqNo().toString(), + palletHeader -> palletHeader.getPalletQty() != null ? palletHeader.getPalletQty() : 1, + (existing, replacement) -> existing, + LinkedHashMap::new + )); + // 托盘序号 -> 托号(第一列展示,来源ecss_CoDelPalletHeader.pallet_no) + Map palletNoMap = palletHeaderDataList.stream() + .filter(palletHeader -> palletHeader.getSeqNo() != null) + .collect(Collectors.toMap( + palletHeader -> palletHeader.getSeqNo().toString(), + palletHeader -> StringUtils.defaultString(palletHeader.getPalletNo()), + (existing, replacement) -> existing, + LinkedHashMap::new + )); + // 记录每个托盘是否已经在明细中显示过托盘数(只显示在该托盘首行) + Set displayedPalletSeqSet = new HashSet<>(); + List exportList = new ArrayList<>();//全部需要导出的明细 + EcssCoDelPalletHeaderData boxData = new EcssCoDelPalletHeaderData(); + boxData.setSite(data.getSite()); + boxData.setBuNo(notifyHeader.getBuNo()); + boxData.setDelNo(notifyHeader.getDelNo()); + List allBoxDetailList = coDelMapper.selectPalletDetailList(boxData); + Map> boxDetailMap = allBoxDetailList == null ? new HashMap<>() : + allBoxDetailList.stream() + .filter(detail -> detail.get("seq_no") != null) + .collect(Collectors.groupingBy(detail -> detail.get("seq_no").toString())); + int palletStartIndex = -1; + String previousPalletSeqNo = null; + for (int m = 0; m < list.size(); m++) { + String boxNo = list.get(m).get("item_no") != null ? list.get(m).get("item_no").toString() : ""; + String palletSeqNo = list.get(m).get("seq_no") != null ? list.get(m).get("seq_no").toString() : ""; + String palletNo = StringUtils.defaultString(palletNoMap.get(palletSeqNo), palletSeqNo); + List checkList = boxDetailMap.getOrDefault(boxNo, Collections.emptyList()); + // 记录当前箱子对应的exportList起始索引 + int boxStartIndex = exportList.size(); + // 当前托盘的起始行(用于托盘数列跨箱合并) + if (!Objects.equals(palletSeqNo, previousPalletSeqNo)) { + palletStartIndex = boxStartIndex; + } + // 获取box的rolls + BigDecimal boxRolls = list.get(m).get("rolls") != null ? + ((BigDecimal) list.get(m).get("rolls")).setScale(0, RoundingMode.HALF_UP) : BigDecimal.ZERO; + // 获取box的volume + BigDecimal volume = list.get(m).get("volume") != null ? + ((BigDecimal) list.get(m).get("volume")).setScale(6, RoundingMode.HALF_UP) : BigDecimal.ZERO; + for (int i = 0; i < checkList.size(); i++) { + Map eorder = checkList.get(i); + BigDecimal qty = ((BigDecimal)eorder.get("qty")).setScale(6, RoundingMode.HALF_UP); + totalQty = totalQty.add(qty); + eorder.put("total_qty", ((BigDecimal)eorder.get("qty")).setScale(6, RoundingMode.HALF_UP)); + // 合箱时:第一行显示数值,用于合并单元格居中显示 + boolean isFirstRow = (i == 0); + eorder.put("noCartons",((BigDecimal)eorder.get("box_qty")).setScale(0, RoundingMode.HALF_UP)); + eorder.put("qty_percarton", ((BigDecimal)eorder.get("rolls")).setScale(0, RoundingMode.HALF_UP)); + // 托盘数:一个托盘对应多个箱,仅在该托盘第一条明细显示,其他行留空,后续按托盘合并 + boolean showPalletQty = StringUtils.isNotEmpty(palletSeqNo) && !displayedPalletSeqSet.contains(palletSeqNo); + if (showPalletQty) { + displayedPalletSeqSet.add(palletSeqNo); + } + Integer palletQty = palletQtyMap.get(palletSeqNo); + // 第一列展示托号(pallet_no),按托盘首行显示,后续行留空再做按托合并 + eorder.put("seq_no", showPalletQty ? palletNo : ""); + eorder.put("total_pallets", showPalletQty ? (palletQty != null ? palletQty : 1) : ""); + eorder.put("volume", isFirstRow ? volume : ""); + eorder.put("gross_weight", isFirstRow ? ((BigDecimal) list.get(m).get("gross_weight")).setScale(2, RoundingMode.HALF_UP) : ""); + eorder.put("net_weight", isFirstRow ? ((BigDecimal) list.get(m).get("net_weight")).setScale(2, RoundingMode.HALF_UP) : ""); + + // rolls处理:如果明细的rolls全部为空或0,则取box的rolls;合箱时合并居中显示 + // 使用box的rolls,合箱时只在第一行显示(用于合并单元格居中) + + if (isFirstRow) { + rolls = rolls.add(boxRolls); + } + // 构建artNo内容 + String artNoContent = eorder.get("pn")+" " + eorder.get("part_description") + "\n" + + " PO#" + eorder.get("po_no")+" HS CODE#" + eorder.get("hsCode"); + eorder.put("artNo", artNoContent); + // 计算artNo内容的行数,动态设置行高 + int lineCount = calculateLineCount(artNoContent); + // 基础行高16点 + 每额外行增加15点(可根据实际字体大小调整) + int rowHeight = 16 + (lineCount - 1) * 15; + // 设置当前行的行高(exportList的当前索引) + template.setRowHeight(exportList.size(), exportList.size(), rowHeight); + + exportList.add(eorder); + } + // 如果当前箱子对应多个物料(合箱),添加合并单元格区域 + int boxEndIndex = exportList.size() - 1; + if (boxEndIndex >= boxStartIndex) { + // 箱级字段按箱明细范围合并(一个箱可能有多条箱明细) + // 净重列(倒数第2列,索引7) + template.addMergeRegion(boxStartIndex, boxEndIndex, 9); + // 毛重列(倒数第1列,索引8) + template.addMergeRegion(boxStartIndex, boxEndIndex, 10); + } + // 托盘数列按托盘(seq_no)跨箱合并:一个托盘下多个箱共享同一托盘数 + String nextPalletSeqNo = ""; + if (m + 1 < list.size()) { + Object nextSeqNo = list.get(m + 1).get("seq_no"); + nextPalletSeqNo = nextSeqNo != null ? nextSeqNo.toString() : ""; + } + boolean isCurrentPalletEnd = (m == list.size() - 1) || !Objects.equals(palletSeqNo, nextPalletSeqNo); + if (isCurrentPalletEnd && palletStartIndex >= 0 && boxEndIndex >= palletStartIndex) { + // 第一列托号按托盘合并(一个托可能有多个箱) + template.addMergeRegion(palletStartIndex, boxEndIndex, 0); + // 托盘列(倒数第3列,索引6)按托盘合并 + template.addMergeRegion(palletStartIndex, boxEndIndex, 8); + } + previousPalletSeqNo = palletSeqNo; + totalCartons = totalCartons.add(list.get(m).get("box_qty") !=null? new BigDecimal(list.get(m).get("box_qty").toString()) : BigDecimal.valueOf(0.0)); + grossWeight = grossWeight.add(list.get(m).get("gross_weight") !=null?new BigDecimal(list.get(m).get("gross_weight").toString()):BigDecimal.valueOf(0.0)); + netWeight = netWeight.add(list.get(m).get("net_weight") !=null?new BigDecimal(list.get(m).get("net_weight").toString()):BigDecimal.valueOf(0.0)); + } + // 托盘重量=根据每个pallet的重量*数量累加 + BigDecimal palletWeight = BigDecimal.ZERO; + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + if (palletHeader.getPallet() != null && !palletHeader.getPallet().isEmpty()) { + // 根据pallet编号查询EcssPallet信息 + List palletDataList = coDelMapper.getPallet(notifyHeader.getSite(), notifyHeader.getBuNo(), palletHeader.getPallet()); + if (!palletDataList.isEmpty()) { + EcssPalletData palletData = palletDataList.get(0); + if (palletData.getPalletWeight() != null && palletHeader.getPalletQty() != null) { + // 栈板重量 = 单个栈板重量 * 数量 + palletWeight = palletWeight.add(palletHeader.getVolume()); + } + } + } + } + template.addVar("Total_Cartons", totalCartons.setScale(0, RoundingMode.HALF_UP)); + template.addVar("Gross_Weight", (grossWeight.add(palletWeight)).setScale(2, RoundingMode.HALF_UP)); + template.addVar("Net_Weight", netWeight.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_grossweight", grossWeight.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_netweight", netWeight.setScale(2, RoundingMode.HALF_UP)); + template.addVar("total_box", totalCartons.setScale(0, RoundingMode.HALF_UP)); + template.addVar("goods_total_qty", totalQty); + template.addVar("total_rolls", rolls.setScale(0, RoundingMode.HALF_UP)); + // 下面是可选的或者手动维护的 + // RFID需要的 + if (notifyHeader.getBuNo().equals("01-Label") || notifyHeader.getBuNo().equals("03-RFID")) { + template.addVar("total:", "total:"); + template.addVar("madein", stringInput(data.getOrigin())); + template.addVar("shippingNo", "shipping no"); + template.addVar("sp_cmc_invoice", notifyHeader.getCmcInvoice()); + // 如果palletWeight==0,不显示 + template.addVar("pallet_weight_name", palletWeight.compareTo(BigDecimal.ZERO)==0?"":"pallet weight:"); + template.addVar("pallet_weight", palletWeight.compareTo(BigDecimal.ZERO)==0?"":palletWeight.setScale(0, RoundingMode.HALF_UP)); + } + String plt = "CTN"; + if (!palletHeaderDataList.isEmpty()) { + plt = "PLT"; + } else { + totalPlt = totalCartons.setScale(0, RoundingMode.HALF_UP).intValue(); + } + template.addVar("total_plt", totalPlt+plt); + // 孟加拉需要的 + if (data.getMaterial()!=null && data.getMaterial()) { + template.addVar("RFIDBase", "RFID Base Material"); + template.addVar("HSCode", "& H.S.Code:"+stringInput(data.getHsCode())); + } + // 欧洲地区需要 + if (data.getPackaging()!=null && data.getPackaging()) { + template.addVar("packaging", "Non-Reusable plastic packaging:"); + // 根据维护参数计算出KGS=总托数*plastic packaging维护参数 + BigDecimal totalKgs = data.getKgs()!=null?data.getKgs():BigDecimal.ZERO; + if (palletHeaderDataList.isEmpty()) { + totalKgs = BigDecimal.valueOf(0.1); + } + template.addVar("KGS_qty", data.getKgs()!=null?(totalKgs.multiply(BigDecimal.valueOf(totalPlt))).setScale(2, RoundingMode.HALF_UP):""); + template.addVar("KGS", "KGS"); + } + // 新加的 货物明细单选框 勾选显示 + if (data.getGoodsLabel()!=null && data.getGoodsLabel()) { + template.addVar("nameLabel", "Name of goods"+(notifyHeader.getBuNo().equals("03-RFID")?" RFID LABEL":" RF LABEL")); + template.addVar("originLabel", "Origin of goods Made in China"); + template.addVar("sellerLabel", "Name of seller Checkpoint Commercial (Shanghai) Co.. Ltd."); + } + template.addVar("Shipping_Mark", stringInput(data.getShippingMark())); + // 体积计算:优先使用栈板体积,栈板不存在时根据装箱明细的物料计算体积 + BigDecimal totalVolume = BigDecimal.ZERO; + if (!palletHeaderDataList.isEmpty()) { + // 计算所有栈板的体积总和 + for (EcssCoDelPalletHeaderData palletHeader : palletHeaderDataList) { + totalVolume = totalVolume.add(palletHeader.getVolume()); + } + totalVolume = totalVolume.setScale(2, RoundingMode.HALF_UP); + } else { + // 栈板不存在时,根据装箱明细的物料计算体积 + totalVolume = calculateVolumeByMaterials(notifyHeader); + } + template.addVar("Measurement", totalVolume); template.addListVarAll(exportList); } diff --git a/src/main/resources/templates/TX/declaration-all-template-pdf.xlsx b/src/main/resources/templates/TX/declaration-all-template-pdf.xlsx index b782cb31..f5b61d6c 100644 Binary files a/src/main/resources/templates/TX/declaration-all-template-pdf.xlsx and b/src/main/resources/templates/TX/declaration-all-template-pdf.xlsx differ diff --git a/src/main/resources/templates/TX/declaration-all-template.xlsx b/src/main/resources/templates/TX/declaration-all-template.xlsx index 36764f1b..ab72c97a 100644 Binary files a/src/main/resources/templates/TX/declaration-all-template.xlsx and b/src/main/resources/templates/TX/declaration-all-template.xlsx differ diff --git a/src/main/resources/templates/TX/declaration-packingList-template.xlsx b/src/main/resources/templates/TX/declaration-packingList-template.xlsx index d934b5c8..57570a91 100644 Binary files a/src/main/resources/templates/TX/declaration-packingList-template.xlsx and b/src/main/resources/templates/TX/declaration-packingList-template.xlsx differ diff --git a/src/main/resources/templates/YB/declaration-all-template-pdf.xlsx b/src/main/resources/templates/YB/declaration-all-template-pdf.xlsx index b782cb31..1a344b6f 100644 Binary files a/src/main/resources/templates/YB/declaration-all-template-pdf.xlsx and b/src/main/resources/templates/YB/declaration-all-template-pdf.xlsx differ diff --git a/src/main/resources/templates/YB/declaration-all-template.xlsx b/src/main/resources/templates/YB/declaration-all-template.xlsx index 36764f1b..f1a4d263 100644 Binary files a/src/main/resources/templates/YB/declaration-all-template.xlsx and b/src/main/resources/templates/YB/declaration-all-template.xlsx differ diff --git a/src/main/resources/templates/YB/declaration-packingList-template.xlsx b/src/main/resources/templates/YB/declaration-packingList-template.xlsx index d934b5c8..23a4ed38 100644 Binary files a/src/main/resources/templates/YB/declaration-packingList-template.xlsx and b/src/main/resources/templates/YB/declaration-packingList-template.xlsx differ