diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsInvoiceRegSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsInvoiceRegSyncSupport.java new file mode 100644 index 00000000..f4ca4d03 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsInvoiceRegSyncSupport.java @@ -0,0 +1,334 @@ +/** + * 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.support.hcs; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.log.exception.ServiceException; +import org.springblade.core.tool.utils.Func; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.hcs.config.HcsProperties; +import org.springblade.hcs.dto.HcsIdVO; +import org.springblade.hcs.dto.HcsInvoiceRegAddDTO; +import org.springblade.hcs.dto.HcsInvoiceRegUpdateDTO; +import org.springblade.hcs.dto.HcsPageQueryDTO; +import org.springblade.hcs.dto.HcsPageVO; +import org.springblade.hcs.dto.HcsInvoiceRegVO; +import org.springblade.hcs.dto.HcsResult; +import org.springblade.hcs.exception.HcsException; +import org.springblade.hcs.feign.IHcsClient; +import org.springblade.transport.constant.KingdeeBfiConstant; +import org.springblade.system.cache.SysCache; +import org.springblade.system.cache.UserCache; +import org.springblade.system.pojo.entity.Dept; +import org.springblade.system.pojo.entity.User; +import org.springblade.transport.constant.HcsSyncConstant; +import org.springblade.transport.mapper.InvoiceApplicationInvoiceMapper; +import org.springblade.transport.mapper.InvoiceApplicationMapper; +import org.springblade.transport.mapper.InvoiceApplicationSettlementMapper; +import org.springblade.transport.mapper.FormalSettlementMapper; +import org.springblade.transport.pojo.entity.FormalSettlement; +import org.springblade.transport.pojo.entity.InvoiceApplication; +import org.springblade.transport.pojo.entity.InvoiceApplicationInvoice; +import org.springblade.transport.pojo.entity.InvoiceApplicationSettlement; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * HCS 开票登记同步支撑组件(C111-1 查询判重 / C111-2 新增 / C111-3 更新,设计文档 §5.7,D12) + *
+ * 审批通过即推 C111-2(票据日期空);金蝶开票完成(kingdeeStatus=invoiced,由金蝶结果回写置待同步)
+ * → C111-3 回填 fdBillDate/fdStatus。判重 C111-1 conditions={"fdNumber": applicationNo}(§9-U4 联调确认)。
+ * fdContractNumber 缺口:InvoiceApplication 无合同字段,经应收结算关联表回溯合同编号(§9-U10,回溯不到失败提示)。
+ *
+ * @author Chill
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class HcsInvoiceRegSyncSupport {
+
+ private final IHcsClient hcsClient;
+ private final HcsProperties hcsProperties;
+ private final InvoiceApplicationMapper invoiceApplicationMapper;
+ private final InvoiceApplicationSettlementMapper invoiceApplicationSettlementMapper;
+ private final InvoiceApplicationInvoiceMapper invoiceApplicationInvoiceMapper;
+ private final FormalSettlementMapper formalSettlementMapper;
+ private final HcsAttachmentSupport hcsAttachmentSupport;
+
+ /**
+ * 推送开票登记:无合规ID 走判重+C111-2 新增(金蝶已开票完成时紧接着 C111-3),有合规ID 走 C111-3
+ *
+ * @param application 开票申请单(approvalStatus=approved)
+ * @return 推送结果描述
+ */
+ public String pushInvoiceRegistration(InvoiceApplication application) {
+ if (StringUtil.isBlank(application.getHcsFdId())) {
+ String existFdId = findExistFdId(application);
+ if (StringUtil.isNotBlank(existFdId)) {
+ markSynced(application, existFdId, "判重命中,回填已有合规开票登记ID");
+ return "判重命中,回填已有合规开票登记ID";
+ }
+ String fdId = addInvoiceRegistration(application);
+ // 开票登记ID 立即持久化(调用方必须保存)
+ markSynced(application, fdId, "C111-2 开票登记新增成功");
+ String result = "C111-2 开票登记新增成功";
+ // 金蝶已开票完成(新增前回写竞态):紧接着回填票据日期
+ if (KingdeeBfiConstant.KINGDEE_STATUS_INVOICED.equals(application.getKingdeeStatus())) {
+ result += ";" + updateInvoiceRegistration(application);
+ }
+ return result;
+ }
+ String result = updateInvoiceRegistration(application);
+ markSynced(application, application.getHcsFdId(), "C111-3 开票登记更新成功");
+ return result;
+ }
+
+ /**
+ * 单条手动重推:重置为待同步并清零重试计数后立即推送
+ *
+ * @param id 开票申请单ID
+ * @return 推送结果描述
+ */
+ public String repushInvoiceRegistration(Long id) {
+ InvoiceApplication application = invoiceApplicationMapper.selectById(id);
+ if (application == null) {
+ throw new ServiceException("开票申请单不存在:" + id);
+ }
+ if (!"approved".equals(application.getApprovalStatus())) {
+ throw new ServiceException("仅审批通过的开票申请可推送合规(当前状态:" + application.getApprovalStatus() + ")");
+ }
+ application.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
+ application.setHcsRetryCount(0);
+ invoiceApplicationMapper.updateById(application);
+ try {
+ return pushInvoiceRegistration(application);
+ } catch (Exception exception) {
+ String message = StringUtil.isBlank(exception.getMessage()) ? "推送异常" : exception.getMessage();
+ markFailed(application, message);
+ throw new ServiceException("HCS开票登记推送失败:" + message);
+ }
+ }
+
+ /**
+ * C111-2 新增开票登记,返回合规 fdId(字段标注照 §3.4 规范,映射见设计文档 §5.7)
+ */
+ private String addInvoiceRegistration(InvoiceApplication application) {
+ HcsInvoiceRegAddDTO param = new HcsInvoiceRegAddDTO();
+ // 单据编号:开票申请单号(同租户唯一,判重候选键)
+ param.setFdNumber(application.getApplicationNo());
+ // 登记日期:applicationDate(毫秒时间戳)
+ param.setFdRegisterDate(toMillis(application.getApplicationDate()));
+ // 【必填】合同编号:经应收结算链路回溯(§9-U10 关联字段确认后细化)
+ param.setFdContractNumber(requireContractNumber(application));
+ // 发票类型:invoice-type-mapping 字典映射(§9-U3,未配置不传)
+ param.setFdInvoiceType(hcsProperties.getInvoice().getInvoiceTypeMapping().get(application.getInvoiceType()));
+ if (param.getFdInvoiceType() == null) {
+ log.warn("HCS开票登记发票类型映射缺失:invoiceType=[{}] 不在 invoice.invoice-type-mapping,fdInvoiceType 不传(§9-U3)",
+ application.getInvoiceType());
+ }
+ // 【必填】结算币种:currency-id-mapping(TMS 币种码 → HCS 币种fdId,经 C103-12 缓存取值)
+ param.setFdCurrencyId(requireCurrencyId(application));
+ // 开票方(销方):issuerName
+ param.setFdInvoiceParty(application.getIssuerName());
+ // 款项性质:配置 invoice.fund-nature-default(默认 progress)
+ param.setFdFundNature(hcsProperties.getInvoice().getFundNatureDefault());
+ // 开票金额(含税):invoiceAmount
+ param.setFdInvoiceAmount(application.getInvoiceAmount());
+ // 备注:remark
+ param.setFdRemark(application.getRemark());
+ // 状态:新增固定 0(已开票)
+ param.setFdStatus(0);
+ // 第三方创建人编码:申请人工号(User.code)
+ param.setFdThirdCreatorCode(resolveCreatorCode(application));
+ // 第三方创建部门编码:申请人部门编码
+ param.setFdThirdCreatorDeptCode(resolveDeptCode(application));
+ // 附件ID列表:attachmentsJson 经 C103-2 转传
+ if (hcsProperties.getAttachment().isTransferEnabled() && StringUtil.isNotBlank(application.getAttachmentsJson())) {
+ param.setFdAttIds(hcsAttachmentSupport.transferAttachments(application.getAttachmentsJson(), resolveCreatorCode(application)));
+ }
+ log.info("HCS开票登记新增开始:applicationNo={}", application.getApplicationNo());
+ HcsResult