buhuazhen
4 小时以前 2f3cd0975b753d535054a9ffb19e61c4031032a4
feat(sales): 新增加工承揽合同导出功能

- 在Customer实体中新增法人、传真、开户行和代理字段
- 新增ExportProcessContractVo类及其内部类,封装加工承揽合同导出数据结构
- SalesLedger实体新增签订地点字段placeOfSinging
- ISalesLedgerService接口新增exportProcessContract方法声明
- SalesLedgerController新增导出加工承揽合同接口
- SalesLedgerServiceImpl实现加工承揽合同导出逻辑,包括查询数据、转换VO、计算总金额和税率
- 使用poi-tl模板引擎动态渲染合同Word文档并通过响应流导出
- 删除pom.xml中hutool-all依赖重复声明
- 在StringUtils中新增padRight方法用于字符串右侧补齐空格
- 引入hutool、poi-tl等工具支持合同导出功能实现
已添加2个文件
已修改7个文件
422 ■■■■ 文件已修改
pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/Customer.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/utils/StringUtils.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/vo/ExportProcessContractVo.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/contract_tmp.docx 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -315,13 +315,6 @@
            <version>${getui-sdk.version}</version>
            <scope>compile</scope>
        </dependency>
        <!--hutool工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
src/main/java/com/ruoyi/basic/pojo/Customer.java
@@ -115,4 +115,24 @@
    @ApiModelProperty(value = "开户行号")
    @Excel(name = "开户行号")
    private String bankCode;
    @ApiModelProperty(value = "法人")
    @Excel(name = "法人")
    @TableField(value = "corporation")
    private String corporation;
    @ApiModelProperty(value = "传真")
    @Excel(name = "传真")
    @TableField(value = "fax")
    private String fax;
    @ApiModelProperty(value = "开户行")
    @Excel(name = "开户行")
    @TableField(value = "bank_name")
    private String bankName;
    @ApiModelProperty(value = "代理")
    @Excel(name = "代理")
    @TableField(value = "agent")
    private String agent;
}
src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -719,4 +719,11 @@
        }
        return sb.toString();
    }
    public static String padRight(String str, int length) {
        if (str == null) str = "";
        if (str.length() >= length) return str;
        // ç”¨ç©ºæ ¼è¡¥é½
        return String.format("%-" + length + "s", str);
    }
}
src/main/java/com/ruoyi/sales/controller/SalesLedgerController.java
@@ -179,6 +179,12 @@
        util.exportExcel(response, salesLedgerIPage == null ? new ArrayList<>() : salesLedgerIPage.getRecords(), "导出开票登记列表");
    }
    @Log(title = "导出加工承揽合同", businessType = BusinessType.EXPORT)
    @GetMapping("/exportProcessContract/{id}")
    public void exportProcessContract(@PathVariable Long id){
        salesLedgerService.exportProcessContract(id);
    }
    /**
     * æ–°å¢žä¿®æ”¹é”€å”®å°è´¦
     */
src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -147,5 +147,9 @@
    @TableField(exist = false)
    //是否可编辑
    private Boolean isEdit;
    // ç­¾è®¢åœ°ç‚¹
    @TableField(value = "place_of_singing")
    private String placeOfSinging;
}
src/main/java/com/ruoyi/sales/service/ISalesLedgerService.java
@@ -52,4 +52,10 @@
    List<LossProductModelDto> getSalesLedgerWithProductsLoss(Long salesLedgerId);
    IPage<SalesLedgerDto> listSalesLedger(SalesLedgerDto salesLedgerDto, Page page);
    /**
     * å¯¼å‡ºåŠ å·¥æ‰¿æ½åˆåŒ
     * @param id
     */
    void exportProcessContract(@NotNull Long id);
}
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -1,5 +1,11 @@
package com.ruoyi.sales.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -8,8 +14,10 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.ruoyi.account.service.AccountIncomeService;
import com.ruoyi.aftersalesservice.pojo.AfterSalesService;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
@@ -17,31 +25,26 @@
import com.ruoyi.common.enums.FileNameType;
import com.ruoyi.common.enums.SaleEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.ProductionProductMainService;
import com.ruoyi.production.service.impl.ProductionProductMainServiceImpl;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.sales.dto.*;
import com.ruoyi.sales.mapper.*;
import com.ruoyi.sales.pojo.*;
import com.ruoyi.sales.service.ISalesLedgerProductService;
import com.ruoyi.sales.service.ISalesLedgerService;
import com.ruoyi.sales.vo.ExportProcessContractVo;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
@@ -51,8 +54,12 @@
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
@@ -81,69 +88,41 @@
@RequiredArgsConstructor
@Slf4j
public class SalesLedgerServiceImpl extends ServiceImpl<SalesLedgerMapper, SalesLedger> implements ISalesLedgerService {
    private final AccountIncomeService accountIncomeService;
    private final SalesLedgerMapper salesLedgerMapper;
    private final CustomerMapper customerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl;
    private final CommonFileMapper commonFileMapper;
    private final TempFileMapper tempFileMapper;
    private final ReceiptPaymentMapper receiptPaymentMapper;
    private final ShippingInfoServiceImpl shippingInfoServiceImpl;
    private final CommonFileServiceImpl commonFileService;
    private final ShippingInfoMapper shippingInfoMapper;
    private final InvoiceLedgerMapper invoiceLedgerMapper;
    private final SalesLedgerSchedulingMapper salesLedgerSchedulingMapper;
    private final SalesLedgerWorkMapper salesLedgerWorkMapper;
    private final SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private final InvoiceRegistrationMapper invoiceRegistrationMapper;
    private final ProductOrderMapper productOrderMapper;
    private final ProcessRouteMapper processRouteMapper;
    private final ProductProcessRouteMapper productProcessRouteMapper;
    private final ProcessRouteItemMapper processRouteItemMapper;
    private final ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private final ProductWorkOrderMapper productWorkOrderMapper;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionProductOutputMapper productionProductOutputMapper;
    private final ProductionProductInputMapper productionProductInputMapper;
    private final QualityInspectMapper qualityInspectMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
    private static final String LOCK_PREFIX = "contract_no_lock:";
    private static final long LOCK_WAIT_TIMEOUT = 10; // é”ç­‰å¾…超时时间(秒)
    private static final long LOCK_EXPIRE_TIME = 30;  // é”è‡ªåŠ¨è¿‡æœŸæ—¶é—´ï¼ˆç§’ï¼‰
    private final AccountIncomeService accountIncomeService;
    private final SalesLedgerMapper salesLedgerMapper;
    private final CustomerMapper customerMapper;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl;
    private final CommonFileMapper commonFileMapper;
    private final TempFileMapper tempFileMapper;
    private final ReceiptPaymentMapper receiptPaymentMapper;
    private final ShippingInfoServiceImpl shippingInfoServiceImpl;
    private final CommonFileServiceImpl commonFileService;
    private final ShippingInfoMapper shippingInfoMapper;
    private final InvoiceLedgerMapper invoiceLedgerMapper;
    private final SalesLedgerSchedulingMapper salesLedgerSchedulingMapper;
    private final SalesLedgerWorkMapper salesLedgerWorkMapper;
    private final SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
    private final InvoiceRegistrationMapper invoiceRegistrationMapper;
    private final ProductOrderMapper productOrderMapper;
    private final ProcessRouteMapper processRouteMapper;
    private final ProductProcessRouteMapper productProcessRouteMapper;
    private final ProcessRouteItemMapper processRouteItemMapper;
    private final ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private final ProductWorkOrderMapper productWorkOrderMapper;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionProductOutputMapper productionProductOutputMapper;
    private final ProductionProductInputMapper productionProductInputMapper;
    private final QualityInspectMapper qualityInspectMapper;
    private final RedisTemplate<String, String> redisTemplate;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
    @Autowired
    private ProductModelMapper productModelMapper;
@@ -154,14 +133,15 @@
    @Autowired
    private ProductionProductMainService productionProductMainService;
    ;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    public List<SalesLedger> selectSalesLedgerList(SalesLedgerDto salesLedgerDto) {
        return salesLedgerMapper.selectSalesLedgerList(salesLedgerDto);
    }
    public List<SalesLedgerProduct> getSalesLedgerProductListByRelateId(Long relateId, SaleEnum type){
    public List<SalesLedgerProduct> getSalesLedgerProductListByRelateId(Long relateId, SaleEnum type) {
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, relateId);
        productWrapper.eq(SalesLedgerProduct::getType, type.getCode());
@@ -339,9 +319,6 @@
        return salesLedgerMapper.selectSalesLedgerListPage(page, salesLedgerDto);
    }
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult importData(MultipartFile file) {
@@ -497,35 +474,90 @@
        return salesLedgerDtoIPage;
    }
    @Override
    public void exportProcessContract(Long id) {
        //加工承揽合同
        ExportProcessContractVo exportProcessContract = new ExportProcessContractVo();
        exportProcessContract.setId(id);
        SalesLedger salesLedger = salesLedgerMapper.selectById(id);
        // æŸ¥è¯¢å®¢æˆ·å…¬å¸ä¿¡æ¯
        Customer customer = customerMapper.selectById(salesLedger.getCustomerId());
        exportProcessContract.setCreateTime(LocalDateTimeUtil.format(Optional.ofNullable(salesLedger.getExecutionDate()).orElse(LocalDate.now()), "yyyyå¹´MM月dd日"));
        exportProcessContract.setRemark(Optional.ofNullable(salesLedger.getRemarks()).orElse("无")); // å¤‡æ³¨
        exportProcessContract.setPlaceOfSinging(Optional.ofNullable(salesLedger.getPlaceOfSinging()).orElse(""));
    // å†…部类用于存储聚合结果
    private static class GroupedCustomer {
        private final Long customerId;
        private final String customerName;
        private BigDecimal totalAmount = BigDecimal.ZERO;
        // å¡«å†™ç”²æ–¹ä¿¡æ¯
        ExportProcessContractVo.Customer partyA = ExportProcessContractVo.Customer.getCustomer(customer);
        exportProcessContract.setPartyAClientName(customer.getCustomerName());
        exportProcessContract.setPartyA(partyA);
        public GroupedCustomer(Long customerId, String customerName) {
            this.customerId = customerId;
            this.customerName = customerName;
        // å¡«å†™ä¹™æ–¹ä¿¡æ¯
        ExportProcessContractVo.Customer partyB = new ExportProcessContractVo.Customer();
        exportProcessContract.setPartyBClientName("");//todo@ ä¹™æ–¹å…¬å¸åç§°
        exportProcessContract.setPartyB(partyB);
        // å¡«å†™å•†å“ä¿¡æ¯
        final BigDecimal[] totalAmount = {BigDecimal.ZERO}; // æ€»é‡‘额
        LambdaQueryWrapper<SalesLedgerProduct> productWrapper = new LambdaQueryWrapper<>();
        productWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedger.getId());
        productWrapper.eq(SalesLedgerProduct::getType, 1);
        List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper);
        List<ExportProcessContractVo.SaleProduct> productList = products.stream().map(it -> {
            ExportProcessContractVo.SaleProduct saleProduct = BeanUtil.copyProperties(it, ExportProcessContractVo.SaleProduct.class);
            // è®¡ç®—总价格
            totalAmount[0] = totalAmount[0].add(Optional.ofNullable(saleProduct.getTaxInclusiveTotalPrice()).orElse(BigDecimal.ZERO));
            return saleProduct;
        }).collect(Collectors.toList());
        // ç¬¬ä¸€ä¸ªè®¾ç½® åˆåŒç¼–号
        if (!productList.isEmpty()) {
            productList.get(0).setSalesContractNo(salesLedger.getSalesContractNo());
        }
        // æŸ¥çœ‹ç¨Žçއ ç†è®ºä¸Šç¨ŽçŽ‡å•ä¸€ï¼Œå¦‚æžœå¤šç¨ŽçŽ‡ä¸ºç©º
        Map<BigDecimal, Long> rateMap = productList.stream().map(product -> product.getTaxRate()).filter(Objects::nonNull)
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        String taxRateStr = rateMap.size() == 1 ? rateMap.keySet().iterator().next().toString() + " %" : "";
        exportProcessContract.setTaxRate(taxRateStr);
        exportProcessContract.setSaleProducts(productList);// å•†å“ä¿¡æ¯
        // è®¾ç½®å¤§å†™çš„æ€»ä»·æ ¼
        exportProcessContract.setTotalAmountZh(Convert.digitToChinese(totalAmount[0].doubleValue()));
        exportProcessContractToWord(exportProcessContract);
    }
    @SneakyThrows
    private void exportProcessContractToWord(@NotNull ExportProcessContractVo exportProcessContract){
        // ç¡®ä¿ saleProducts ä¸ä¸º null
        if (exportProcessContract.getSaleProducts() == null) {
            exportProcessContract.setSaleProducts(new ArrayList<>());
        }
        public void addAmount(BigDecimal amount) {
            if (amount != null) {
                this.totalAmount = this.totalAmount.add(amount);
            }
        }
        // æ¨¡æ¿è¾“入流
        InputStream inputStream = this.getClass().getResourceAsStream("/static/contract_tmp.docx");
        Assert.isTrue(inputStream != null, "模板不存在");
        public Long getCustomerId() {
            return customerId;
        }
        // è½¬ Map
        Map<String, Object> dataMap = BeanUtil.beanToMap(exportProcessContract);
        public String getCustomerName() {
            return customerName;
        }
        // ç»‘定循环策略
        Configure configure = Configure.builder()
                .bind("saleProducts", new LoopRowTableRenderPolicy())
                .build();
        public BigDecimal getTotalAmount() {
            return totalAmount;
        }
        // æ¸²æŸ“模板
        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure)
                .render(dataMap);
//        template.write(FileUtil.getOutputStream("/Users/ONEX/Downloads/a.docx"));
        // è¾“出到浏览器
        HttpServletResponse response =
                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                        .getResponse();
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setHeader("Content-Disposition", "attachment;filename="+ StrUtil.format("{}-{}",exportProcessContract.getPartyAClientName(),exportProcessContract.getCreateTime()) +"合同.docx");
        template.write(response.getOutputStream());
        template.close();
        response.flushBuffer();
    }
    /**
@@ -646,7 +678,7 @@
            // 4. å¤„理子表数据
            List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
            if (productList != null && !productList.isEmpty()) {
                handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class,salesLedgerDto.getType()));
                handleSalesLedgerProducts(salesLedger.getId(), productList, EnumUtil.fromCode(SaleEnum.class, salesLedgerDto.getType()));
                updateMainContractAmount(
                        salesLedger.getId(),
                        productList,
@@ -665,8 +697,6 @@
            throw new BaseException("文件迁移失败: " + e.getMessage());
        }
    }
    // æ–‡ä»¶è¿ç§»æ–¹æ³•
    /**
     * å°†ä¸´æ—¶æ–‡ä»¶è¿ç§»åˆ°æ­£å¼ç›®å½•
@@ -743,6 +773,7 @@
        }
    }
    // æ–‡ä»¶è¿ç§»æ–¹æ³•
    @Override
    public void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type) {
@@ -884,4 +915,34 @@
            throw new RuntimeException("动态更新主表金额失败", e);
        }
    }
    // å†…部类用于存储聚合结果
    private static class GroupedCustomer {
        private final Long customerId;
        private final String customerName;
        private BigDecimal totalAmount = BigDecimal.ZERO;
        public GroupedCustomer(Long customerId, String customerName) {
            this.customerId = customerId;
            this.customerName = customerName;
        }
        public void addAmount(BigDecimal amount) {
            if (amount != null) {
                this.totalAmount = this.totalAmount.add(amount);
            }
        }
        public Long getCustomerId() {
            return customerId;
        }
        public String getCustomerName() {
            return customerName;
        }
        public BigDecimal getTotalAmount() {
            return totalAmount;
        }
    }
}
src/main/java/com/ruoyi/sales/vo/ExportProcessContractVo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package com.ruoyi.sales.vo;
import com.ruoyi.basic.pojo.Customer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import static com.ruoyi.common.utils.StringUtils.padRight;
/**
 * @author buhuazhen
 * @date 2026/3/18
 * @email 3038525872@qq.com
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExportProcessContractVo implements Serializable {
    private Long id;
    // yyyyå¹´MM月dd日
    private String createTime;
    // æ€»é‡‘额(大写汉字)
    private String totalAmountZh;
    // ç”²æ–¹
    private Customer partyA;
    // ä¹™æ–¹
    private Customer partyB;
    // ç”²æ–¹å…¬å¸åç§°
    private String partyAClientName;
    // ä¹™æ–¹å…¬å¸åç§°
    private String partyBClientName;
    // å¤‡æ³¨
    private String remark;
    private List<SaleProduct> saleProducts;
    // ç­¾è®¢åœ°ç‚¹
    private String placeOfSinging;
    private String taxRate;
    // å…¬å¼ä¿¡æ¯
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Customer implements Serializable {
        // æ³•人
        private String corporation = "";
        // å¼€æˆ·è¡Œ
        private String bankName = "";
        //代理人
        private String agent = "";
        // åœ°å€
        private String address = "";
        //传真
        private String fax = "";
        private String taxpayerIdentificationNumber = "";
        // å¼€æˆ·è¡Œå·
        private String bankCode = "";
        // é‚®ç¼–
        private String postCode = "";
        public static ExportProcessContractVo.Customer getCustomer(com.ruoyi.basic.pojo.Customer customer) {
            ExportProcessContractVo.Customer partyA = new ExportProcessContractVo.Customer();
            partyA.setFax(Optional.ofNullable(customer.getFax()).orElse(""));
            partyA.setAddress(padRight(customer.getCompanyAddress(), 15));
            partyA.setCorporation(Optional.ofNullable(customer.getCorporation()).orElse(""));
            partyA.setBankName(padRight(customer.getBankName(), 13));
            partyA.setAgent(Optional.ofNullable(customer.getAgent()).orElse(""));
            partyA.setTaxpayerIdentificationNumber(padRight(customer.getTaxpayerIdentificationNumber(), 15));
            partyA.setBankCode(padRight(customer.getBankCode(), 12));
            return partyA;
        }
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class SaleProduct implements Serializable {
        // åˆåŒç¼–号
        private String salesContractNo = "";
        private String productCategory = "";
        private String specificationModel = "";
        private BigDecimal quantity;
        // ç¨Žçއ
        private BigDecimal taxRate;
        // å«ç¨Žå•ä»·
        private BigDecimal taxInclusiveUnitPrice;
        // å«ç¨Žæ€»ä»·
        private BigDecimal taxInclusiveTotalPrice;
    }
}
src/main/resources/static/contract_tmp.docx
Binary files differ