diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index 7bc5e63e..f2e1537f 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -20,6 +20,13 @@ import org.springblade.core.secure.utils.AuthUtil; import org.springblade.core.tool.utils.BeanUtil; import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.bfi.constant.BfiConstant; +import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoicePoolQueryDTO; +import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoicePoolQueryDataDTO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolItemVO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolVO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO; +import org.springblade.thirdparty.bfi.service.IBfiService; import org.springblade.transport.mapper.FormalSettlementDetailMapper; import org.springblade.transport.mapper.FormalSettlementDetailFeeMapper; import org.springblade.transport.mapper.FormalSettlementChangeRecordMapper; @@ -103,6 +110,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -143,6 +151,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl selectPage(IPage page, FormalSettlementVO query) { @@ -1346,12 +1355,15 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl "草稿"; - case TransportBusiConstant.REVIEWING -> "审批中"; - case TransportBusiConstant.APPROVED -> "审批通过"; - case TransportBusiConstant.RETURNED -> "已驳回"; - case TransportBusiConstant.VOIDED -> "已作废"; + if (value == null) { + return ""; + } + return switch (value) { + case "draft" -> "草稿"; + case "reviewing" -> "审批中"; + case "approved" -> "审批通过"; + case "returned" -> "已驳回"; + case "voided" -> "已作废"; default -> value; }; } @@ -1440,44 +1452,106 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl invoicePoolQuery(FormalSettlementInvoicePoolQueryDTO query) { - // 认领查询改为查本地收票管理已登记的发票,仅审批通过的收票可认领,不再远程调用金蝶全票池 long pageNo = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1); - long pageSize = Math.min(Math.max(query.getSize() == null ? 10 : query.getSize(), 1), 500); - IPage receipts = invoiceReceiptMapper.selectPage(new Page<>(pageNo, pageSize), - Wrappers.lambdaQuery() - .eq(InvoiceReceipt::getApprovalStatus, TransportBusiConstant.APPROVED) - .like(StringUtil.isNotBlank(query.getInvoiceNo()), InvoiceReceipt::getInvoiceNo, - query.getInvoiceNo() == null ? null : query.getInvoiceNo().trim()) - .ge(StringUtil.isNotBlank(query.getInvoiceDateStart()), InvoiceReceipt::getInvoiceDate, - parseInvoiceDate(query.getInvoiceDateStart())) - .le(StringUtil.isNotBlank(query.getInvoiceDateEnd()), InvoiceReceipt::getInvoiceDate, - parseInvoiceDate(query.getInvoiceDateEnd())) - .orderByDesc(InvoiceReceipt::getCreateTime)); - return receipts.convert(this::toInvoicePoolVO); - } - - private LocalDate parseInvoiceDate(String value) { - return StringUtil.isBlank(value) ? null : LocalDate.parse(value.trim()); + // 上限 499:预留 1 条给 hasMore 探测,单次远程请求不超过 rim 契约 pageSize 上限 500 + long pageSize = Math.min(Math.max(query.getSize() == null ? 10 : query.getSize(), 1), 499); + String invoiceNo = query.getInvoiceNo(); + String invoiceDateStart = query.getInvoiceDateStart(); + String invoiceDateEnd = query.getInvoiceDateEnd(); + String salerTaxNo = query.getSalerTaxNo(); + String buyerTaxNo = query.getBuyerTaxNo(); + String expenseStatus = query.getExpenseStatus(); + // A801-1 rim 契约不支持全量且不返回总数:透传 pageNo/pageSize 单页查询, + // 多取 1 条探测是否还有下一页,total 合成为当前已知的最小精确值供前端分页器翻页 + BfiInvoicePoolQueryDataDTO data = new BfiInvoicePoolQueryDataDTO(); + data.setPageNo((int) pageNo); + data.setPageSize((int) pageSize + 1); + if (StringUtil.isNotBlank(invoiceNo)) { + data.setInvoiceNo(invoiceNo.trim()); + } + if (StringUtil.isNotBlank(invoiceDateStart)) { + data.setInvoiceDateStart(invoiceDateStart.trim()); + } + if (StringUtil.isNotBlank(invoiceDateEnd)) { + data.setInvoiceDateEnd(invoiceDateEnd.trim()); + } + if (StringUtil.isNotBlank(salerTaxNo)) { + data.setSalerTaxNo(salerTaxNo.trim()); + } + if (StringUtil.isNotBlank(buyerTaxNo)) { + data.setBuyerTaxNo(buyerTaxNo.trim()); + } + if (StringUtil.isNotBlank(expenseStatus)) { + data.setExpenseStatus(expenseStatus.trim()); + } + BfiInvoicePoolQueryDTO param = new BfiInvoicePoolQueryDTO(); + param.setQueryParam(data); + BfiPageVO poolPage = bfiService.queryInvoicePool(param); + List invoices = poolPage.getRows() == null ? List.of() + : poolPage.getRows().stream() + .map(this::toInvoicePoolVO) + .toList(); + // 多取的 1 条仅用于 hasMore 判断,不返回给前端 + boolean hasMore = invoices.size() > pageSize; + List records = hasMore ? invoices.subList(0, (int) pageSize) : invoices; + long total = hasMore ? pageNo * pageSize + 1 : (pageNo - 1) * pageSize + records.size(); + log.info("正式结算单认领查询 全票池第 {} 页 {} 条/页,返回 {} 条,hasMore:{}", pageNo, pageSize, records.size(), hasMore); + Page page = new Page<>(pageNo, pageSize); + page.setRecords(records); + page.setTotal(total); + return page; } /** - * 收票登记发票 -> 正式结算单认领VO 字段映射 + * A801-1 全票池发票 -> 正式结算单认领VO 字段映射 */ - private FormalSettlementInvoicePoolVO toInvoicePoolVO(InvoiceReceipt receipt) { + private FormalSettlementInvoicePoolVO toInvoicePoolVO(BfiInvoicePoolVO poolInvoice) { FormalSettlementInvoicePoolVO vo = new FormalSettlementInvoicePoolVO(); - vo.setInvoiceNo(receipt.getInvoiceNo()); - // 收票登记无票池流水号,用登记主键作为前端行唯一标识 - vo.setSerialNo(String.valueOf(receipt.getId())); - vo.setInvoiceDate(receipt.getInvoiceDate() == null ? null : receipt.getInvoiceDate().toString()); - vo.setInvoiceTypeName(receipt.getInvoiceType()); - vo.setTaxRate(receipt.getTaxRate()); - // 开票金额为价税合计数,可匹配金额、默认认领金额与发票金额同源 - vo.setInvoiceAmount(money(receipt.getInvoiceAmount())); - vo.setAvailableInvoiceAmount(money(receipt.getInvoiceAmount())); - vo.setMatchedAmount(money(receipt.getInvoiceAmount())); - vo.setSalerName(receipt.getIssuerName()); - vo.setBuyerName(receipt.getReceiverName()); - vo.setAttachmentJson(receipt.getAttachmentsJson()); + // 发票号码 + vo.setInvoiceNo(poolInvoice.getInvoiceNo()); + // 发票流水号,票池发票唯一标识 + vo.setSerialNo(poolInvoice.getSerialNo()); + // 开票日期 yyyy-MM-dd + vo.setInvoiceDate(poolInvoice.getInvoiceDate()); + // 发票类型编码转名称,未知编码原样返回 + vo.setInvoiceTypeName(BfiConstant.INVOICE_TYPE_NAMES.getOrDefault(poolInvoice.getInvoiceType(), + poolInvoice.getInvoiceType())); + // 税率:顶层优先、回退首条明细,小数转百分数(0.13 -> 13) + vo.setTaxRate(resolveTaxRate(poolInvoice)); + // 价税合计作为发票金额,可匹配金额、默认认领金额与发票金额同源 + BigDecimal totalAmount = parseAmount(poolInvoice.getTotalAmount()); + vo.setInvoiceAmount(totalAmount); + vo.setAvailableInvoiceAmount(totalAmount); + vo.setMatchedAmount(totalAmount); + // 开票单位(销方名称) + vo.setSalerName(poolInvoice.getSalerName()); + // 受票单位(购方名称) + vo.setBuyerName(poolInvoice.getBuyerName()); + // 附件信息JSON:path 供 A801-2 进项发票下载,文件名以发票号码命名 + Map attachment = new LinkedHashMap<>(); + attachment.put("source", "piaozone"); + attachment.put("path", poolInvoice.getDownloadUrl()); + attachment.put("name", poolInvoice.getInvoiceNo() + ".pdf"); + vo.setAttachmentJson(JsonUtil.toJson(attachment)); return vo; } + + /** + * 税率优先取顶层税率的小数值换算为百分数,如 0.13 -> 13;顶层为空时回退取首条明细税率,均无时返回 null + */ + private BigDecimal resolveTaxRate(BfiInvoicePoolVO poolInvoice) { + if (StringUtil.isNotBlank(poolInvoice.getTaxRate())) { + return new BigDecimal(poolInvoice.getTaxRate()).multiply(BigDecimal.valueOf(100)); + } + return Optional.ofNullable(poolInvoice.getItems()).orElse(List.of()).stream() + .map(BfiInvoicePoolItemVO::getTaxRate) + .filter(StringUtil::isNotBlank) + .findFirst() + .map(taxRate -> new BigDecimal(taxRate).multiply(BigDecimal.valueOf(100))) + .orElse(null); + } + + private BigDecimal parseAmount(String value) { + return StringUtil.isBlank(value) ? BigDecimal.ZERO : new BigDecimal(value); + } }