diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementInvoicePoolQueryDTO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementInvoicePoolQueryDTO.java deleted file mode 100644 index 2fd0a090..00000000 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/FormalSettlementInvoicePoolQueryDTO.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * BladeX Commercial License Agreement - * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. - *

- * Use of this software is governed by the Commercial License Agreement - * obtained after purchasing a license from BladeX. - *

- * 1. This software is for development use only under a valid license - * from BladeX. - *

- * 2. Redistribution of this software's source code to any third party - * without a commercial license is strictly prohibited. - *

- * 3. Licensees may copyright their own code but cannot use segments - * from this software for such purposes. Copyright of this software - * remains with BladeX. - *

- * Using this software signifies agreement to this License, and the software - * must not be used for illegal purposes. - *

- * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is - * not liable for any claims arising from secondary or illegal development. - *

- * Author: Chill Zhuang (bladejava@qq.com) - */ -package org.springblade.transport.pojo.dto; - -import lombok.Data; - -import java.io.Serial; -import java.io.Serializable; - -/** - * 正式结算单认领查询 全票池发票查询条件 - * - * @author Chill - */ -@Data -public class FormalSettlementInvoicePoolQueryDTO implements Serializable { - @Serial - private static final long serialVersionUID = 1L; - /** - * 页码,默认 1 - */ - private Long current = 1L; - /** - * 每页条数,默认 10,最大 500 - */ - private Long size = 10L; - /** - * 发票号码 - */ - private String invoiceNo; - /** - * 开票日期起 yyyy-MM-dd - */ - private String invoiceDateStart; - /** - * 开票日期止 yyyy-MM-dd - */ - private String invoiceDateEnd; - /** - * 销方税号 - */ - private String salerTaxNo; - /** - * 购方税号 - */ - private String buyerTaxNo; - /** - * 单据使用状态,多个用英文逗号分隔[1未用,30在用,60已用,65已入账] - */ - private String expenseStatus; -} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/KingdeeInvoicePoolQueryDTO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/KingdeeInvoicePoolQueryDTO.java new file mode 100644 index 00000000..c0a01139 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/KingdeeInvoicePoolQueryDTO.java @@ -0,0 +1,117 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.pojo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 金蝶票据池镜像统一查询条件(收票登记选票与正式结算单认领共用) + * + * @author Chill + */ +@Data +@Schema(description = "金蝶票据池镜像统一查询条件") +public class KingdeeInvoicePoolQueryDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 当前页 + */ + @Schema(description = "当前页") + private Long current = 1L; + + /** + * 每页条数 + */ + @Schema(description = "每页条数") + private Long size = 10L; + + /** + * 发票号码,模糊 + */ + @Schema(description = "发票号码(模糊)") + private String invoiceNo; + + /** + * 开票日期起 yyyy-MM-dd + */ + @Schema(description = "开票日期起 yyyy-MM-dd") + private String invoiceDateStart; + + /** + * 开票日期止 yyyy-MM-dd + */ + @Schema(description = "开票日期止 yyyy-MM-dd") + private String invoiceDateEnd; + + /** + * 开票单位(销方名称),模糊 + */ + @Schema(description = "开票单位(销方名称,模糊)") + private String salerName; + + /** + * 受票单位(购方名称),模糊 + */ + @Schema(description = "受票单位(购方名称,模糊)") + private String buyerName; + + /** + * 发票类型名称,模糊(镜像存中文名) + */ + @Schema(description = "发票类型名称(模糊)") + private String invoiceType; + + /** + * 销方税号,精确 + */ + @Schema(description = "销方税号") + private String salerTaxNo; + + /** + * 购方税号,精确 + */ + @Schema(description = "购方税号") + private String buyerTaxNo; + + /** + * 认领过滤:true 仅已认领,false 仅未认领,null 不过滤(已认领=存在正式结算单发票行) + */ + @Schema(description = "认领过滤:true已认领/false未认领/null不限") + private Boolean claimed; + + /** + * 登记过滤:true 仅已登记,false 仅未登记,null 不过滤(已登记=存在非作废收票登记) + */ + @Schema(description = "登记过滤:true已登记/false未登记/null不限") + private Boolean registered; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementInvoicePoolVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementInvoicePoolVO.java deleted file mode 100644 index 2b1d3566..00000000 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementInvoicePoolVO.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * BladeX Commercial License Agreement - * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. - *

- * Use of this software is governed by the Commercial License Agreement - * obtained after purchasing a license from BladeX. - *

- * 1. This software is for development use only under a valid license - * from BladeX. - *

- * 2. Redistribution of this software's source code to any third party - * without a commercial license is strictly prohibited. - *

- * 3. Licensees may copyright their own code but cannot use segments - * from this software for such purposes. Copyright of this software - * remains with BladeX. - *

- * Using this software signifies agreement to this License, and the software - * must not be used for illegal purposes. - *

- * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is - * not liable for any claims arising from secondary or illegal development. - *

- * Author: Chill Zhuang (bladejava@qq.com) - */ -package org.springblade.transport.pojo.vo; - -import lombok.Data; - -import java.io.Serial; -import java.io.Serializable; -import java.math.BigDecimal; - -/** - * 正式结算单认领查询 全票池发票VO - * - * @author Chill - */ -@Data -public class FormalSettlementInvoicePoolVO implements Serializable { - @Serial - private static final long serialVersionUID = 1L; - /** - * 发票号码 - */ - private String invoiceNo; - /** - * 发票流水号 - */ - private String serialNo; - /** - * 开票日期 - */ - private String invoiceDate; - /** - * 发票类型名称 - */ - private String invoiceTypeName; - /** - * 税率(百分数,如 13);票池明细缺失时为 null - */ - private BigDecimal taxRate; - /** - * 发票金额(含税,取票池价税合计) - */ - private BigDecimal invoiceAmount; - /** - * 可匹配发票金额(含税,本次口径等于价税合计) - */ - private BigDecimal availableInvoiceAmount; - /** - * 认领金额(默认等于可匹配金额,由前端按结算余额分配) - */ - private BigDecimal matchedAmount; - /** - * 开票单位(销方名称) - */ - private String salerName; - /** - * 受票单位(购方名称) - */ - private String buyerName; - /** - * 附件信息JSON,结构 {"source":"piaozone","path":"A801-2下载path","name":"文件名"} - */ - private String attachmentJson; -} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/KingdeeInvoicePoolVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/KingdeeInvoicePoolVO.java new file mode 100644 index 00000000..1da543d6 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/KingdeeInvoicePoolVO.java @@ -0,0 +1,153 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.pojo.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 金蝶票据池镜像统一视图(含本地认领/登记标记;认领口径:可匹配与默认认领金额=价税合计) + * + * @author Chill + */ +@Data +@Schema(description = "金蝶票据池镜像统一视图") +public class KingdeeInvoicePoolVO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 镜像主键,收票登记保存绑定用(kingdeeInvoicePoolId) */ + @Schema(description = "镜像主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** 发票号码 */ + @Schema(description = "发票号码") + private String invoiceNo; + + /** 金蝶发票流水号(票池唯一标识) */ + @Schema(description = "金蝶发票流水号") + private String serialNo; + + /** 开票日期 yyyy-MM-dd */ + @Schema(description = "开票日期") + private String invoiceDate; + + /** 发票类型名称(镜像落库时已由编码转名称) */ + @Schema(description = "发票类型名称") + private String invoiceType; + + /** 税率百分数,如 13 */ + @Schema(description = "税率百分数") + private BigDecimal taxRate; + + /** 价税合计 */ + @Schema(description = "发票金额(价税合计)") + private BigDecimal invoiceAmount; + + /** 税额 */ + @Schema(description = "税额") + private BigDecimal taxAmount; + + /** 受票单位(购方名称) */ + @Schema(description = "受票单位") + private String receiverName; + + /** 购方税号 */ + @Schema(description = "购方税号") + private String buyerTaxNo; + + /** 开票单位(销方名称) */ + @Schema(description = "开票单位") + private String issuerName; + + /** 销方税号 */ + @Schema(description = "销方税号") + private String salerTaxNo; + + /** 销方开户行名称 */ + @Schema(description = "销方开户行名称") + private String bankName; + + /** 销方银行账号 */ + @Schema(description = "销方银行账号") + private String bankAccount; + + /** 销方开户行及账号 */ + @Schema(description = "开户行及账号") + private String issuingBank; + + /** 销方电话 */ + @Schema(description = "电话") + private String phone; + + /** 客户邮箱 JSON 串 */ + @Schema(description = "客户邮箱JSON") + private String customerEmails; + + /** 部门邮箱 JSON 串 */ + @Schema(description = "部门邮箱JSON") + private String departmentEmails; + + /** 金蝶流水号(登记侧展示为金蝶单据号) */ + @Schema(description = "金蝶单据号") + private String kingdeeBillNo; + + /** 同步状态 unsynced/synced/failed */ + @Schema(description = "金蝶同步状态") + private String kingdeeStatus; + + /** 查验状态 */ + @Schema(description = "查验状态") + private String checkStatus; + + /** 附件 JSON(票易通下载 path) */ + @Schema(description = "附件信息JSON") + private String attachmentsJson; + + /** 可认领金额,口径同发票金额 */ + @Schema(description = "可认领金额") + private BigDecimal availableInvoiceAmount; + + /** 默认认领金额,口径同发票金额 */ + @Schema(description = "默认认领金额") + private BigDecimal matchedAmount; + + /** 是否已被正式结算单认领 */ + @Schema(description = "是否已认领") + private Boolean claimed; + + /** 是否已存在非作废收票登记 */ + @Schema(description = "是否已登记") + private Boolean registered; +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java index 856756f6..fffcffff 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/FormalSettlementController.java @@ -27,14 +27,15 @@ import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest; import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest; -import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO; +import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO; import org.springblade.transport.pojo.dto.PreSettlementDetailAdjustRequest; import org.springblade.transport.pojo.entity.FormalSettlementDetailFee; -import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO; +import org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO; import org.springblade.transport.pojo.vo.FormalSettlementVO; import org.springblade.transport.excel.FormalSettlementExcel; import org.springblade.transport.pojo.vo.PreSettlementVO; import org.springblade.transport.service.IFormalSettlementService; +import org.springblade.transport.service.IInvoiceReceiptService; import org.springblade.transport.service.IPreSettlementService; import org.springblade.transport.service.IReceiptFlowService; import org.springframework.web.bind.annotation.GetMapping; @@ -63,6 +64,7 @@ public class FormalSettlementController extends BladeController { private final IFormalSettlementService formalSettlementService; private final IPreSettlementService preSettlementService; private final IReceiptFlowService receiptFlowService; + private final IInvoiceReceiptService invoiceReceiptService; @GetMapping("/list") @ApiOperationSupport(order = 1) @@ -260,8 +262,15 @@ public class FormalSettlementController extends BladeController { @GetMapping("/invoice-pool") @ApiOperationSupport(order = 21) - @Operation(summary = "查询发票云全票池发票(认领查询)") - public R> invoicePool(FormalSettlementInvoicePoolQueryDTO query) { - return R.data(formalSettlementService.invoicePoolQuery(query)); + @Operation(summary = "金蝶票据池镜像分页查询(认领查询)") + public R> invoicePool(KingdeeInvoicePoolQueryDTO query) { + return R.data(invoiceReceiptService.invoicePoolPage(query)); + } + + @PostMapping("/invoice-pool/fetch") + @ApiOperationSupport(order = 22) + @Operation(summary = "按发票号码从金蝶票池补查单张发票") + public R fetchInvoicePool(@RequestParam String invoiceNo) { + return R.data(invoiceReceiptService.fetchKingdeeInvoiceByNo(invoiceNo)); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceReceiptController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceReceiptController.java index 4819ecfc..413825df 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceReceiptController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceReceiptController.java @@ -37,8 +37,9 @@ import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; import org.springblade.transport.pojo.dto.InvoiceReceiptSaveRequest; import org.springblade.transport.pojo.dto.InvoiceReceiptStatusRequest; -import org.springblade.transport.pojo.entity.KingdeeInvoicePool; +import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO; import org.springblade.transport.pojo.vo.InvoiceReceiptVO; +import org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO; import org.springblade.transport.service.IInvoiceReceiptService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -80,9 +81,16 @@ public class InvoiceReceiptController extends BladeController { @GetMapping("/invoice-pool") @ApiOperationSupport(order = 3) - @Operation(summary = "查询金蝶进项发票票据池") - public R> invoicePool(@RequestParam(required = false) String keyword) { - return R.data(invoiceReceiptService.invoicePool(keyword)); + @Operation(summary = "金蝶票据池镜像分页查询(含认领/登记标记)") + public R> invoicePool(KingdeeInvoicePoolQueryDTO query) { + return R.data(invoiceReceiptService.invoicePoolPage(query)); + } + + @PostMapping("/invoice-pool/fetch") + @ApiOperationSupport(order = 14) + @Operation(summary = "按发票号码从金蝶票池补查单张发票") + public R fetchInvoicePool(@RequestParam String invoiceNo) { + return R.data(invoiceReceiptService.fetchKingdeeInvoiceByNo(invoiceNo)); } @GetMapping("/settlement-candidates") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.java index d299c635..241517d9 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.java @@ -26,8 +26,12 @@ package org.springblade.transport.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO; import org.springblade.transport.pojo.entity.KingdeeInvoicePool; +import org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO; /** * 金蝶进项发票票据池 Mapper 接口 @@ -36,4 +40,25 @@ import org.springblade.transport.pojo.entity.KingdeeInvoicePool; */ @Mapper public interface KingdeeInvoicePoolMapper extends BaseMapper { + + /** + * 金蝶票据池镜像统一分页查询:认领/登记标记由 EXISTS 子查询直出 + * + * @param page 分页参数 + * @param query 统一查询条件(已在上层完成 trim 与日期格式校验) + * @param tenantId 当前租户 + * @return 统一视图分页 + */ + IPage selectInvoicePoolPage(IPage page, + @Param("query") KingdeeInvoicePoolQueryDTO query, @Param("tenantId") String tenantId); + + /** + * 按发票号码查询单张镜像统一视图(补查落库后回读) + * + * @param invoiceNo 发票号码 + * @param tenantId 当前租户 + * @return 统一视图,无镜像时 null + */ + KingdeeInvoicePoolVO selectInvoicePoolVO(@Param("invoiceNo") String invoiceNo, + @Param("tenantId") String tenantId); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.xml new file mode 100644 index 00000000..72314c70 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/KingdeeInvoicePoolMapper.xml @@ -0,0 +1,87 @@ + + + + + + + EXISTS (SELECT 1 FROM blade_formal_settlement_invoice c + WHERE c.is_deleted = 0 AND c.tenant_id = p.tenant_id AND c.invoice_no = p.invoice_no + AND EXISTS (SELECT 1 FROM blade_formal_settlement s + WHERE s.id = c.formal_settlement_id AND s.is_deleted = 0 + AND s.approval_status != 'voided')) + + + + + EXISTS (SELECT 1 FROM blade_invoice_receipt r + WHERE r.is_deleted = 0 AND r.tenant_id = p.tenant_id + AND r.approval_status != 'voided' AND r.invoice_no = p.invoice_no) + + + + + p.id, p.invoice_no, p.serial_no, + DATE_FORMAT(p.invoice_date, '%Y-%m-%d') AS invoice_date, + p.invoice_type, p.tax_rate, p.invoice_amount, p.tax_amount, + p.receiver_name, p.buyer_tax_no, p.issuer_name, p.saler_tax_no, + p.bank_name, p.bank_account, p.issuing_bank, p.phone, + p.customer_emails, p.department_emails, + p.kingdee_bill_no, p.kingdee_status, p.check_status, p.attachments_json, + p.invoice_amount AS available_invoice_amount, + p.invoice_amount AS matched_amount, + AS claimed, + AS registered + + + + + + diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java index 4f956c6a..f49babc3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IFormalSettlementService.java @@ -13,7 +13,6 @@ import org.springblade.core.mp.base.BaseService; import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest; import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest; import org.springblade.transport.pojo.entity.FormalSettlement; -import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO; import org.springblade.transport.pojo.vo.FormalSettlementVO; import org.springblade.transport.pojo.vo.PreSettlementVO; import org.springblade.transport.pojo.entity.PreSettlement; @@ -23,7 +22,6 @@ import java.util.List; import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest; -import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO; /** * 正式结算单服务 @@ -50,5 +48,4 @@ public interface IFormalSettlementService extends BaseService String applyPayment(FormalSettlementPaymentRequest request); List applyPayments(FormalSettlementBatchPaymentRequest request); void claimInvoices(FormalSettlementInvoiceClaimRequest request); - IPage invoicePoolQuery(FormalSettlementInvoicePoolQueryDTO query); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceReceiptService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceReceiptService.java index cf5a877d..bd959bd8 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceReceiptService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IInvoiceReceiptService.java @@ -29,10 +29,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import org.springblade.core.mp.base.BaseService; import org.springblade.transport.pojo.dto.InvoiceReceiptSaveRequest; import org.springblade.transport.pojo.dto.InvoiceReceiptStatusRequest; +import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO; import org.springblade.transport.pojo.entity.InvoiceReceipt; import org.springblade.transport.pojo.entity.InvoiceReceiptRecord; import org.springblade.transport.pojo.entity.KingdeeInvoicePool; import org.springblade.transport.pojo.vo.InvoiceReceiptVO; +import org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO; import java.util.List; import java.util.Map; @@ -48,7 +50,22 @@ public interface IInvoiceReceiptService extends BaseService { InvoiceReceiptVO detail(Long id); - List invoicePool(String keyword); + /** + * 金蝶票据池镜像分页查询(含本地认领/登记标记),收票登记选票与正式结算单认领共用 + * + * @param query 统一查询条件 + * @return 镜像分页 + */ + IPage invoicePoolPage(KingdeeInvoicePoolQueryDTO query); + + /** + * 按发票号码从金蝶票池精确补查单张发票:与定时同步同一套白名单校验, + * 通过后 upsert 入镜像并返回;不受增量时间窗口约束 + * + * @param invoiceNo 发票号码 + * @return 镜像统一视图 + */ + KingdeeInvoicePoolVO fetchKingdeeInvoiceByNo(String invoiceNo); List> settlementCandidates(String keyword, Long receiptId); 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 f2e1537f..38769d54 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 @@ -11,7 +11,6 @@ package org.springblade.transport.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springblade.core.log.exception.ServiceException; @@ -19,14 +18,6 @@ import org.springblade.core.mp.base.BaseServiceImpl; 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; @@ -54,7 +45,6 @@ import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest; import org.springblade.transport.pojo.dto.FormalSettlementPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementBatchPaymentRequest; import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest; -import org.springblade.transport.pojo.dto.FormalSettlementInvoicePoolQueryDTO; import org.springblade.transport.pojo.dto.FormalSettlementStatusRequest; import org.springblade.transport.pojo.dto.PreSettlementDetailAdjustRequest; import org.springblade.transport.pojo.entity.FormalSettlement; @@ -80,7 +70,6 @@ import org.springblade.transport.pojo.entity.ReceivablePayableDetail; import org.springblade.transport.pojo.entity.ReceivablePayableCargoFee; import org.springblade.transport.pojo.entity.SettlementAdjustment; import org.springblade.transport.pojo.entity.Waybill; -import org.springblade.transport.pojo.vo.FormalSettlementInvoicePoolVO; import org.springblade.transport.pojo.vo.FormalSettlementVO; import org.springblade.transport.pojo.vo.PreSettlementVO; import org.springblade.transport.pojo.vo.SettlementAdjustmentVO; @@ -110,7 +99,6 @@ 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; @@ -151,7 +139,6 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl selectPage(IPage page, FormalSettlementVO query) { @@ -1449,109 +1436,4 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl max) throw new ServiceException("内容不能超过" + max + "个字"); return value; } - - @Override - public IPage invoicePoolQuery(FormalSettlementInvoicePoolQueryDTO query) { - long pageNo = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1); - // 上限 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; - } - - /** - * A801-1 全票池发票 -> 正式结算单认领VO 字段映射 - */ - private FormalSettlementInvoicePoolVO toInvoicePoolVO(BfiInvoicePoolVO poolInvoice) { - FormalSettlementInvoicePoolVO vo = new FormalSettlementInvoicePoolVO(); - // 发票号码 - 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); - } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java index 1d3838bd..77267f19 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceReceiptServiceImpl.java @@ -28,6 +28,7 @@ package org.springblade.transport.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.toolkit.Db; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -58,6 +59,7 @@ import org.springblade.transport.mapper.InvoiceReceiptSettlementMapper; import org.springblade.transport.mapper.KingdeeInvoicePoolMapper; import org.springblade.transport.pojo.dto.InvoiceReceiptSaveRequest; import org.springblade.transport.pojo.dto.InvoiceReceiptStatusRequest; +import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO; import org.springblade.transport.pojo.entity.CustomerArchive; import org.springblade.transport.pojo.entity.CustomerContact; import org.springblade.transport.pojo.entity.FormalSettlement; @@ -66,6 +68,7 @@ import org.springblade.transport.pojo.entity.InvoiceReceiptRecord; import org.springblade.transport.pojo.entity.InvoiceReceiptSettlement; import org.springblade.transport.pojo.entity.KingdeeInvoicePool; import org.springblade.transport.pojo.vo.InvoiceReceiptVO; +import org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO; import org.springblade.transport.service.IInvoiceReceiptService; import org.springblade.transport.wrapper.InvoiceReceiptWrapper; import org.springframework.beans.factory.ObjectProvider; @@ -78,6 +81,7 @@ import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; @@ -135,6 +139,9 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl invoicePool(String keyword) { - return invoicePoolMapper.selectList(Wrappers.lambdaQuery() - .eq(KingdeeInvoicePool::getStatus, 1) - .and(Func.isNotEmpty(keyword), wrapper -> wrapper.like(KingdeeInvoicePool::getInvoiceNo, keyword) - .or().like(KingdeeInvoicePool::getIssuerName, keyword) - .or().like(KingdeeInvoicePool::getReceiverName, keyword)) - .orderByDesc(KingdeeInvoicePool::getSourceUpdatedTime) - .orderByDesc(KingdeeInvoicePool::getCreateTime) - .last("limit 200")); + public IPage invoicePoolPage(KingdeeInvoicePoolQueryDTO query) { + long current = query.getCurrent() == null ? 1L : Math.max(query.getCurrent(), 1L); + long size = query.getSize() == null ? 10L : Math.min(Math.max(query.getSize(), 1L), 100L); + query.setCurrent(current); + query.setSize(size); + query.setInvoiceNo(trimOrNull(query.getInvoiceNo())); + query.setSalerName(trimOrNull(query.getSalerName())); + query.setBuyerName(trimOrNull(query.getBuyerName())); + query.setInvoiceType(trimOrNull(query.getInvoiceType())); + query.setSalerTaxNo(trimOrNull(query.getSalerTaxNo())); + query.setBuyerTaxNo(trimOrNull(query.getBuyerTaxNo())); + query.setInvoiceDateStart(normalizeQueryDate(query.getInvoiceDateStart())); + query.setInvoiceDateEnd(normalizeQueryDate(query.getInvoiceDateEnd())); + Page page = new Page<>(current, size); + return invoicePoolMapper.selectInvoicePoolPage(page, query, AuthUtil.getTenantId()); + } + + /** 空白串归一为 null,便于 XML 的 != '' 判空条件 */ + private String trimOrNull(String text) { + return StringUtil.isBlank(text) ? null : text.trim(); + } + + /** 查询日期清洗:空转 null,格式非法直接报业务异常(自定义 SQL 无类型保护,先校验再进 XML) */ + private String normalizeQueryDate(String text) { + if (StringUtil.isBlank(text)) { + return null; + } + try { + return LocalDate.parse(text.trim()).toString(); + } catch (DateTimeParseException exception) { + throw new ServiceException("开票日期格式应为 yyyy-MM-dd"); + } + } + + @Override + public KingdeeInvoicePoolVO fetchKingdeeInvoiceByNo(String invoiceNo) { + if (StringUtil.isBlank(invoiceNo)) { + throw new ServiceException("请填写发票号码"); + } + String trimmedInvoiceNo = invoiceNo.trim(); + // 镜像优先回读:白名单只约束「入镜像」,回读镜像不受限;镜像已有票不再打远程,不受金蝶可用性影响 + KingdeeInvoicePoolVO existingVo = invoicePoolMapper.selectInvoicePoolVO(trimmedInvoiceNo, AuthUtil.getTenantId()); + if (existingVo != null) { + return existingVo; + } + // A801-1 无时间窗口约束,票池更新时间滑出增量窗口的老票也能精确命中;复用窄查后仍保留票号全等匹配的防御过滤 + List poolRows = queryPoolByInvoiceNo(trimmedInvoiceNo); + BfiInvoicePoolVO poolRow = poolRows.stream() + .filter(row -> StringUtil.isNotBlank(row.getInvoiceNo()) + && trimmedInvoiceNo.equals(row.getInvoiceNo().trim())) + .findFirst() + .orElse(null); + if (poolRow == null) { + throw new ServiceException("金蝶票池未查询到发票 " + trimmedInvoiceNo); + } + if (filterByInvoiceTypes(List.of(poolRow)).isEmpty()) { + throw new ServiceException("发票 " + trimmedInvoiceNo + " 的票种不在同步白名单内,不能补查"); + } + if (filterByArchiveTaxNos(customerTaxNoSet(), List.of(poolRow)).isEmpty()) { + throw new ServiceException("发票 " + trimmedInvoiceNo + + " 购销双方存在未建档税号,请先在客商档案维护统一社会信用代码后再补查"); + } + // upsert 自带票面指纹比对,无变化不重写,重复点击安全;随后从镜像回读统一视图(含认领/登记标记) + batchUpsertInvoicePool(List.of(poolRow)); + KingdeeInvoicePoolVO vo = invoicePoolMapper.selectInvoicePoolVO(trimmedInvoiceNo, AuthUtil.getTenantId()); + if (vo == null) { + throw new ServiceException("补查失败:镜像回读不到发票 " + trimmedInvoiceNo); + } + return vo; } @Override @@ -752,6 +819,8 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl page = bfiService.queryInvoicePool(param); @@ -767,6 +836,8 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl page = bfiService.queryInvoicePool(param);