Przeglądaj źródła

fix: 修复发票批量导出因航信重构后items/blueFapiao为null导致NPE的问题

- 对items为null(航信发票无行明细)时改为单行汇总导出
- 对blueFapiao为null(手动开票)时安全降级为空串
- 对buyerInformation增加null防御
- 是否作废列改为根据实际status判断,不再写死为否
- 航信发票新增价税合计列,方便财务对账
- 移除无用列(规格型号、单位)及无用import

Co-Authored-By: Claude <noreply@anthropic.com>
skyline 1 miesiąc temu
rodzic
commit
1e199eea6a

+ 40 - 29
service/src/main/java/com/kym/service/admin/impl/InvoiceDetailServiceImpl.java

@@ -1,10 +1,8 @@
 package com.kym.service.admin.impl;
 
-import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.poi.excel.ExcelUtil;
 import cn.hutool.poi.excel.ExcelWriter;
-import com.alibaba.fastjson2.JSON;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.github.pagehelper.PageHelper;
 import com.github.yulichang.base.MPJBaseServiceImpl;
@@ -13,7 +11,6 @@ import com.kym.entity.admin.InvoiceDetail;
 import com.kym.entity.admin.queryParams.InvoiceDetailQueryParam;
 import com.kym.entity.common.PageBean;
 import com.kym.entity.miniapp.Invoice;
-import com.kym.entity.wechat.FapiaoApplications;
 import com.kym.mapper.admin.InvoiceDetailMapper;
 import com.kym.service.admin.InvoiceDetailService;
 import com.kym.service.miniapp.DataDictService;
@@ -60,44 +57,58 @@ public class InvoiceDetailServiceImpl extends MPJBaseServiceImpl<InvoiceDetailMa
         // excel 行信息
         List<Map<String, Object>> rows = new ArrayList<>();
 
-        // 单张发票若发票行有多行,则每项单独一行
         invoiceDetailList.forEach(invoiceDetail -> {
-            var items = JSON.parseArray(JSON.toJSONString(invoiceDetail.getItems()), FapiaoApplications.FapiaoItem.class);
-            items.forEach(item -> {
-                var row = new HashMap<String, Object>();
-                // 发票基本信息
-                row.put("blue_fapiao.fapiao_time", invoiceDetail.getFapiaoTime());
-                row.put("blue_fapiao.fapiao_code", invoiceDetail.getBlueFapiao().getFapiao_code());
-                row.put("blue_fapiao.fapiao_number", invoiceDetail.getBlueFapiao().getFapiao_number());
-                row.put("buyer_information.type", Invoice.TYPE_企业.equals(invoiceDetail.getBuyerInformation().getType()) ? "企业" : "个人");
-                row.put("buyer_information.name", invoiceDetail.getBuyerInformation().getName());
-                row.put("buyer_information.taxpayer_id", invoiceDetail.getBuyerInformation().getTaxpayer_id());
-                row.put("fapiao_information.status", "否"); // invoiceDetail.getStatus()
-
-                row.putAll(Map.of(
-                        "goods_name", item.getGoods_name(),
-//             "specification",item.getSpecification(),
-//             "unit",item.getUnit(),
-                        "quantity", item.getQuantity() / 100000000,
-                        "amount", NumberUtil.decimalFormat("#.##", item.getAmount() / 100f),
-                        "tax_amount", NumberUtil.decimalFormat("#.##", item.getTax_amount() / 100f),
-                        "tax_rate", NumberUtil.decimalFormat("#.##", item.getTax_rate() / 10000f)
-                ));
-
+            // 发票公共信息
+            var blueFapiao = invoiceDetail.getBlueFapiao();
+            var buyerInfo = invoiceDetail.getBuyerInformation();
+
+            Map<String, Object> baseInfo = new HashMap<>();
+            baseInfo.put("blue_fapiao.fapiao_time", invoiceDetail.getFapiaoTime());
+            baseInfo.put("blue_fapiao.fapiao_code", blueFapiao != null ? blueFapiao.getFapiao_code() : "");
+            baseInfo.put("blue_fapiao.fapiao_number", blueFapiao != null ? blueFapiao.getFapiao_number() : "");
+            baseInfo.put("buyer_information.type", buyerInfo != null && Invoice.TYPE_企业.equals(buyerInfo.getType()) ? "企业" : "个人");
+            baseInfo.put("buyer_information.name", buyerInfo != null ? buyerInfo.getName() : "");
+            baseInfo.put("buyer_information.taxpayer_id", buyerInfo != null ? buyerInfo.getTaxpayer_id() : "");
+            baseInfo.put("fapiao_information.status", "REVERSED".equals(invoiceDetail.getStatus()) ? "是" : "否");
+
+            var items = invoiceDetail.getItems();
+            if (items != null && !items.isEmpty()) {
+                // 旧版微信发票:含发票行明细,每行展开一条记录
+                items.forEach(item -> {
+                    var row = new HashMap<>(baseInfo);
+                    row.put("goods_name", item.getGoods_name());
+                    row.put("quantity", item.getQuantity() / 100000000);
+                    row.put("amount", NumberUtil.decimalFormat("#.##", item.getAmount() / 100f));
+                    row.put("tax_amount", NumberUtil.decimalFormat("#.##", item.getTax_amount() / 100f));
+                    row.put("tax_rate", NumberUtil.decimalFormat("#.##", item.getTax_rate() / 10000f));
+                    rows.add(row);
+                });
+            } else {
+                // 新版航信发票:无发票行明细,一张发票汇为一行,金额统一为元
+                var row = new HashMap<>(baseInfo);
+                row.put("goods_name", "");
+                row.put("quantity", "");
+                row.put("amount", invoiceDetail.getAmount() != null
+                        ? NumberUtil.decimalFormat("#.##", invoiceDetail.getAmount() / 100.0) : "");
+                row.put("tax_amount", invoiceDetail.getTaxAmount() != null
+                        ? NumberUtil.decimalFormat("#.##", invoiceDetail.getTaxAmount() / 100.0) : "");
+                row.put("tax_rate", "");
+                // 航信发票额外导出含税总金额,方便财务对账
+                row.put("total_amount_has_tax", invoiceDetail.getTotalAmount() != null
+                        ? NumberUtil.decimalFormat("#.##", invoiceDetail.getTotalAmount() / 100.0) : "");
                 rows.add(row);
-            });
+            }
         });
 
         writer.addHeaderAlias("blue_fapiao.fapiao_code", "发票代码");
         writer.addHeaderAlias("blue_fapiao.fapiao_number", "发票号码");
         writer.addHeaderAlias("blue_fapiao.fapiao_time", "开票日期");
         writer.addHeaderAlias("goods_name", "货物、应税劳务及服务");
-        writer.addHeaderAlias("specification", "规格型号");
         writer.addHeaderAlias("quantity", "数量");
-        writer.addHeaderAlias("unit", "单位");
         writer.addHeaderAlias("amount", "金额");
         writer.addHeaderAlias("tax_rate", "税率");
         writer.addHeaderAlias("tax_amount", "税额");
+        writer.addHeaderAlias("total_amount_has_tax", "价税合计");
         writer.addHeaderAlias("fapiao_information.status", "是否作废");
         writer.addHeaderAlias("buyer_information.type", "购买方类型");
         writer.addHeaderAlias("buyer_information.name", "购方单位名称");