diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java index b4329814..3d8e8e1e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java @@ -159,6 +159,12 @@ public final class KingdeeBfiConstant { /** A303-1 特殊票种货物运输判定关键字:命中商品名称或商品和服务分类简称即视为货物运输类。 */ public static final List BFI_INVOICE_FREIGHT_KEYWORDS = List.of("货物运输", "运费"); + /** A303-1 货物运输单据体运输工具种类合法值:字典 transport_type 翻译名称命中则直送,否则兜底其它运输工具。 */ + public static final List BFI_FREIGHT_TRANSPORT_TYPES = List.of("铁路运输", "公路运输", "水路运输", "航空运输", "管道运输", "其它运输工具"); + + /** A303-1 货物运输单据体运输工具种类兜底值。 */ + public static final String BFI_FREIGHT_TRANSPORT_TYPE_OTHER = "其它运输工具"; + /** 金蝶同步状态:开票完成(A303-5 查到发票,终态,开票申请专用)。 */ public static final String KINGDEE_STATUS_INVOICED = "invoiced"; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java index 8c550987..4974787e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/InvoiceApplicationServiceImpl.java @@ -44,6 +44,7 @@ import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyOpenApiQueryDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyQueryDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveEntryDTO; +import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveFreightDTO; import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO; import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyOpenApiBillVO; import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyOpenApiInvoiceVO; @@ -97,6 +98,7 @@ import org.springblade.transport.support.KingdeeArBillSyncSupport; import org.springblade.transport.support.KingdeeSyncLockSupport; import org.springblade.transport.support.KingdeeAttachmentSyncSupport; import org.springblade.transport.wrapper.InvoiceApplicationWrapper; +import org.springblade.transport.wrapper.WaybillWrapper; import org.springframework.beans.factory.annotation.Value; import org.springblade.core.tool.utils.SpringUtil; import org.springframework.stereotype.Service; @@ -780,8 +782,18 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl freights = this.buildFreights(entity); + if (Func.isNotEmpty(freights)) { + param.setFreights(freights); + } else { + log.warn("金蝶开票申请推送:开票申请[{}]按货物运输特殊票种(E04)推送但无结算明细快照,货物运输单据体未填报", + entity.getApplicationNo()); + } + } // 【必填】客户.编码:受票方客商档案客商编码 param.setCustomnameidNumber(customer.getCustomerCode()); // 【必填】项目.项目编码(联调期临时传金蝶测试档案值 999) @@ -893,6 +905,72 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl + * 金蝶数电货运发票要求随单报送运输工具信息,取数与组装规则: + *
    + *
  • 数据来源:开票申请结算明细快照(blade_invoice_application_detail,制单时自正式结算明细快照,含车牌/线路/货物运输信息)
  • + *
  • 字段映射:vehicleNo→licenseplate(运输工具牌号)、departureAddress→startplace(起运地)、 + * arrivalAddress→endplace(到达地)、cargoName→transportgoods(运输货物名称)、 + * transportType→transporttype(运输工具种类,字典键翻译后映射,见 {@link #kingdeeTransportTypeOf(String)})
  • + *
  • 行归并:结算明细按车次成行,同一「车牌×起运地×到达地×货物×运输工具种类」的多笔明细只报送一条
  • + *
  • 空行剔除:牌号/起运地/到达地/货物名称均为空的明细行不报送(快照缺运输信息),避免金蝶侧空行报错
  • + *
+ * + * @param entity 开票申请 + * @return 货物运输单据体分录(无可用快照数据时返回空列表,由调用方决定是否告警) + */ + private List buildFreights(InvoiceApplication entity) { + List details = applicationDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(InvoiceApplicationDetail::getInvoiceApplicationId, entity.getId())); + // 按运程归并键去重:结算明细按车次成行,同一运程多笔明细金蝶侧只需一条(LinkedHashMap 保持明细行序) + Map freightByKey = new LinkedHashMap<>(); + int emptyRowSkipCount = 0; + for (InvoiceApplicationDetail detail : details) { + BfiInvoiceApplySaveFreightDTO freight = new BfiInvoiceApplySaveFreightDTO(); + // 运输工具种类:字典 transport_type 键翻译后映射合法枚举,未命中兜底其它运输工具 + freight.setTransporttype(this.kingdeeTransportTypeOf(detail.getTransportType())); + // 运输工具牌号/运输货物名称/起运地/到达地:结算明细快照直传 + freight.setLicenseplate(detail.getVehicleNo()); + freight.setTransportgoods(detail.getCargoName()); + freight.setStartplace(detail.getDepartureAddress()); + freight.setEndplace(detail.getArrivalAddress()); + // 全空明细行(快照缺运输信息)不报送,避免金蝶侧空行报错 + if (Func.isBlank(freight.getLicenseplate()) && Func.isBlank(freight.getStartplace()) + && Func.isBlank(freight.getEndplace()) && Func.isBlank(freight.getTransportgoods())) { + emptyRowSkipCount++; + continue; + } + String dedupeKey = Func.toStr(freight.getLicenseplate()) + "|" + Func.toStr(freight.getStartplace()) + + "|" + Func.toStr(freight.getEndplace()) + "|" + Func.toStr(freight.getTransportgoods()) + + "|" + Func.toStr(freight.getTransporttype()); + freightByKey.putIfAbsent(dedupeKey, freight); + } + List freights = new ArrayList<>(freightByKey.values()); + log.info("金蝶开票申请货物运输单据体组装:开票申请[{}],结算明细快照{}条,剔除空行{}条,去重归并后分录{}条", + entity.getApplicationNo(), details.size(), emptyRowSkipCount, freights.size()); + freights.forEach(freight -> log.info("金蝶开票申请货物运输分录:开票申请[{}],运输工具种类[{}],牌号[{}],货物[{}],起运地[{}],到达地[{}]", + entity.getApplicationNo(), freight.getTransporttype(), freight.getLicenseplate(), + freight.getTransportgoods(), freight.getStartplace(), freight.getEndplace())); + return freights; + } + + /** + * 运输工具种类映射:结算明细快照存字典 transport_type 键,按业务/系统字典翻译为名称 + * (复用运单展示口径 {@link WaybillWrapper#transportTypeName(String)}), + * 翻译名称命中 A303-1 合法枚举(铁路/公路/水路/航空/管道/其它运输工具)直送,否则兜底其它运输工具 + * + * @param transportType 结算明细快照运输方式(字典键) + * @return 金蝶运输工具种类 + */ + private String kingdeeTransportTypeOf(String transportType) { + String name = WaybillWrapper.transportTypeName(transportType); + return KingdeeBfiConstant.BFI_FREIGHT_TRANSPORT_TYPES.contains(name) + ? name : KingdeeBfiConstant.BFI_FREIGHT_TRANSPORT_TYPE_OTHER; + } + @Override public String syncKingdeeInvoiceResult() { return kingdeeSyncLockSupport.runSingleFlight(INVOICE_RESULT_SYNC_LOCK_PREFIX, invoiceResultLockLeaseSeconds,