diff --git a/.gitignore b/.gitignore index fd4d5e63..b3add6d9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ Thumbs.db .claude /outputs/ /.codex-work/ +.serena/ +.workbuddy/ diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 00000000..83387607 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,82 @@ +# TMS(运输业务中台)—— 业财集成语境 + +TMS 与集团财务系统(金蝶星瀚)、合同合规系统(HCS)集成,单据在三个系统间流转。 +本表只收录集成语境下易混淆的业务术语,不含实现细节。 + +## Language + +**合同合规系统(HCS)**: +集团合同合规管控系统(桂物数字合同合规),接收 TMS 推送的项目、合同、履约与付款数据并做合规审批。 +_Avoid_: 桂物系统、合同系统、数字合同系统 + +**相对方**: +HCS 对签约主体(甲乙方)的称谓,与 TMS 的"客商"同指一个外部单位。 +_Avoid_: 在 HCS 集成语境用"客户/供应商" + +**来源单据键**: +TMS 单据在 HCS 侧的对账主键(接口字段 `fd_kingdee_id`,字段名沿用金蝶),统一存 TMS 业务单据主键。 +_Avoid_: 外部ID、kingdeeId(泛指金蝶单据号时易混淆) + +**来源标识**: +TMS 推送给 HCS 时声明自身身份的常量(接口字段 `fd_sources`,约定值 "TMS")。 +_Avoid_: XH(那是星瀚侧的来源标识) + +**履约规则**: +合同维度的资金计划阶段(付款/收款、款项性质、计划金额、计划日期、开票规则),由 TMS 维护并同步到 HCS。 +_Avoid_: 履约计划(旧版已废弃接口的叫法)、付款计划 + +**履约阶段**: +履约规则表中的一行;HCS 为其生成阶段ID,后续更新/删除必须凭该ID。 +_Avoid_: 阶段项、plan 行 + +**履约记录**: +付款/收款执行事实在 HCS 的登记(一笔付款申请一条记录,随实际付款推进更新),与履约规则是"计划 vs 执行"的关系。 +_Avoid_: 履约流水 + +**额度校验**: +付款提交时由 HCS 判定申请金额是否超出合同/项目剩余额度的同步检查;超额不阻断提交,转入特批链路。 +_Avoid_: 余额检查 + +**特批流程**: +额度超额(或风险不通过)时在 HCS 发起的审批流程;TMS 持有其流程ID,流程结束由 HCS 经 B103 回传结果。 +_Avoid_: 特殊审批、绿色通道 + +**特批放行**: +特批结果为"通过"后对**本次**付款申请解除推送拦截的动作;只改特批状态,不触发推送,单次有效。 +_Avoid_: 特批通过自动付款、放行推送 + +**锁单(B901)**: +金蝶确认接收 TMS 推送的单据后,经 B901 通知 TMS 将本地源单据置为不可编辑、不可作废、不可重推的状态;金蝶反审核及删除后经 B901 解锁。 +_Avoid_: 冻结、金蝶锁定(那是金蝶侧单据自己的状态) + +**解锁**: +B901 通知金蝶侧单据已反审核及删除后,TMS 解除锁单、同步状态置失败、清金蝶单号留痕;不自动重推,人工核实后重新推送。 +_Avoid_: 反审核(那是金蝶侧的动作)、自动重推 + +**合规附件ID**: +TMS 附件先上传 HCS 附件服务换取的 fdId;同一附件以 TMS 附件元数据为键缓存映射,不重复上传。 +_Avoid_: 附件fdId(指代不明) + +**同步状态**: +TMS 单据对 HCS 的推送生命周期(待同步/已同步/失败),权威状态在 TMS 侧字段,HCS 不回写业务状态。 +_Avoid_: 推送标志 + +**二次认领**: +金蝶司库完成第一轮自动认领(客户维度)后,TMS 把收款流水认领到合同/结算单维度的动作;只在 TMS 本地登记,本身不产生金蝶收款单。 +_Avoid_: 收款认领(泛指整条链路时易与金蝶第一轮自动认领混淆) + +**公告未认领金额**: +金蝶认领公告(A301-2)上尚未被认领的金额(字段 unclaimamount),是 TMS 二次认领的上限和"认领完成"的判定基准;以金蝶侧为准,推送前须重查最新值。 +_Avoid_: 剩余可认领金额(那是本地展示口径)、未认领金额(不指明公告侧/本地侧时指代不明) + +**认领完成**: +一条收款流水的累计有效认领金额等于公告未认领金额的状态;只有认领完成的流水才满足推送金蝶的条件,不认领完就不推。 +_Avoid_: 认领完毕、全部认领(未指明对照基准) + +**认领状态**: +收款流水本地认领进度(未认领/部分认领/认领完成),对照公告未认领金额计算;作废的认领分摊不计入累计。 +_Avoid_: 与推送状态混用 + +**推送状态**: +收款流水对金蝶 A301-3 的推送生命周期(未推送/已推送/推送失败),权威在流水级,认领主单仅冗余展示;已推送的流水不可二次推送。 +_Avoid_: 金蝶状态(那是 A301-1 回写的单据生命周期:已推送/审核通过/已收款) diff --git a/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java b/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java index f5e9e342..ac8f3c43 100644 --- a/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java +++ b/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java @@ -32,6 +32,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; +import feign.Logger; +import lombok.AllArgsConstructor; +import org.springblade.common.feign.CustomFeignLogger; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; /** * 公共封装包配置类 @@ -49,4 +56,11 @@ public class BladeCommonConfiguration { return new OperationApiLogAspect(); } + @Bean + @Primary + @ConditionalOnClass(name = "feign.Logger") + public Logger feignLogger() { + return new CustomFeignLogger(); + } + } diff --git a/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java new file mode 100644 index 00000000..fd2c459c --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java @@ -0,0 +1,242 @@ +/** + * 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.common.feign; + +import feign.Logger; +import feign.Request; +import feign.Response; +import feign.Util; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义Feign日志记录器,只记录出入参信息 + * + * @author Chill + */ +@Slf4j +public class CustomFeignLogger extends Logger { + + private static final String BINARY_CONTENT_TYPE_PREFIX = "application/octet-stream"; + private static final String IMAGE_CONTENT_TYPE_PREFIX = "image/"; + private static final String AUDIO_CONTENT_TYPE_PREFIX = "audio/"; + private static final String VIDEO_CONTENT_TYPE_PREFIX = "video/"; + private static final String MULTIPART_CONTENT_TYPE_PREFIX = "multipart/"; + + @Override + protected void log(String configKey, String format, Object... args) { + // 不处理默认日志格式 + } + + @Override + protected void logRequest(String configKey, Level logLevel, Request request) { + String url = request.url(); + String method = request.httpMethod().name(); + Map> headers = request.headers(); + byte[] body = request.body(); + + String contentType = getContentType(headers); + String bodyStr; + if (body == null || body.length == 0) { + bodyStr = "[无]"; + } else if (isMultipartContent(contentType)) { + bodyStr = parseMultipartBody(body, contentType); + } else if (isBinaryContent(contentType)) { + bodyStr = "[二进制数据,长度=" + body.length + "字节]"; + } else { + bodyStr = new String(body, StandardCharsets.UTF_8); + } + + String queryParams = extractQueryParams(url); + if ("GET".equalsIgnoreCase(method) && !queryParams.isEmpty()) { + log.info("===> [{}] {} {} | Headers: {} | Params: {} | Body: {}", configKey, method, url, headers, queryParams, flattenBody(bodyStr)); + } else { + log.info("===> [{}] {} {} | Headers: {} | Body: {}", configKey, method, url, headers, flattenBody(bodyStr)); + } + } + + private String extractQueryParams(String url) { + int queryStart = url.indexOf('?'); + if (queryStart < 0 || queryStart >= url.length() - 1) { + return ""; + } + String queryString = url.substring(queryStart + 1); + Map params = new LinkedHashMap<>(); + for (String pair : queryString.split("&")) { + int eqPos = pair.indexOf('='); + if (eqPos > 0) { + String key = URLDecoder.decode(pair.substring(0, eqPos), StandardCharsets.UTF_8); + String value = URLDecoder.decode(pair.substring(eqPos + 1), StandardCharsets.UTF_8); + params.put(key, value); + } else if (!pair.isEmpty()) { + params.put(URLDecoder.decode(pair, StandardCharsets.UTF_8), ""); + } + } + return params.toString(); + } + + @Override + protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long millis) throws IOException { + String url = response.request().url(); + int status = response.status(); + Map> headers = response.headers(); + + if (response.body() == null) { + log.info("<==== [{}] {} | Status: {} | Duration: {}ms | Body: [空响应]", configKey, url, status, millis); + return response; + } + + byte[] bodyData = Util.toByteArray(response.body().asInputStream()); + String bodyStr; + if (bodyData.length == 0) { + bodyStr = "[空响应]"; + } else if (isBinaryContent(getContentType(headers))) { + bodyStr = "[二进制数据]"; + } else { + bodyStr = new String(bodyData, StandardCharsets.UTF_8); + } + + log.info("<==== [{}] {} | Status: {} | Duration: {}ms | Body: {}", configKey, url, status, millis, flattenBody(bodyStr)); + + return response.toBuilder().body(bodyData).build(); + } + + /** + * 将报文内容压缩为单行显示(换行、回车、制表符等空白序列替换为单个空格) + */ + private String flattenBody(String bodyStr) { + if (bodyStr == null) { + return null; + } + return bodyStr.replaceAll("\\s+", " ").trim(); + } + + /** + * 获取Content-Type + */ + private String getContentType(Map> headers) { + Collection contentTypes = headers.get("Content-Type"); + if (contentTypes != null && !contentTypes.isEmpty()) { + return contentTypes.iterator().next(); + } + return null; + } + + /** + * 判断是否为二进制内容 + */ + private boolean isBinaryContent(String contentType) { + if (contentType == null || contentType.isEmpty()) { + return false; + } + String lowerContentType = contentType.toLowerCase(); + return lowerContentType.startsWith(BINARY_CONTENT_TYPE_PREFIX) + || lowerContentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX) + || lowerContentType.startsWith(AUDIO_CONTENT_TYPE_PREFIX) + || lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX); + } + + /** + * 判断是否为multipart内容 + */ + private boolean isMultipartContent(String contentType) { + return contentType != null && contentType.toLowerCase().startsWith(MULTIPART_CONTENT_TYPE_PREFIX); + } + + /** + * 解析multipart请求体,文件部分用占位符替代,文本字段保留打印 + */ + private String parseMultipartBody(byte[] body, String contentType) { + String boundary = extractBoundary(contentType); + if (boundary == null) { + return "[multipart数据,长度=" + body.length + "字节]"; + } + String bodyStr = new String(body, StandardCharsets.UTF_8); + String delimiter = "--" + boundary; + String[] parts = bodyStr.split(delimiter); + + List fields = new ArrayList<>(); + for (String part : parts) { + if (part.isEmpty() || part.startsWith("--") || part.trim().isEmpty()) { + continue; + } + int headerEnd = part.indexOf("\r\n\r\n"); + if (headerEnd < 0) { + continue; + } + String headers = part.substring(0, headerEnd); + String value = part.substring(headerEnd + 4); + if (value.endsWith("\r\n")) { + value = value.substring(0, value.length() - 2); + } + + String name = extractParam(headers, "name"); + + if (headers.contains("filename=")) { + String filename = extractParam(headers, "filename"); + fields.add(name + "=[文件: " + filename + "]"); + } else { + fields.add(name + "=" + value); + } + } + return fields.isEmpty() ? "[multipart数据]" : "{" + String.join(", ", fields) + "}"; + } + + /** + * 从Content-Type中提取boundary + */ + private String extractBoundary(String contentType) { + for (String param : contentType.split(";")) { + param = param.trim(); + if (param.toLowerCase().startsWith("boundary=")) { + return param.substring("boundary=".length()).trim(); + } + } + return null; + } + + /** + * 从headers中提取参数值(如name、filename) + */ + private String extractParam(String headers, String paramName) { + Pattern pattern = Pattern.compile(paramName + "=\"([^\"]*)\""); + Matcher matcher = pattern.matcher(headers); + if (matcher.find()) { + return matcher.group(1); + } + return paramName; + } +} diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClient.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClient.java index 77eeef8d..c1ebf5b5 100644 --- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClient.java +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClient.java @@ -73,7 +73,9 @@ public interface ISysClient { String REGION = API_PREFIX + "/region"; String FEE_ITEMS = API_PREFIX + "/fee-items"; String CARGO_TYPES = API_PREFIX + "/cargo-types"; + String MEASUREMENT_UNITS = API_PREFIX + "/measurement-units"; String PERMISSIONS = API_PREFIX + "/permissions"; + String INVOICE_ITEMS = API_PREFIX + "/invoice-items"; /** * 获取菜单 @@ -317,6 +319,14 @@ public interface ISysClient { @GetMapping(CARGO_TYPES) R> getCargoTypes(); + /** + * 获取计量单位档案(金蝶推送按计费单位名称匹配取计量单位编码) + * + * @return 计量单位集合 + */ + @GetMapping(MEASUREMENT_UNITS) + R> getMeasurementUnits(); + /** * 获取角色权限标识集合(按钮编号) * @@ -326,4 +336,12 @@ public interface ISysClient { @GetMapping(PERMISSIONS) R> getPermissions(@RequestParam("roleId") String roleId); + /** + * 获取启用的开票项目(含税收分类编码,开票申请推送金蝶按分类+简称匹配取数) + * + * @return 开票项目集合 + */ + @GetMapping(INVOICE_ITEMS) + R> getInvoiceItems(); + } diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClientFallback.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClientFallback.java index 4825a4da..b7704b68 100644 --- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClientFallback.java +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/feign/ISysClientFallback.java @@ -174,9 +174,19 @@ public class ISysClientFallback implements ISysClient { return R.fail("获取数据失败"); } + @Override + public R> getMeasurementUnits() { + return R.fail("获取数据失败"); + } + @Override public R> getPermissions(String roleId) { return R.fail("获取数据失败"); } + @Override + public R> getInvoiceItems() { + return R.fail("获取数据失败"); + } + } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceApplicationSaveRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceApplicationSaveRequest.java index 207879ca..bd0130f7 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceApplicationSaveRequest.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceApplicationSaveRequest.java @@ -71,6 +71,7 @@ public class InvoiceApplicationSaveRequest implements Serializable { @Serial private static final long serialVersionUID = 1L; private String goodsCategory; private String goodsName; + private String taxClassificationCode; private String unit; private BigDecimal quantity; private BigDecimal unitPriceNoTax; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceReceiptSaveRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceReceiptSaveRequest.java index 5f73b6c9..f7b4c29b 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceReceiptSaveRequest.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/InvoiceReceiptSaveRequest.java @@ -42,7 +42,6 @@ import java.util.List; public class InvoiceReceiptSaveRequest implements Serializable { @Serial private static final long serialVersionUID = 1L; private Long id; - private Long kingdeeInvoicePoolId; private String invoiceNo; private LocalDate invoiceDate; private String invoiceType; @@ -54,8 +53,6 @@ public class InvoiceReceiptSaveRequest implements Serializable { private String bankName; private String bankAccount; private String issuingBank; - private String kingdeeBillNo; - private String kingdeeStatus; private String phone; private String customerEmails; private String departmentEmails; 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..4e726481 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/KingdeeInvoicePoolQueryDTO.java @@ -0,0 +1,116 @@ +/** + * 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. + *

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

+ * 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 the License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +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; + + /** + * 发票类型编码(票易通字典,如 26/27,镜像按 invoice_type_code 精确匹配) + */ + @Schema(description = "发票类型编码(票易通字典,如 26/27)") + 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/dto/ReceiptClaimPushResult.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/ReceiptClaimPushResult.java new file mode 100644 index 00000000..dd27a8d9 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/ReceiptClaimPushResult.java @@ -0,0 +1,70 @@ +/** + * 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 + * is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for their own commercial purposes. + *

+ * Using this software signifies agreement to this license agreement and terms of use, + * and you expressly agree to be bound by the terms of use of this software. + *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + *

+ * 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; + +/** + * 收款认领推送金蝶结果(A301-3) + *

+ * 推送失败不抛异常跨事务边界(保证失败状态落库不被回滚),以本结果对象向调用方报告成败与原因。 + * + * @author Chill + */ +@Data +@Schema(description = "收款认领推送金蝶结果") +public class ReceiptClaimPushResult implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "是否推送成功") + private boolean success; + + @Schema(description = "金蝶收款单号(成功时返回,可能为空:金蝶响应未返回单号)") + private String kingdeeBillNo; + + @Schema(description = "失败原因(成功时为空)") + private String message; + + public static ReceiptClaimPushResult success(String kingdeeBillNo) { + ReceiptClaimPushResult result = new ReceiptClaimPushResult(); + result.setSuccess(true); + result.setKingdeeBillNo(kingdeeBillNo); + return result; + } + + public static ReceiptClaimPushResult fail(String message) { + ReceiptClaimPushResult result = new ReceiptClaimPushResult(); + result.setSuccess(false); + result.setMessage(message); + return result; + } +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java index fa780bbf..5ea8bec1 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/CustomerArchive.java @@ -161,4 +161,13 @@ public class CustomerArchive extends TenantEntity { @Schema(description = "审核通过时间") private LocalDateTime approvedTime; + @Schema(description = "金蝶协同状态:none未推送/exist金蝶已有/synced已推送/failed推送失败") + private String kingdeeStatus; + + @Schema(description = "金蝶推送失败原因") + private String kingdeeFailReason; + + @Schema(description = "金蝶推送时间") + private LocalDateTime kingdeeSyncTime; + } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java index 107b7a82..0a03304b 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlement.java @@ -21,6 +21,8 @@ import java.time.LocalDateTime; /** * 正式结算单实体类 + *

+ * 结算单主表,明细快照见 {@link FormalSettlementDetail},合计费用见 {@link FormalSettlementSummaryFee} * * @author Chill */ @@ -30,38 +32,144 @@ import java.time.LocalDateTime; @Schema(description = "正式结算单") public class FormalSettlement extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 正式结算单号(同租户唯一) + */ private String formalSettlementNo; + /** + * 来源类型:预结算合并/应收应付/混合来源 + */ private String sourceType; + /** + * 结算类型:receivable-应收 payable-应付 + */ private String settlementType; + /** + * 所属项目ID + */ private Long projectId; + /** + * 所属项目名称 + */ private String projectName; + /** + * 所属组织ID + */ private Long deptId; + /** + * 所属组织名称 + */ private String deptName; + /** + * 结算合同ID + */ private Long contractId; + /** + * 结算合同编号 + */ private String contractNo; + /** + * 结算合同名称 + */ private String contractName; + /** + * 付款方名称 + */ private String payerName; + /** + * 收款方名称 + */ private String payeeName; + /** + * 结算币种,默认 RMB + */ private String currency; + /** + * 结算金额(结算币种,含全部合计费用) + */ private BigDecimal settlementAmount; + /** + * 本位币,默认 RMB + */ private String localCurrency; + /** + * 本位币结算金额 = 结算金额 × 结算汇率 + */ private BigDecimal localSettlementAmount; + /** + * 已申请付款金额,含来源预结算的预付 + */ private BigDecimal appliedPaymentAmount; + /** + * 已收/已付金额合计,含来源预结算的预付 + */ private BigDecimal paidAmount; + /** + * 剩余可付金额 = 结算金额 - 已收已付合计,最小为 0 + */ private BigDecimal remainingPayableAmount; + /** + * 已开票/已收票金额,取开票申请与收票登记的分摊合计 + */ private BigDecimal invoiceAmount; + /** + * 汇率日期 + */ private LocalDate exchangeRateDate; + /** + * 结算汇率 + */ private BigDecimal exchangeRate; + /** + * 开票状态(应收为开票、应付为收票):unreceived-未开票 partial-部分开票 completed-已开票 + */ private String invoiceStatus; + /** + * 收付款状态:unpaid-未收付款 partial-部分收付款 paid-已收付款 + */ private String paymentStatus; + /** + * 审核状态:draft-草稿 reviewing-审批中 approved-审批通过 returned-已驳回 voided-已作废 + */ private String approvalStatus; + /** + * 当前审批节点,如「草稿」「财务审核」「审批通过」 + */ private String currentNode; + /** + * 当前处理人姓名 + */ private String currentProcessor; + /** + * 金蝶单据编号 + */ private String kingdeeBillNo; + /** + * 金蝶单据id(星瀚内部主键,A902 附件上传定位用) + */ + private String kingdeeBillId; + /** + * 金蝶同步状态:unsynced-未同步 synced-已同步 failed-同步失败 + */ private String kingdeeSyncStatus; + /** + * 附件JSON + */ private String attachmentsJson; + /** + * 备注 + */ private String remark; + /** + * 审核通过时间 + */ private LocalDateTime approvedTime; + /** + * 同步金蝶时间 + */ private LocalDateTime syncedTime; + /** + * 作废原因,驳回时记录驳回原因 + */ private String voidReason; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetail.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetail.java index bf3cda88..899dffe1 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetail.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetail.java @@ -27,37 +27,136 @@ import java.time.LocalDateTime; @TableName("blade_formal_settlement_detail") public class FormalSettlementDetail extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 正式结算单ID + */ private Long formalSettlementId; + /** + * 来源预结算单ID + */ private Long sourcePreSettlementId; + /** + * 来源预结算明细ID + */ private Long sourcePreSettlementDetailId; + /** + * 来源应收应付明细ID + */ private Long sourceDetailId; + /** + * 行号 + */ private Integer lineNo; + /** + * 单据编号 + */ private String documentNo; + /** + * 运单ID + */ private Long waybillId; + /** + * 运单号 + */ private String waybillNo; + /** + * 车牌号 + */ private String vehicleNo; + /** + * 发货地址 + */ private String departureAddress; + /** + * 收货地址 + */ private String arrivalAddress; + /** + * 发货联系人 + */ private String departureContact; + /** + * 发货联系方式 + */ private String departurePhone; + /** + * 收货联系人 + */ private String arrivalContact; + /** + * 收货联系方式 + */ private String arrivalPhone; + /** + * 实际发货时间 + */ private LocalDateTime actualDepartureTime; + /** + * 实际完成时间 + */ private LocalDateTime actualCompletionTime; + /** + * 运输类型 + */ private String transportType; + /** + * 货物名称 + */ private String cargoName; + /** + * 货物类型 + */ private String cargoType; + /** + * 运输总量 + */ private BigDecimal transportQuantity; + /** + * 数量单位 + */ private String quantityUnit; + /** + * 里程 + */ private BigDecimal mileage; + /** + * 批次号 + */ private String batchNo; + /** + * 运输单价 + */ private BigDecimal unitPrice; + /** + * 运费 + */ private BigDecimal freightAmount; + /** + * 费用项JSON + */ private String feeItemsJson; + /** + * 原金额 + */ private BigDecimal originalAmount; + /** + * 调整金额 + */ private BigDecimal adjustAmount; + /** + * 结算金额(含税) + */ private BigDecimal settlementAmountTax; + /** + * 结算金额(不含税) + */ private BigDecimal settlementAmountNoTax; + /** + * 币种 + */ private String currency; + /** + * 备注 + */ private String remark; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetailFee.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetailFee.java index b7e5892a..c4e62e26 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetailFee.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementDetailFee.java @@ -26,20 +26,68 @@ import java.math.BigDecimal; @TableName("blade_formal_settlement_detail_fee") public class FormalSettlementDetailFee extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 正式结算明细ID + */ private Long formalSettlementDetailId; + /** + * 源费用行ID + */ private Long sourceFeeId; + /** + * 行号 + */ private String lineNo; + /** + * 货物名称 + */ private String cargoName; + /** + * 货物类型 + */ private String cargoType; + /** + * 运输量 + */ private BigDecimal transportQuantity; + /** + * 数量单位 + */ private String quantityUnit; + /** + * 里程 + */ private BigDecimal mileage; + /** + * 运输单价 + */ private BigDecimal unitPrice; + /** + * 运费 + */ private BigDecimal freightAmount; + /** + * 费用项JSON + */ private String feeItemsJson; + /** + * 原金额 + */ private BigDecimal originalAmount; + /** + * 调整金额 + */ private BigDecimal adjustAmount; + /** + * 结算金额(含税) + */ private BigDecimal settlementAmountTax; + /** + * 结算金额(不含税) + */ private BigDecimal settlementAmountNoTax; + /** + * 备注 + */ private String remark; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementPayment.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementPayment.java index 962f7ad4..fdfb94eb 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementPayment.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementPayment.java @@ -26,12 +26,20 @@ import java.math.BigDecimal; @TableName("blade_formal_settlement_payment") public class FormalSettlementPayment extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** 正式结算单id */ private Long formalSettlementId; + /** 付款单号(租户内唯一) */ private String paymentNo; + /** 付款类型(final:结算尾款) */ private String paymentType; + /** 申请付款金额 */ private BigDecimal appliedAmount; + /** 已付金额 */ private BigDecimal paidAmount; + /** 单据状态(reviewing:审核中,approved:审核通过,returned:已退回,voided:已作废,paid:已付款) */ private String billStatus; + /** 金蝶(星瀚)单据编号 */ private String kingdeeBillNo; + /** 备注 */ private String remark; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementSummaryFee.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementSummaryFee.java index 9292f68e..acfd797f 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementSummaryFee.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/FormalSettlementSummaryFee.java @@ -28,13 +28,40 @@ import java.math.BigDecimal; @Schema(description = "正式结算合计费用") public class FormalSettlementSummaryFee extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 正式结算单ID + */ private Long formalSettlementId; + /** + * 行号 + */ private Integer lineNo; + /** + * 费用类型 + */ private String feeType; + /** + * 费用项 + */ private String feeItem; + /** + * 原金额 + */ private BigDecimal originalAmount; + /** + * 调整金额 + */ private BigDecimal adjustAmount; + /** + * 结算金额 + */ private BigDecimal settlementAmount; + /** + * 备注 + */ private String remark; + /** + * 是否手工添加 + */ private Integer manualFlag; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplication.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplication.java index bf03a3ce..2b89b282 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplication.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplication.java @@ -47,37 +47,140 @@ import java.time.LocalDateTime; @Schema(description = "开票申请") public class InvoiceApplication extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请单号(同租户唯一,A303-1 billno 幂等候选键) + */ private String applicationNo; + /** + * 所属项目ID(结算单快照带出) + */ private Long projectId; + /** + * 所属项目名称(结算单快照带出) + */ private String projectName; + /** + * 申请部门(所属组织)ID + */ private Long deptId; + /** + * 申请部门(所属组织)名称 + */ private String deptName; + /** + * 开票单位名称(销方=结算单收款方快照,金蝶 salername 取数来源) + */ private String issuerName; + /** + * 受票方客商档案ID + */ private Long receiverCustomerId; + /** + * 受票方单位名称(客商开票信息企业全称快照,金蝶 buyername) + */ private String receiverName; + /** + * 发票类型:electronic_normal-数电普票 / electronic_special-数电专票 + */ private String invoiceType; + /** + * 可开票金额(关联结算单剩余可开票额度合计) + */ private BigDecimal availableInvoiceAmount; + /** + * 本次开票金额(商品行价税合计,金蝶 totalamount) + */ private BigDecimal invoiceAmount; + /** + * 申请日期(提交审批时置当日) + */ private LocalDate applicationDate; + /** + * 申请人姓名 + */ private String applicantName; + /** + * 承办部门ID(结算单所属组织快照) + */ private Long undertakingDeptId; + /** + * 承办部门名称(结算单所属组织快照) + */ private String undertakingDeptName; + /** + * 部门邮箱(发票推送邮箱,多个以分号分隔,最多3个,金蝶 buyeremail) + */ private String departmentEmails; + /** + * 受票方客商开票信息ID(blade_customer_invoice_info) + */ private Long receiverInvoiceInfoId; + /** + * 受票方纳税人识别号(开票信息快照,金蝶 buyertaxno) + */ private String taxpayerNo; + /** + * 受票方开户行(开票信息快照) + */ private String bankName; + /** + * 受票方开户账号(开票信息快照) + */ private String bankAccount; + /** + * 受票方注册地址(开票信息快照) + */ private String registeredAddress; + /** + * 受票方联系人 + */ private String contactName; + /** + * 受票方联系电话 + */ private String contactPhone; + /** + * 受票方邮箱 + */ private String email; + /** + * 审批状态:draft-草稿 reviewing-审批中 approved-审批通过 returned-已驳回 voided-已作废 + */ private String approvalStatus; + /** + * 当前审批节点,如「草稿」「开票审核」「审批通过」 + */ private String currentNode; + /** + * 当前处理人姓名 + */ private String currentProcessor; + /** + * 金蝶开票申请单编号(A303-1 返回,A303-5 查询 serialNo) + */ private String kingdeeBillNo; + /** + * 金蝶开票申请单id(星瀚内部主键,A902 附件上传定位用) + */ + private String kingdeeBillId; + /** + * 金蝶同步状态:unsynced-未同步 synced-已同步 failed-同步失败 invoiced-开票完成(终态) + */ private String kingdeeStatus; + /** + * 同步金蝶时间 + */ private LocalDateTime syncedTime; + /** + * 附件JSON + */ private String attachmentsJson; + /** + * 备注 + */ private String remark; + /** + * 作废原因,驳回时记录驳回原因 + */ private String voidReason; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationDetail.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationDetail.java index 44104935..2d43fc59 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationDetail.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationDetail.java @@ -44,25 +44,88 @@ import java.time.LocalDateTime; @TableName("blade_invoice_application_detail") public class InvoiceApplicationDetail extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请ID + */ private Long invoiceApplicationId; + /** + * 正式结算单ID + */ private Long formalSettlementId; + /** + * 来源正式结算明细ID + */ private Long formalSettlementDetailId; + /** + * 行号 + */ private Integer lineNo; + /** + * 结算明细单据号(快照) + */ private String documentNo; + /** + * 运单号(快照) + */ private String waybillNo; + /** + * 车牌号(快照) + */ private String vehicleNo; + /** + * 起运地(快照) + */ private String departureAddress; + /** + * 到达地(快照) + */ private String arrivalAddress; + /** + * 实际发运时间(快照) + */ private LocalDateTime actualDepartureTime; + /** + * 实际完成时间(快照) + */ private LocalDateTime actualCompletionTime; + /** + * 运输方式(快照,字典 transport_type) + */ private String transportType; + /** + * 货物名称(快照) + */ private String cargoName; + /** + * 货物类型(快照) + */ private String cargoType; + /** + * 运输数量(快照) + */ private BigDecimal transportQuantity; + /** + * 数量单位(快照) + */ private String quantityUnit; + /** + * 里程(快照) + */ private BigDecimal mileage; + /** + * 批次号(快照) + */ private String batchNo; + /** + * 运费金额(快照) + */ private BigDecimal freightAmount; + /** + * 费用项明细JSON(快照) + */ private String feeItemsJson; + /** + * 结算金额(含税,快照) + */ private BigDecimal settlementAmountTax; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationInvoice.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationInvoice.java new file mode 100644 index 00000000..8e145070 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationInvoice.java @@ -0,0 +1,80 @@ +/** + * 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.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.tenant.mp.TenantEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 开票申请金蝶发票结果实体类 + *

+ * 定时任务经 A303-5 开票申请发票查询回写的发票明细(发票号唯一键幂等落库), + * 发票文件地址(板式文件/数电PDF/XML/预览图)独立落库供详情页查看下载, + * 原文 JSON 整体留档便于联调与追溯 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("blade_invoice_application_invoice") +@Schema(description = "开票申请金蝶发票结果") +public class InvoiceApplicationInvoice extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "开票申请ID") + private Long invoiceApplicationId; + + @Schema(description = "金蝶开票申请单编号(A303-5 serialNo)") + private String serialNo; + + @Schema(description = "发票号码") + private String invoiceNo; + + @Schema(description = "开票日期") + private LocalDateTime invoiceDate; + + @Schema(description = "价税合计") + private BigDecimal invoiceAmount; + + @Schema(description = "板式文件下载地址(数电票为OFD,航信百望税盘普通电票为PDF,电子专票/UKey电票为OFD)") + private String invoiceFileUrl; + + @Schema(description = "PDF图片预览地址") + private String invoiceImageUrl; + + @Schema(description = "数电PDF下载地址(发票云5.0.025版本返回)") + private String invoicePdfFileUrl; + + @Schema(description = "数电XML下载地址(发票云5.0.025版本返回)") + private String invoiceXmlFileUrl; + + @Schema(description = "A303-5 返回原文JSON留档") + private String rawJson; + +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationLine.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationLine.java index 30ded9f6..60f04c39 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationLine.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationLine.java @@ -43,18 +43,64 @@ import java.math.BigDecimal; @TableName("blade_invoice_application_line") public class InvoiceApplicationLine extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请ID + */ private Long invoiceApplicationId; + /** + * 所属发票张次ID + */ private Long invoiceSheetId; + /** + * 行号(张内序号) + */ private Integer lineNo; + /** + * 商品和服务分类名称(开票项目主数据选择,金蝶 goodssimplename) + */ private String goodsCategory; + /** + * 货物或服务简称(开票项目主数据选择,金蝶 goodsname) + */ private String goodsName; + /** + * 税收分类编码(选择开票内容时快照,推送金蝶 A303-1 分录直接取该值) + */ + private String taxClassificationCode; + /** + * 单位 + */ private String unit; + /** + * 数量 + */ private BigDecimal quantity; + /** + * 不含税单价 + */ private BigDecimal unitPriceNoTax; + /** + * 不含税金额 + */ private BigDecimal amountNoTax; + /** + * 含税金额(历史兼容字段,与含税合计同值) + */ private BigDecimal amountWithTax; + /** + * 税率(0-100 百分比值,推送金蝶时转小数文本) + */ private BigDecimal taxRate; + /** + * 税额 + */ private BigDecimal taxAmount; + /** + * 价税合计(含税合计,金蝶 taxamount) + */ private BigDecimal totalAmount; + /** + * 行备注 + */ private String remark; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationRecord.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationRecord.java index 8a411bd8..01c9e9b0 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationRecord.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationRecord.java @@ -42,12 +42,36 @@ import java.io.Serial; @TableName("blade_invoice_application_record") public class InvoiceApplicationRecord extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请ID + */ private Long invoiceApplicationId; + /** + * 操作类型:create/save/submit/approve/return/void/sync 等 + */ private String actionType; + /** + * 操作名称(展示用,如「提交审批」「同步金蝶」) + */ private String actionName; + /** + * 操作前审批状态 + */ private String fromStatus; + /** + * 操作后审批状态 + */ private String toStatus; + /** + * 操作人姓名 + */ private String operatorName; + /** + * 原因说明(驳回/作废原因等) + */ private String reason; + /** + * 金蝶单据编号(同步类操作记录) + */ private String kingdeeBillNo; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSettlement.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSettlement.java index 57a03396..ac6ba4ca 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSettlement.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSettlement.java @@ -43,10 +43,28 @@ import java.math.BigDecimal; @TableName("blade_invoice_application_settlement") public class InvoiceApplicationSettlement extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请ID + */ private Long invoiceApplicationId; + /** + * 正式结算单ID + */ private Long formalSettlementId; + /** + * 正式结算单号(关联时快照) + */ private String formalSettlementNo; + /** + * 结算金额(关联时快照) + */ private BigDecimal settlementAmount; + /** + * 可开票金额(关联时快照) + */ private BigDecimal availableInvoiceAmount; + /** + * 本申请分摊开票金额(结算单开票状态按此汇总) + */ private BigDecimal allocatedInvoiceAmount; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSheet.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSheet.java index 8550946d..bee703e4 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSheet.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceApplicationSheet.java @@ -43,7 +43,20 @@ import java.math.BigDecimal; @TableName("blade_invoice_application_sheet") public class InvoiceApplicationSheet extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 开票申请ID + */ private Long invoiceApplicationId; + /** + * 发票张序号(从 1 开始) + */ private Integer sheetNo; + /** + * 本张开票金额(张内商品行价税合计) + */ private BigDecimal invoiceAmount; + /** + * 金蝶开票申请单编号(本张发票对应,A303-1 返回,A303-5 查询 serialNo) + */ + private String kingdeeBillNo; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceReceipt.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceReceipt.java index 67859e1c..9023e07e 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceReceipt.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/InvoiceReceipt.java @@ -36,6 +36,7 @@ import org.springblade.core.tenant.mp.TenantEntity; import java.io.Serial; import java.math.BigDecimal; import java.time.LocalDate; +import java.time.LocalDateTime; /** * 收票登记实体类 @@ -48,36 +49,135 @@ import java.time.LocalDate; @Schema(description = "收票登记") public class InvoiceReceipt extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 关联金蝶票据池镜像主键(blade_kingdee_invoice_pool.id) + */ @JsonSerialize(using = ToStringSerializer.class) private Long kingdeeInvoicePoolId; + /** + * 发票号码(同租户唯一,一张发票仅可登记一次) + */ private String invoiceNo; + /** + * 开票日期 + */ private LocalDate invoiceDate; + /** + * 发票类型(如「数电发票(增值税专用发票)」) + */ private String invoiceType; + /** + * 税率(百分数,13 表示 13%) + */ private BigDecimal taxRate; + /** + * 开票金额 + */ private BigDecimal invoiceAmount; + /** + * 税额 + */ private BigDecimal taxAmount; + /** + * 受票单位(购方) + */ private String receiverName; + /** + * 开票单位(销方) + */ private String issuerName; + /** + * 所属项目id + */ @JsonSerialize(using = ToStringSerializer.class) private Long projectId; + /** + * 所属项目名称 + */ private String projectName; + /** + * 所属组织id + */ @JsonSerialize(using = ToStringSerializer.class) private Long deptId; + /** + * 所属组织名称 + */ private String deptName; + /** + * 付款方 + */ private String payerName; + /** + * 收款方 + */ private String payeeName; + /** + * 开票单位开户银行 + */ private String bankName; + /** + * 开票单位银行账号 + */ private String bankAccount; + /** + * 开票单位开户网点 + */ private String issuingBank; + /** + * 联系电话 + */ private String phone; + /** + * 客户邮箱(分号分隔,最多 3 个) + */ private String customerEmails; + /** + * 部门邮箱(分号分隔,最多 3 个) + */ private String departmentEmails; + /** + * 审批状态(draft 草稿 / reviewing 审批中 / approved 审批通过 / returned 已驳回 / voided 已作废) + */ private String approvalStatus; + /** + * 当前审批节点 + */ private String currentNode; + /** + * 当前处理人 + */ private String currentProcessor; - private String kingdeeBillNo; - private String kingdeeStatus; + /** + * 金蝶收票单单据编号(A303-2 批量保存生成/幂等预检命中回填) + */ + private String kingdeeReceivedInvoiceNo; + /** + * 金蝶收票单id(星瀚内部主键,A902 附件上传定位用) + */ + private String kingdeeReceivedInvoiceId; + /** + * 收票单推送状态(unsynced 未推送 / pushed 已保存收票单 / assigned 已指定应付 / failed 推送失败) + */ + private String kingdeePushStatus; + /** + * 收票单推送失败原因(金蝶侧删除/红冲/查验不通过等异常提示人工) + */ + private String kingdeePushMsg; + /** + * 最近一次收票单推送/回写时间 + */ + private LocalDateTime kingdeePushTime; + /** + * 附件信息JSON + */ private String attachmentsJson; + /** + * 备注(最多 200 字) + */ private String remark; + /** + * 作废原因 + */ private String voidReason; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeAttachmentSync.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeAttachmentSync.java new file mode 100644 index 00000000..c8c0377d --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeAttachmentSync.java @@ -0,0 +1,84 @@ +/** + * 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.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +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 lombok.EqualsAndHashCode; +import org.springblade.core.tenant.mp.TenantEntity; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 金蝶附件同步实体类(A902 附件上传到指定单据,单附件行级状态) + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("blade_kingdee_attachment_sync") +@Schema(description = "金蝶附件同步") +public class KingdeeAttachmentSync extends TenantEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 业务单据类型(pay_application/formal_settlement_ap/formal_settlement_ar/invoice_receipt/invoice_application) + */ + private String bizType; + /** + * 业务单据id + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long bizId; + /** + * 附件幂等键(用户附件=附件url;发票文件=A801-2 的 path) + */ + private String attachmentKey; + /** + * 附件名称(展示/排查用) + */ + private String attachmentName; + /** + * 金蝶侧文件路径(A902 返回 data.path) + */ + private String kingdeePath; + /** + * 同步状态:1 成功 / 0 失败 + */ + private Integer syncStatus; + /** + * 失败原因(超限/下载失败/金蝶报错) + */ + private String failReason; + /** + * 最近上传时间 + */ + private LocalDateTime syncTime; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeBillLock.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeBillLock.java new file mode 100644 index 00000000..1817ce2e --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeBillLock.java @@ -0,0 +1,87 @@ +/** + * 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. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.pojo.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.tenant.mp.TenantEntity; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 金蝶单据锁定记录实体类 + *

+ * 一行 = 一张金蝶目标单:推送成功后 lockBusiData 锁定(FAILED→LOCKED),金蝶经 unLockNotifyBusiData + * 回调解锁(→UNLOCKED,DELETE 时废除源单并还原关联数据);同时承担当前锁状态(编辑守卫/回调定位) + * 与锁定补偿扫表(FAILED+try_count)、锁定/解锁留痕 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("blade_kingdee_bill_lock") +@Schema(description = "金蝶单据锁定记录") +public class KingdeeBillLock extends TenantEntity { + @Serial private static final long serialVersionUID = 1L; + /** + * WLTMS 单据类型(SETTLEMENT/INVOICE_APPLY,预留 PAY_APPLY/RECEIVED_INVOICE) + */ + private String sourceBillType; + /** + * WLTMS 业务主键 + */ + private Long sourceBillId; + /** + * WLTMS 单号(正式结算单号/开票申请单号) + */ + private String sourceBillNo; + /** + * 金蝶单据类型(ApFin_other_BT_S/arfin_other_BT_S/cdm_paybill_bank_elect_BT_S 等固定值) + */ + private String targetBillType; + /** + * 金蝶单据编号(即业务单 kingdeeBillNo,开票申请按发票张次逐张一行) + */ + private String targetBillNo; + /** + * 锁状态:failed-锁定失败(补偿重试对象)/locked-金蝶锁定中/unlocked-已解锁(终态) + */ + private String lockStatus; + /** + * 最近动作来源操作类型:SAVE-单据新增,DELETE-单据删除(DELETE 回调触发废除还原) + */ + private String operationType; + /** + * 锁定重试次数(补偿任务用,超上限停试) + */ + private Integer tryCount; + /** + * 锁定时间 + */ + private LocalDateTime lockTime; + /** + * 解锁时间 + */ + private LocalDateTime unlockTime; + /** + * 推单发起人(锁定操作人) + */ + private String lockOperator; + /** + * 回调操作人(金蝶侧操作人) + */ + private String notifyOperator; + /** + * 锁定失败原因 + */ + private String failReason; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeInvoicePool.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeInvoicePool.java index 261110c3..79eb12e7 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeInvoicePool.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeInvoicePool.java @@ -47,22 +47,108 @@ import java.time.LocalDateTime; @Schema(description = "金蝶进项发票票据池镜像") public class KingdeeInvoicePool extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 发票号码(同租户唯一,A801-1 invoiceNo,镜像 upsert 匹配键) + */ private String invoiceNo; + /** + * 金蝶发票流水号(票池唯一标识,A801-1 serialNo) + */ + private String serialNo; + /** + * 开票日期 + */ private LocalDate invoiceDate; + /** + * 发票类型(票易通编码转名称后落库,如「数电发票(增值税专用发票)」) + */ private String invoiceType; + /** + * 票易通原始发票类型编码(A801-1 invoiceType,如 26/27,A303-2 invoicetypef7_number 取数) + */ + private String invoiceTypeCode; + /** + * 税率(百分数,13 表示 13%) + */ private BigDecimal taxRate; + /** + * 开票金额(A801-1 价税合计 totalAmount) + */ private BigDecimal invoiceAmount; + /** + * 税额 + */ private BigDecimal taxAmount; + /** + * 受票单位(购方名称,A801-1 buyerName) + */ private String receiverName; + /** + * 购方税号(A801-1 buyerTaxNo) + */ + private String buyerTaxNo; + /** + * 开票单位(销方名称,A801-1 salerName) + */ private String issuerName; + /** + * 销方税号(A801-1 salerTaxNo) + */ + private String salerTaxNo; + /** + * 开票单位开户银行 + */ private String bankName; + /** + * 开票单位银行账号 + */ private String bankAccount; + /** + * 开票单位开户网点 + */ private String issuingBank; + /** + * 联系电话 + */ private String phone; + /** + * 客户邮箱(分号分隔,最多 3 个) + */ private String customerEmails; + /** + * 部门邮箱(分号分隔,最多 3 个) + */ private String departmentEmails; + /** + * 金蝶单据号(票池发票取发票流水号 serialNo) + */ private String kingdeeBillNo; + /** + * 金蝶同步状态(unsynced 未同步 / synced 已同步 / failed 同步失败) + */ private String kingdeeStatus; + /** + * 查验状态(A801-1 checkStatus,0 未查验 / 1 通过 / 2 不通过) + */ + private String checkStatus; + /** + * 附件信息JSON(结构 {"source":"piaozone","path":"A801-2下载path","name":"文件名"}) + */ private String attachmentsJson; + /** + * A801-1 发票明细行JSON(BfiInvoicePoolItemVO 列表,票池存档;收票单分录已改与应付单同源结算费用行) + */ + private String itemsJson; + /** + * 票池数据更新时间(A801-1 billCreateTime,镜像排序字段) + */ private LocalDateTime sourceUpdatedTime; + /** + * 剩余可用金额(A801-1 availableAmount,金蝶可用税额口径,选票本地过滤用:>0 才可选) + */ + private BigDecimal availableAmount; + /** + * 金额使用状态(A801-1 amountUseStatus,0 未使用可选 / 2 全部使用;选票本地过滤用) + */ + private String amountUseStatus; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeReceiptFlow.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeReceiptFlow.java index 79b75c1b..533394ae 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeReceiptFlow.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/KingdeeReceiptFlow.java @@ -26,6 +26,8 @@ package org.springblade.transport.pojo.entity; import com.baomidou.mybatisplus.annotation.TableName; +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 lombok.EqualsAndHashCode; @@ -49,16 +51,105 @@ public class KingdeeReceiptFlow extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** + * 金蝶认领公告id(A301-2 返回 id,认领推送 A301-3 必填) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long billId; + /** + * 金蝶付款人id(A301-2 公告对应往来单位 id,认领推送 A301-3 用) + */ + private String payerId; + /** + * 公告认领状态(A301-2 claimstatus:0未认领/1已认领) + */ + private String kingdeeClaimStatus; + /** + * 我已认领(A301-2 claimme) + */ + private Boolean claimMe; + /** + * 未认领金额(A301-2 unclaimamount) + */ + private BigDecimal unclaimedAmount; + /** + * 手续费(A301-2 fee) + */ + private BigDecimal fee; + /** + * 收款人(A301-2 recpayee) + */ + private String payeeName; + /** + * 收款通知单号(A301-2 billno) + */ private String receiptNoticeNo; + /** + * 付款人名称(A301-2 recviewpayer) + */ private String payerName; + /** + * 收款金额(A301-2 reamount) + */ private BigDecimal receiptAmount; + /** + * 对方单位名称(A301-2 oppunit) + */ private String counterpartyName; + /** + * 对方银行账号(A301-2 oppbanknumber) + */ private String counterpartyAccount; + /** + * 对方开户行(A301-2 oppbank) + */ private String counterpartyBank; + /** + * 摘要(A301-2 description) + */ private String summary; + /** + * 交易日期(A301-2 tradetime) + */ private LocalDateTime transactionTime; + /** + * 明细流水号(A301-2 detailid,回退 tradedetailno / bankdetailno,幂等键) + */ private String detailSerialNo; + /** + * 已认领金额(本地认领驱动,初始为0) + */ private BigDecimal claimedAmount; + /** + * 认领状态(unclaimed未认领/partial部分认领/claimed认领完成,累计认领与公告未认领金额对表) + */ private String claimStatus; + /** + * 推送状态(none未推送/pushed已推送/failed推送失败;已推送为终态闸门,不允许二次推送) + */ + private String pushStatus; + /** + * 最近一次推送失败原因(成功后清空) + */ + private String pushFailReason; + /** + * 首次推送金蝶成功时间 + */ + private LocalDateTime pushedTime; + /** + * 金蝶推送来源单据编码(分摊结算单号下划线拼接,推送时生成落库;分摊不变则重试编码不变,A301-1 回写匹配键) + */ + private String awrpSrcCode; + /** + * 金蝶收款单号(推送响应回填 / A301-1 回写推进) + */ + private String kingdeeBillNo; + /** + * 金蝶回写状态(none未推送/pushed已推送/approved审核通过/received已收款/failed失败) + */ + private String kingdeeBillStatus; + /** + * 来源更新时间(取交易日期,同步时刷新) + */ private LocalDateTime sourceUpdatedTime; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java index 1d6d033c..45ab1f4c 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java @@ -2,18 +2,24 @@ * 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. + * 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. + * 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. + * 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. + * 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. + * 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. + * 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) */ @@ -34,43 +40,84 @@ import java.time.LocalDate; @TableName("blade_payment_application") public class PaymentApplication extends TenantEntity { @Serial private static final long serialVersionUID = 1L; + /** 付款申请单号 */ private String paymentNo; + /** 付款类型:project_advance 项目预付 / progress_advance 进度预付 / settlement_payment 结算付款 */ private String paymentType; + /** 正式结算单id */ private Long settlementId; + /** 正式结算单号 */ private String settlementNo; + /** 预结算单id */ private Long preSettlementId; + /** 预结算单号 */ private String preSettlementNo; + /** 项目id */ private Long projectId; + /** 项目名称 */ private String projectName; + /** 所属组织id */ private Long deptId; + /** 所属组织名称 */ private String deptName; + /** 合同id */ private Long contractId; + /** 合同编号 */ private String contractNo; + /** 合同名称 */ private String contractName; + /** 付款方 */ private String payerName; + /** 收款方 */ private String payeeName; + /** 结算金额 */ private BigDecimal settlementAmount; + /** 应付金额 */ private BigDecimal payableAmount; + /** 来源单据类型:项目预付 / 正式结算单 / 预结算单 */ private String billType; + /** 付款比例(%) */ private BigDecimal paymentRatio; + /** 申请付款金额 */ private BigDecimal appliedAmount; + /** 付款方式,业务字典 pay_method */ private String paymentMethod; + /** 汇票台账id(票据付款时关联) */ private Long billLedgerId; + /** 票据号码 */ private String billNo; + /** 收款账户id */ private Long receiptAccountId; + /** 收款账户名称 */ private String receiptAccountName; + /** 收款银行名称 */ private String bankName; + /** 收款账号 */ private String bankAccount; + /** 申请人 */ private String applicantName; + /** 申请日期 */ private LocalDate applyDate; + /** 发票匹配状态:unmatched 未匹配 / matched 已匹配 */ private String invoiceStatus; + /** 匹配发票金额 */ private BigDecimal matchedInvoiceAmount; + /** 已付款金额 */ private BigDecimal paidAmount; + /** 审批状态:draft 草稿 / reviewing 审批中 / approved 审批通过 / returned 已驳回 / voided 已作废 */ private String approvalStatus; + /** 当前审批节点 */ private String currentNode; + /** 当前处理人 */ private String currentProcessor; + /** 金蝶单据号(A103-2 查询回填的星瀚单据编号 billno,如 FKSQ-xxxx) */ private String kingdeeBillNo; + /** 金蝶单据id(A103-1/A103-3 推送返回的星瀚内部主键,附件上传等使用) */ + private String kingdeeBillId; + /** 金蝶同步状态:unsynced 未生成 / synced 已生成 / paying 付款中 / paid 已付款 / closed 已关闭 / failed 生成失败 */ private String kingdeeStatus; + /** 附件信息JSON */ private String attachmentsJson; + /** 备注 */ private String remark; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ReceiptClaim.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ReceiptClaim.java index fc7f7862..2294d0a9 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ReceiptClaim.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ReceiptClaim.java @@ -65,7 +65,13 @@ public class ReceiptClaim extends TenantEntity { private String attachmentsJson; private String remark; private String claimStatus; + /** + * 金蝶收款单号(冗余展示:推送成功/A301-1 回写时由流水级联刷新) + */ private String kingdeeBillNo; + /** + * 金蝶回写状态(冗余展示:与流水 kingdee_bill_status 同步刷新) + */ private String kingdeeBillStatus; @JsonSerialize(using = ToStringSerializer.class) private Long voidedBy; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementVO.java index 1e507376..5929669a 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/FormalSettlementVO.java @@ -9,6 +9,7 @@ package org.springblade.transport.pojo.vo; import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import org.springblade.transport.pojo.entity.FormalSettlement; @@ -18,6 +19,7 @@ import org.springblade.transport.pojo.entity.FormalSettlementInvoice; import org.springblade.transport.pojo.entity.FormalSettlementSource; import org.springblade.transport.pojo.entity.FormalSettlementPayment; import org.springblade.transport.pojo.entity.FormalSettlementSummaryFee; +import org.springblade.transport.pojo.entity.InvoiceApplicationInvoice; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serial; @@ -39,6 +41,15 @@ public class FormalSettlementVO extends FormalSettlement { @TableField(exist = false) private String ids; @TableField(exist = false) private String approvalStatusName; @TableField(exist = false) private String settlementTypeName; + @TableField(exist = false) private Integer attachmentCount; + @TableField(exist = false) private String attachmentSyncStatus; + @TableField(exist = false) private String attachmentSyncStatusName; + @TableField(exist = false) @Schema(description = "金蝶锁状态:locked-锁定中/failed-锁定失败;unlocked为终态不返回,null=无锁或已解锁") + private String kingdeeLockStatus; + @TableField(exist = false) @Schema(description = "金蝶锁失败原因(仅锁定失败时有值)") + private String kingdeeLockFailReason; + @TableField(exist = false) @Schema(description = "金蝶锁重试次数(达上限5后不再自动补偿,需重推同步金蝶)") + private Integer kingdeeTryCount; @TableField(exist = false) private String preSettlementNos; @TableField(exist = false) private String preSettlementNo; @TableField(exist = false) private List sources; @@ -46,6 +57,8 @@ public class FormalSettlementVO extends FormalSettlement { @TableField(exist = false) private List summaryFees; @TableField(exist = false) private List payments; @TableField(exist = false) private List invoices; + @TableField(exist = false) @Schema(description = "开票申请经金蝶回写的发票结果(含文件地址,只读展示)") + private List issuedInvoices; @TableField(exist = false) private List paymentApplications; @TableField(exist = false) private List adjustments; @TableField(exist = false) private List changeRecords; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceApplicationVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceApplicationVO.java index 664d02b0..c54945aa 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceApplicationVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceApplicationVO.java @@ -30,6 +30,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.springblade.transport.pojo.entity.InvoiceApplication; import org.springblade.transport.pojo.entity.InvoiceApplicationDetail; +import org.springblade.transport.pojo.entity.InvoiceApplicationInvoice; import org.springblade.transport.pojo.entity.InvoiceApplicationRecord; import org.springblade.transport.pojo.entity.InvoiceApplicationSettlement; @@ -48,9 +49,16 @@ public class InvoiceApplicationVO extends InvoiceApplication { @TableField(exist = false) private String settlementNos; @TableField(exist = false) private String approvalStatusName; @TableField(exist = false) private String kingdeeStatusName; + @TableField(exist = false) private Integer attachmentCount; + @TableField(exist = false) private String attachmentSyncStatus; + @TableField(exist = false) private String attachmentSyncStatusName; + @TableField(exist = false) private String kingdeeLockStatus; + @TableField(exist = false) private String kingdeeLockFailReason; + @TableField(exist = false) private Integer kingdeeTryCount; @TableField(exist = false) private String createUserName; @TableField(exist = false) private List settlements; @TableField(exist = false) private List sheets; @TableField(exist = false) private List details; @TableField(exist = false) private List records; + @TableField(exist = false) private List invoices; } diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceReceiptVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceReceiptVO.java index 8be4dcf2..2f344ff1 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceReceiptVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/InvoiceReceiptVO.java @@ -48,7 +48,10 @@ public class InvoiceReceiptVO extends InvoiceReceipt { @Serial private static final long serialVersionUID = 1L; @TableField(exist = false) private String settlementNos; @TableField(exist = false) private String approvalStatusName; - @TableField(exist = false) private String kingdeeStatusName; + @TableField(exist = false) private String kingdeePushStatusName; + @TableField(exist = false) private Integer attachmentCount; + @TableField(exist = false) private String attachmentSyncStatus; + @TableField(exist = false) private String attachmentSyncStatusName; @TableField(exist = false) private String createUserName; @TableField(exist = false) private String updateUserName; @TableField(exist = false) private List settlements; 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..001bae66 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/KingdeeInvoicePoolVO.java @@ -0,0 +1,157 @@ +/** + * 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; + + /** 镜像主键,仅已入镜像(已登记)的票非空 */ + @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 availableTaxAmount; + + /** 默认认领金额,口径同发票金额 */ + @Schema(description = "默认认领金额") + private BigDecimal matchedAmount; + + /** 是否已被正式结算单认领(实时查询返回行的本地装饰标记,仅展示不作过滤) */ + @Schema(description = "是否已认领") + private Boolean claimed; + + /** 是否已存在非作废收票登记(实时查询返回行的本地装饰标记,仅展示不作过滤) */ + @Schema(description = "是否已登记") + private Boolean registered; +} diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java index 6e6c183a..d8bc998f 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/PaymentApplicationVO.java @@ -43,6 +43,12 @@ public class PaymentApplicationVO extends PaymentApplication { @TableField(exist = false) private String paymentTypeName; @TableField(exist = false) private String approvalStatusName; @TableField(exist = false) private String kingdeeStatusName; + @TableField(exist = false) private Integer attachmentCount; + @TableField(exist = false) private String attachmentSyncStatus; + @TableField(exist = false) private String attachmentSyncStatusName; + @TableField(exist = false) private String kingdeeLockStatus; + @TableField(exist = false) private String kingdeeLockFailReason; + @TableField(exist = false) private Integer kingdeeTryCount; @TableField(exist = false) private List invoices; @TableField(exist = false) private List paymentRecords; @TableField(exist = false) private List settlements; diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptClaimRecordVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptClaimRecordVO.java index 9802b661..400af8ae 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptClaimRecordVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptClaimRecordVO.java @@ -76,6 +76,11 @@ public class ReceiptClaimRecordVO extends ReceiptClaim { private String claimStatusName; @TableField(exist = false) private String kingdeeBillStatusName; + /** + * 关联流水推送状态(none未推送/pushed已推送/failed推送失败,查询条件) + */ + @TableField(exist = false) + private String pushStatus; @TableField(exist = false) private List settlements; @TableField(exist = false) diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptFlowVO.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptFlowVO.java index b282c745..f32a9491 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptFlowVO.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/vo/ReceiptFlowVO.java @@ -56,6 +56,12 @@ public class ReceiptFlowVO extends KingdeeReceiptFlow { @TableField(exist = false) private String claimStatusName; @TableField(exist = false) + private String kingdeeClaimStatusName; + @TableField(exist = false) + private String pushStatusName; + @TableField(exist = false) + private String kingdeeBillStatusName; + @TableField(exist = false) private BigDecimal remainingAmount; @TableField(exist = false) private String createUserName; @@ -73,6 +79,16 @@ public class ReceiptFlowVO extends KingdeeReceiptFlow { @TableField(exist = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime transactionEndTime; + /** + * 收款金额区间-最小值(查询条件) + */ + @TableField(exist = false) + private BigDecimal receiptAmountStart; + /** + * 收款金额区间-最大值(查询条件) + */ + @TableField(exist = false) + private BigDecimal receiptAmountEnd; @TableField(exist = false) private List claimRecords = new ArrayList<>(); } diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/feign/SysClient.java b/blade-service/blade-system/src/main/java/org/springblade/system/feign/SysClient.java index d08d36a6..edacf506 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/feign/SysClient.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/feign/SysClient.java @@ -66,6 +66,8 @@ public class SysClient implements ISysClient { private final IFeeItemService feeItemService; private final ICargoTypeService cargoTypeService; + private final IMeasurementUnitService measurementUnitService; + private final IInvoiceItemService invoiceItemService; @Override @GetMapping(MENU) @@ -231,10 +233,27 @@ public class SysClient implements ISysClient { .orderByAsc(CargoType::getCargoCode))); } + @Override + @GetMapping(MEASUREMENT_UNITS) + public R> getMeasurementUnits() { + return R.data(measurementUnitService.list(Wrappers.lambdaQuery() + .eq(MeasurementUnit::getIsDeleted, 0) + .orderByAsc(MeasurementUnit::getUnitCode))); + } + @Override @GetMapping(PERMISSIONS) public R> getPermissions(String roleId) { return R.data(menuService.permissionCodes(roleId)); } + @Override + @GetMapping(INVOICE_ITEMS) + public R> getInvoiceItems() { + return R.data(invoiceItemService.list(Wrappers.lambdaQuery() + .eq(InvoiceItem::getStatus, 1) + .eq(InvoiceItem::getIsDeleted, 0) + .orderByAsc(InvoiceItem::getCategoryName, InvoiceItem::getShortName))); + } + } diff --git a/blade-service/blade-transport/pom.xml b/blade-service/blade-transport/pom.xml index f55739f3..7f7e843b 100644 --- a/blade-service/blade-transport/pom.xml +++ b/blade-service/blade-transport/pom.xml @@ -47,10 +47,19 @@ org.springblade blade-process-api + + org.springblade + blade-bfi-api + org.springframework.boot spring-boot-starter-amqp + + org.springframework.boot + spring-boot-starter-test + test + io.minio minio diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/TransportApplication.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/TransportApplication.java index 27a73b5b..f5ece848 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/TransportApplication.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/TransportApplication.java @@ -27,12 +27,14 @@ package org.springblade.transport; import org.springblade.core.cloud.client.BladeCloudApplication; import org.springblade.core.launch.BladeApplication; +import org.springframework.scheduling.annotation.EnableScheduling; /** * 运输服务启动器 * * @author Chill */ +@EnableScheduling @BladeCloudApplication public class TransportApplication { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/config/BfiProperties.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/config/BfiProperties.java new file mode 100644 index 00000000..ecc40f74 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/config/BfiProperties.java @@ -0,0 +1,413 @@ +/** + * 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.config; + +import lombok.Data; +import org.springblade.thirdparty.bfi.constant.BfiConstant; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 业财融合(BFI)推送金蝶配置属性,统一收口 Nacos 配置 thirdParty.bfi.*: + *

+ * srcSys 为客商、付款申请等各业务推送金蝶的公共参数(外部系统编号); + * customer、payApply 为各业务专属参数;未配置时使用字段默认值。 + *

+ * prefix 须为全小写(thirdparty.bfi),Spring 宽松绑定可匹配 Nacos 中的 camelCase 键(thirdParty.payApply.srcSys 等)。 + * + * @author Chill + */ +@Data +@Component +@ConfigurationProperties(prefix = "thirdparty.bfi") +public class BfiProperties { + + /** + * 数据来源系统编码(公共):客商、付款申请等推送金蝶时统一使用,默认 WLTMS(运输TMS) + */ + private String srcSys = BfiConstant.DATA_SOURCE_WLTMS; + + /** + * 付款申请推送配置(A103-3 付款申请保存) + */ + private PayApply payApply = new PayApply(); + + /** + * 开票申请推送配置(A303-1 开票申请单新增 / A303-5 开票申请发票查询) + */ + private InvoiceApply invoiceApply = new InvoiceApply(); + + /** + * 收票单推送配置(A303-2 收票单批量保存 / A302-6 收票指定应付 / A801-3 收票单查询回写) + */ + private ReceivedInvoice receivedInvoice = new ReceivedInvoice(); + + /** + * 收款认领公告查询配置(A301-2) + */ + private ClaimAnnounce claimAnnounce = new ClaimAnnounce(); + + /** + * 付款结果定时回写配置(A103-2/A103-4,总开关同时控制应收勾稽、开票结果回写) + */ + private PayResult payResult = new PayResult(); + + /** + * 收票单结果定时回写开关(A801-3) + */ + private ReceivedInvoiceResult receivedInvoiceResult = new ReceivedInvoiceResult(); + + /** + * 收款认领推送配置(A301-3) + */ + private ReceiptClaimPush receiptClaimPush = new ReceiptClaimPush(); + + /** + * 全票池发票定时同步配置(A801-1) + */ + private InvoiceReceipt invoiceReceipt = new InvoiceReceipt(); + + /** + * 客商金蝶数据定时同步配置(A902-1/A902-2) + */ + private Customer customer = new Customer(); + + /** + * 金蝶黑名单查询配置 + */ + private Blacklist blacklist = new Blacklist(); + + /** + * 开票结果回写锁配置(A303-5) + */ + private InvoiceResult invoiceResult = new InvoiceResult(); + + /** + * 应收单勾稽回写锁配置(A302-4) + */ + private ArResult arResult = new ArResult(); + + /** + * 金蝶单据锁定补偿配置 + */ + private BillLock billLock = new BillLock(); + + /** + * 审批通过自动同步金蝶配置(结算单/付款申请/开票申请/收票登记) + */ + private AutoSync autoSync = new AutoSync(); + + /** + * 应付/应收/收票单分录物料映射配置(A102-2/A302-2/A303-2 共用): + * 运输类按项目业务模式映射——国内运输按运单运输方式映射、国际运输映射国际货代服务; + * 装卸大类及手动添加的非运费杂费映射装卸物料 + */ + private Material material = new Material(); + + /** + * 付款申请推送配置项 + */ + @Data + public static class PayApply { + + /** + * 单据类型编码:ap_payapply_BT_S 付款申请单、ap_payapply_oth_BT_S 其他付款单,默认其他付款单 + */ + private String billtype = "ap_payapply_oth_BT_S"; + + /** + * 汇率表编码,默认 01 + */ + private String exratetable = "01"; + + /** + * 外部单据类型,默认 0KzXs + */ + private String srcType = "0KzXs"; + } + + /** + * 开票申请推送配置项(A303-1 / A303-5): + * 销方名称取申请单开票单位快照、销方税号取客商档案统一社会信用代码、税收分类编码取商品行开票内容快照,均不走配置 + */ + @Data + public static class InvoiceApply { + + /** + * 分录外部单据类型,默认 0KzXs(与付款申请对齐) + */ + private String srcType = "0KzXs"; + } + + /** + * 收票单推送配置项(A303-2 / A302-6 / A801-3): + * 发票类型编码、税收分类编码、购销方税号地址银行均取票池镜像与明细,不走配置 + */ + @Data + public static class ReceivedInvoice { + + /** + * A303-2 分录物料编码:联调期临时值(金蝶测试档案),发票明细无金蝶物料概念, + * 真实物料口径与金蝶对齐后替换(与应收/应付联调临时值同源) + */ + private String materialNumber = "SP0324"; + + /** + * A801-3 推送幂等预检回看窗口天数(金蝶修改时间窗口,契约约束不超过 1 个月) + */ + private int queryWindowDays = 30; + + /** + * A801-3 定时回写轮询窗口天数(同样受金蝶 1 个月窗口约束) + */ + private int resultWindowDays = 30; + + /** + * A801-3 回写 single-flight 锁租约秒数,须大于最坏一轮耗时(批量远程查询 + 逐单回写) + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 收款认领公告查询配置项(A301-2) + */ + @Data + public static class ClaimAnnounce { + + /** + * 查询时间窗口(月数),默认 6(近半年) + */ + private int months = 6; + + /** + * 认领人(claimusers):配置后公告查询固定使用该值,不再按当前登录用户姓名查询 + */ + private String claimusers; + } + + /** + * 分录物料映射配置项(thirdparty.bfi.material.*):默认值与金蝶测试档案对齐, + * 金蝶正式档案确认后直接在 Nacos 覆盖 + */ + @Data + public static class Material { + + /** + * 国内运输:运单运输方式 -> 金蝶物料编码(公路运输/铁路运输/水路运输/航空运输四种), + * 运输方式未配置映射时直接拦截报错 + */ + private Map domestic = new LinkedHashMap<>() {{ + put("公路运输", "XDWL11010001"); + put("铁路运输", "XDWL11010002"); + put("水路运输", "XDWL11010003"); + put("航空运输", "Item-00001082"); + }}; + + /** + * 国际运输业务模式物料编码(国际货运代理服务) + */ + private String international = "Item-00001081"; + + /** + * 装卸大类、手动添加非运费杂费物料编码(装卸) + */ + private String loading = "XDWL11002004"; + } + + /** + * 付款结果回写配置项(A103-2/A103-4) + */ + @Data + public static class PayResult { + + /** + * 回写任务开关,代码默认关闭(安全兜底),由 Nacos 按环境显式开启 + */ + private boolean syncEnabled = false; + + /** + * 回写 single-flight 锁租约秒数,须大于最坏一轮耗时(分批远程查询 + 逐单回写) + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 收票单结果回写配置项(A801-3) + */ + @Data + public static class ReceivedInvoiceResult { + + /** + * 回写任务开关,默认关闭(联调稳定后开启) + */ + private boolean syncEnabled = false; + } + + /** + * 收款认领推送配置项(A301-3) + */ + @Data + public static class ReceiptClaimPush { + + /** + * 推送任务开关,默认关闭(联调稳定后开启) + */ + private boolean syncEnabled = false; + + /** + * 推送 single-flight 锁租约秒数,须大于最坏一轮耗时(逐认领人远程查公告 + 逐条推送) + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 全票池发票同步配置项(A801-1) + */ + @Data + public static class InvoiceReceipt { + + /** + * 同步任务开关,默认关闭(上线先手动跑一次全窗口铺底) + */ + private boolean syncEnabled = false; + + /** + * 增量窗口天数(按票池更新时间滚动) + */ + private int incrementDays = 30; + + /** + * 同步发票种类白名单(逗号分隔票易通编码,如 26,27 数电普票/专票),为空不限制 + */ + private String invoiceTypes = ""; + + /** + * 同步 single-flight 锁租约秒数,须大于最坏一轮耗时;进程异常退出时锁在租约到期后自动释放 + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 客商金蝶数据同步配置项(A902-1/A902-2) + */ + @Data + public static class Customer { + + /** + * 同步任务开关,默认关闭 + */ + private boolean syncEnabled = false; + + /** + * 同步 single-flight 锁租约秒数,须大于最坏一轮耗时(复查 + 批量推送 + 停用联动) + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 金蝶黑名单查询配置项 + */ + @Data + public static class Blacklist { + + /** + * 客商提审命中风险警示类(B)时是否一并拦截;默认 false 仅放行告警,禁止交易类(A)始终拦截 + */ + private boolean blockRiskWarning = false; + } + + /** + * 开票结果回写锁配置项(A303-5) + */ + @Data + public static class InvoiceResult { + + /** + * 回写 single-flight 锁租约秒数 + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 应收单勾稽回写锁配置项(A302-4) + */ + @Data + public static class ArResult { + + /** + * 勾稽 single-flight 锁租约秒数 + */ + private long lockLeaseSeconds = 1800; + } + + /** + * 金蝶单据锁定补偿配置项 + */ + @Data + public static class BillLock { + + /** + * 锁定补偿开关,默认关闭(安全兜底) + */ + private boolean retryEnabled = false; + } + + /** + * 审批通过自动同步金蝶配置项(结算单 A102-2/A302-2、付款申请 A103-1/A103-3、开票申请 A303-1、收票 A303-2+A302-6) + */ + @Data + public static class AutoSync { + + /** + * 结算单审批后自动推送开关 + */ + private boolean settlement = false; + + /** + * 付款申请审批后自动推送开关 + */ + private boolean payment = false; + + /** + * 开票申请审批后自动推送开关 + */ + private boolean invoiceApply = false; + + /** + * 收票登记审批后自动推送开关 + */ + private boolean receipt = false; + + /** + * failed 单据定时重推开关(仅重推生成失败,不扫 unsynced 存量) + */ + private boolean retryEnabled = false; + } + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/CustomerBfiConstants.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/CustomerBfiConstants.java new file mode 100644 index 00000000..712643da --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/CustomerBfiConstants.java @@ -0,0 +1,63 @@ +/** + * 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.constant; + +/** + * 客商档案金蝶(星瀚)协同常量 + * + * @author Chill + */ +public class CustomerBfiConstants { + + /** + * 推送金蝶固定值:业务职能全选(1销售,2结算,3付款,4收货) + */ + public static final String BIZ_FUNCTION_ALL = "1,2,3,4"; + + /** + * 客商类型:内部组织(customer_kind 字段值) + */ + public static final String CUSTOMER_KIND_INTERNAL = "internal"; + + /** + * 客商类型:外部客商(customer_kind 字段值) + */ + public static final String CUSTOMER_KIND_EXTERNAL = "external"; + + /** + * 金蝶伙伴类型编码有效范围上界(BfiCustomerSaveDTO.type 全集 1~17) + */ + public static final int PARTNER_TYPE_MAX = 17; + + /** + * 营业期限类型:长期(TMS 表单文本) + */ + public static final String BUSINESS_TERM_LONG = "长期"; + + private CustomerBfiConstants() { + } + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeAttachmentConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeAttachmentConstant.java new file mode 100644 index 00000000..1355bcc6 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeAttachmentConstant.java @@ -0,0 +1,176 @@ +/** + * 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.constant; + +import java.util.Map; + +import org.springblade.core.log.exception.ServiceException; + +/** + * 金蝶附件同步(A902)常量 + *

entityNumber/controlKey 映射以常量维护,二期客商(资质附件)等金蝶确认后补充条目即可,不改架构

+ * + * @author Chill + */ +public class KingdeeAttachmentConstant { + + // ==================== 业务单据类型(biz_type) ==================== + + /** + * 付款申请单 + */ + public static final String BIZ_PAY_APPLICATION = "pay_application"; + /** + * 正式结算-财务应付单面板 + */ + public static final String BIZ_FORMAL_SETTLEMENT_AP = "formal_settlement_ap"; + /** + * 正式结算-财务应收单面板 + */ + public static final String BIZ_FORMAL_SETTLEMENT_AR = "formal_settlement_ar"; + /** + * 收票单 + */ + public static final String BIZ_INVOICE_RECEIPT = "invoice_receipt"; + /** + * 开票申请单 + */ + public static final String BIZ_INVOICE_APPLICATION = "invoice_application"; + + // ==================== 星瀚 entityNumber(A902 定位单据类型) ==================== + + /** + * 付款申请单 + */ + public static final String ENTITY_PAY_APPLICATION = "ap_payapply"; + /** + * 财务应付单(正式结算应付面板) + */ + public static final String ENTITY_AP_BILL = "ap_finapbill"; + /** + * 财务应收单(正式结算应收面板) + */ + public static final String ENTITY_AR_BILL = "ar_finarbill"; + /** + * 收票单 + */ + public static final String ENTITY_INVOICE = "ap_invoice"; + /** + * 开票申请单 + */ + public static final String ENTITY_INVOICE_APPLICATION = "sim_original_bill"; + + /** + * 业务单据类型 -> 星瀚 entityNumber 映射; + * 客商(资质附件)二期接入:待金蝶确认后补充专用 biz_type 与 entityNumber 条目 + */ + public static final Map ENTITY_NUMBERS = Map.of( + BIZ_PAY_APPLICATION, ENTITY_PAY_APPLICATION, + BIZ_FORMAL_SETTLEMENT_AP, ENTITY_AP_BILL, + BIZ_FORMAL_SETTLEMENT_AR, ENTITY_AR_BILL, + BIZ_INVOICE_RECEIPT, ENTITY_INVOICE, + BIZ_INVOICE_APPLICATION, ENTITY_INVOICE_APPLICATION + ); + + /** + * 星瀚附件面板/附件字段标识(各单据统一) + */ + public static final String CONTROL_KEY = "attachmentpanel"; + + // ==================== 限制 ==================== + + /** + * 单文件软限制 20MB(待金蝶确认后调整);超限记失败原因跳过,不阻塞同单其余附件 + */ + public static final long MAX_FILE_SIZE_BYTES = 20L * 1024 * 1024; + + // ==================== 同步行状态(sync_status) ==================== + + /** + * 同步失败 + */ + public static final int SYNC_STATUS_FAILED = 0; + /** + * 同步成功(终态,幂等跳过) + */ + public static final int SYNC_STATUS_SUCCESS = 1; + + // ==================== 附件同步聚合态(列表展示,运行时聚合非存储字段) ==================== + + /** + * 无附件:单据 attachmentsJson 无可上传附件 + */ + public static final String AGGREGATE_NONE = "none"; + /** + * 未上传:有附件且成功数为 0 + */ + public static final String AGGREGATE_PENDING = "pending"; + /** + * 部分未传:有附件且部分上传成功 + */ + public static final String AGGREGATE_PARTIAL = "partial"; + /** + * 已上传:有附件且全部上传成功 + */ + public static final String AGGREGATE_DONE = "done"; + + /** + * 聚合态 -> 展示名称 + */ + public static final Map AGGREGATE_NAMES = Map.of( + AGGREGATE_NONE, "无附件", + AGGREGATE_PENDING, "未上传", + AGGREGATE_PARTIAL, "部分未传", + AGGREGATE_DONE, "已上传" + ); + + /** + * 业务单据类型对应星瀚 entityNumber + * + * @param bizType 业务单据类型 + * @return entityNumber,未配置时抛业务异常(entityNumber 是硬约束,猜错即传不上) + */ + public static String entityNumber(String bizType) { + String entityNumber = ENTITY_NUMBERS.get(bizType); + if (entityNumber == null) { + throw new ServiceException("金蝶附件上传失败:业务单据类型[" + bizType + "]未配置 entityNumber 映射"); + } + return entityNumber; + } + + /** + * 聚合态展示名称(未知态兜底返回原文) + * + * @param aggregateStatus 聚合态 + * @return 展示名称 + */ + public static String aggregateName(String aggregateStatus) { + return AGGREGATE_NAMES.getOrDefault(aggregateStatus, aggregateStatus); + } + + private KingdeeAttachmentConstant() { + } +} 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 new file mode 100644 index 00000000..b1c45f44 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java @@ -0,0 +1,253 @@ +/** + * 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.constant; + +import java.util.List; +import java.util.Map; + +/** + * 付款同步金蝶(星瀚)固定值。 + * 金蝶侧档案编码暂以常量维护,待金蝶侧确认取值后直接修改常量重新发包。 + * + * @author Chill + */ +public final class KingdeeBfiConstant { + + /** 往来类型:供应商(付款申请与财务应付单共用)。 */ + public static final String BFI_SUPPLIER_ASSTACT_TYPE = "bd_supplier"; + + /** 往来类型:客户(财务应付单按合同类别为客户合同时使用)。 */ + public static final String BFI_CUSTOMER_ASSTACT_TYPE = "bd_customer"; + + /** 来源单据类型:上游合同(有合同时传 awrp_src_bt)。 */ + public static final String BFI_CONTRACT_SRC_BT = "awrp_contract_info"; + + /** 无项目时传的项目编码占位值。 */ + public static final String BFI_PROJECT_NUMBER_NONE = "999"; + + /** 联调默认物料编码:金蝶测试档案值,真实货物类型档案编码与金蝶对齐后替换。 */ + public static final String BFI_AP_BILL_MATERIAL_NUMBER_TEST = "SP0324"; + + /** 联调默认计量单位编码:金蝶测试档案值,档案维护计量单位编码后替换。 */ + public static final String BFI_AP_BILL_MEASUREUNIT_NUMBER_TEST = "TON"; + /** 联调默认银行行号:金蝶测试档案值,档案维护银行行号后替换。 */ + public static final String BFI_AP_BILL_BANKACCOUNT_NUMBER_TEST = "001110012009"; + + /** 联调默认项目编码:金蝶测试档案值,项目档案与金蝶对齐后替换。 */ + public static final String BFI_AP_BILL_PROJECT_NUMBER_TEST = "999"; + + /** 付款类型编码:正式付款(结算付款)。 */ + public static final String BFI_PAYMENT_TYPE_FORMAL = "201"; + + /** 付款类型编码:预付款。 */ + public static final String BFI_PAYMENT_TYPE_PREPAY = "202"; + + /** A102-2 默认单据类型名称:服务采购应付(经 AP_BILL_BILLTYPE_MAP 转换为编码)。待确认:具体类型需金蝶侧最终确认,确认后修改此名称即可。 */ + public static final String BFI_AP_BILL_BILLTYPE_NAME = "服务采购应付"; + + /** A102-2 款项性质编码(暂按 2001 材料采购)。待确认:编码需金蝶侧最终确认,确认后如有调整直接改此常量。 */ + public static final String BFI_AP_BILL_PAY_PROPERTY = "2001"; + + /** + * A102-2 付款方式(赊购 CREDIT,允许金蝶侧后续调整付款金额; + */ + public static final String BFI_AP_BILL_PUR_MODE_CREDIT = "CREDIT"; + /** + * A102-2 付款方式 现购 CASH 会由金蝶自动 100% 推付款)。 + */ + public static final String BFI_AP_BILL_PUR_MODE_CASH = "CASH"; + + /** A102-2 必填:外部单据类型(应付单来源标识)。待确认:取值需金蝶侧确认,确认后如有调整直接改此常量。 */ + public static final String BFI_AP_BILL_SRC_TYPE = "TMS财务应付单"; + + /** 财务应付单(ap_finapbill)单据类型映射:中文名称 -> 星瀚编码。 */ + public static final Map AP_BILL_BILLTYPE_MAP = Map.ofEntries( + Map.entry("借贷项调整", "ApFin_borr_BT_S"), + Map.entry("费用采购应付", "ApFin_fee_BT_S"), + Map.entry("其他应付", "ApFin_other_BT_S"), + Map.entry("产品委外应付", "ApFin_product_BT_S"), + Map.entry("标准采购应付", "ApFin_pur_BT_S"), + Map.entry("采购费用应付", "ApFin_purfee_BT_S"), + Map.entry("销售费用应付", "ApFin_salefee_BT_S"), + Map.entry("服务采购应付", "ApFin_service_BT_S"), + Map.entry("资产采购应付", "ap_finapbill_asset_BT_S") + ); + + /** 财务应收单(ar_finarbill)单据类型映射:中文名称 -> 星瀚编码(当前付款链路未启用,应收结算单推送规则待业务确认)。 */ + public static final Map AR_BILL_BILLTYPE_MAP = Map.ofEntries( + Map.entry("借贷项调整", "arfin_borrowar_BT_S"), + Map.entry("其他应收", "arfin_other_BT_S"), + Map.entry("销售费用应收", "arfin_salefee_BT_S"), + Map.entry("服务销售应收", "arfin_sersal_BT_S"), + Map.entry("标准销售应收", "arfin_standard_BT_S") + ); + + /** A302-2 默认单据类型名称:服务销售应收(经 AR_BILL_BILLTYPE_MAP 转换为编码)。待确认:具体类型需金蝶侧最终确认,确认后修改此名称即可。 */ + public static final String BFI_AR_BILL_BILLTYPE_NAME = "服务销售应收"; + + /** A302-2 款项性质编码(暂按 1001 一般销售,与应付 2001 材料采购区分)。待确认:编码需金蝶侧最终确认,确认后如有调整直接改此常量。 */ + public static final String BFI_AR_BILL_PAY_PROPERTY = "1001"; + + /** A302-2 付款方式:现销 CASH(与应付现购对称,由金蝶自动推收款)。待确认:取值需金蝶侧确认。 */ + public static final String BFI_AR_BILL_PAY_MODE_CASH = "CASH"; + + /** A302-2 必填:外部单据类型(应收单来源标识)。待确认:取值需金蝶侧确认,确认后如有调整直接改此常量。 */ + public static final String BFI_AR_BILL_SRC_TYPE = "TMS财务应收单"; + + /** A303-1 上游来源单据类型:财务应收单(awrp_src_bt)。 */ + public static final String BFI_AR_BILL_SRC_BT = "ar_finarbill"; + + /** A301-3 认领推送核心单据类型:其他应付(e_corebilltype)。 */ + public static final String BFI_CLAIM_CORE_BILL_TYPE = "ar_finarbill"; + + /** A303-1 发票种类:数电发票(普通发票)。 */ + public static final String BFI_INVOICE_TYPE_DIGITAL_GENERAL = "10xdp"; + + /** A303-1 发票种类:数电发票(增值税专用发票)。 */ + public static final String BFI_INVOICE_TYPE_DIGITAL_SPECIAL = "08xdp"; + + /** A303-1 特殊票种:货物运输(运费/货物运输类发票)。 */ + public static final String BFI_INVOICE_SPECIAL_TYPE_FREIGHT = "E04"; + + /** A303-1 特殊票种:非特殊票种(其余费用类发票,如其他、非货物运输的运输类)。 */ + public static final String BFI_INVOICE_SPECIAL_TYPE_NONE = "00"; + + /** 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"; + + /** 付款申请单(ap_payapply)单据类型映射:中文名称 -> 星瀚编码(使用规则待业务确认)。 */ + public static final Map PAY_APPLY_BILLTYPE_MAP = Map.of( + "采购付款申请", "ap_payapply_BT_S", + "其他付款申请", "ap_payapply_oth_BT_S" + ); + + /** 金蝶同步状态:未生成(新建单据默认)。 */ + public static final String KINGDEE_STATUS_UNSYNCED = "unsynced"; + + /** 金蝶同步状态:已生成(推送成功,等待付款)。 */ + public static final String KINGDEE_STATUS_SYNCED = "synced"; + + /** 金蝶同步状态:付款中(A103-2 paystatus = Inpayment 部分付款)。 */ + public static final String KINGDEE_STATUS_PAYING = "paying"; + + /** 金蝶同步状态:已付款(A103-2 paystatus = Alreadypay,终态)。 */ + public static final String KINGDEE_STATUS_PAID = "paid"; + + /** 金蝶同步状态:已关闭(A103-2 billstatus = D,终态)。 */ + public static final String KINGDEE_STATUS_CLOSED = "closed"; + + /** 金蝶同步状态:生成失败(推送异常/金蝶返回失败,可重推)。 */ + public static final String KINGDEE_STATUS_FAILED = "failed"; + + /** 收票单推送状态:未推送(新建单据默认,票池绑定层的 kingdee_status 与此无关)。 */ + public static final String RECEIVED_PUSH_UNSYNCED = "unsynced"; + + /** 收票单推送状态:金蝶收票单已保存(A303-2 成功,等待/已完成指定应付)。 */ + public static final String RECEIVED_PUSH_PUSHED = "pushed"; + + /** 收票单推送状态:已指定应付(A302-6 成功,一票一应付,终态;金蝶侧异常回收获占)。 */ + public static final String RECEIVED_PUSH_ASSIGNED = "assigned"; + + /** 收票单推送状态:推送失败(保存/指定异常或金蝶侧删除红冲,人工处理后重推)。 */ + public static final String RECEIVED_PUSH_FAILED = "failed"; + + /** A303-2 业务类型(PUR:采购 / FEE:费用,TMS 服务费进项票暂按采购)。待确认:取值需金蝶侧最终确认。 */ + public static final String BFI_RECEIVED_BIZ_TYPE = "PUR"; + + /** A303-2 必填:外部单据类型(收票单来源标识)。待确认:取值需金蝶侧确认,确认后如有调整直接改此常量。 */ + public static final String BFI_RECEIVED_INVOICE_SRC_TYPE = "TMS收票登记"; + + /** A103-2 付款状态:已付款。 */ + public static final String BFI_PAY_STATUS_ALREADY_PAY = "Alreadypay"; + + /** A103-2 付款状态:部分付款。 */ + public static final String BFI_PAY_STATUS_IN_PAYMENT = "Inpayment"; + + /** A103-2 付款状态:未付款。 */ + public static final String BFI_PAY_STATUS_UNPAID = "Unpaid"; + + /** A103-2 单据状态:已关闭。 */ + public static final String BFI_BILL_STATUS_CLOSED = "D"; + + /** A801-1 查询限定发票状态:0 正常(票易通字典),作废/异常票不进镜像、不可补查 */ + public static final String POOL_INVOICE_STATUS_NORMAL = "0"; + + /** A301-3 认领推送:收款类型.编码(示例 101,待金蝶确认后如有调整直接改此常量)。 */ + public static final String BFI_CLAIM_REC_PAY_TYPE = "999"; + + /** A301-3 认领推送:付款人类型(默认客户 bd_customer,待金蝶确认后如有调整直接改此常量)。 */ + public static final String BFI_CLAIM_PAYMENT_TYPE = "bd_customer"; + + /** 收款流水推送状态:未推送(未满足推送条件或尚未轮到推送)。 */ + public static final String RECEIPT_CLAIM_PUSH_NONE = "none"; + + /** 收款流水推送状态:已推送(终态闸门,不允许二次推送)。 */ + public static final String RECEIPT_CLAIM_PUSH_PUSHED = "pushed"; + + /** 收款流水推送状态:推送失败(本地认领保留,可重新推送,重试复用同一来源编码)。 */ + public static final String RECEIPT_CLAIM_PUSH_FAILED = "failed"; + + /** 收款流水金蝶回写状态:未推送(认领完成推送前的初始状态)。 */ + public static final String RECEIPT_CLAIM_BILL_NONE = "none"; + + /** 金蝶同步状态:认领结果已推送(A301-3 成功,等待金蝶收款单审核/收款,可继续回写)。 */ + public static final String KINGDEE_STATUS_PUSHED = "pushed"; + + /** 金蝶同步状态:收款单已收款(A301-1 billstatus = D,终态)。 */ + public static final String KINGDEE_STATUS_RECEIVED = "received"; + + /** 选票金额使用状态可选值:仅保留剩余可用金额>0 且金额使用状态为 0 的可用票,2-全部使用 */ + public static final String POOL_AMOUNT_USE_STATUS_SELECTABLE = "0"; + + /** 单请求远程补页调用上限:金额可用过滤在拉取后进行,过滤率过低时以此封顶防请求无限拉长 */ + public static final int POOL_MAX_REMOTE_CALLS = 3; + + /** 选票单据使用状态固定为未用(1,票易通字典):仅未使用的票可被认领/登记,前端不提供该筛选条件 */ + public static final String POOL_EXPENSE_STATUS_UNUSED = "1"; + + /** + * 按中文名称从映射字典转换单据类型编码。 + * + * @param billtypeMap 单据类型映射字典 + * @param name 单据类型中文名称 + * @return 星瀚单据类型编码,未维护时返回 null + */ + public static String billtypeBy(Map billtypeMap, String name) { + return billtypeMap.get(name); + } + + private KingdeeBfiConstant() { + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeLockConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeLockConstant.java new file mode 100644 index 00000000..6737bc3e --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeLockConstant.java @@ -0,0 +1,91 @@ +/** + * 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. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.constant; + +import java.util.Map; + +/** + * 金蝶单据锁定/解锁(lockBusiData / unLockNotifyBusiData)固定值。 + *

lockBusiData 由 WLTMS 调金蝶(源系统 WLTMS、目标系统 XH), + * unLockNotifyBusiData 由金蝶调 WLTMS(源系统 XH、目标系统 WLTMS); + * WLTMS 单据类型自定义,金蝶目标单据类型为固定值

+ * + * @author Chill + */ +public final class KingdeeLockConstant { + + /** 锁动作:锁定 */ + public static final String ACTION_LOCK = "LOCK"; + + /** 锁动作:解锁 */ + public static final String ACTION_UNLOCK = "UNLOCK"; + + /** 来源操作类型:单据新增 */ + public static final String OPERATION_SAVE = "SAVE"; + + /** 来源操作类型:单据删除(解锁回调携带 DELETE 时 WLTMS 废除源单并还原关联数据) */ + public static final String OPERATION_DELETE = "DELETE"; + + /** 系统标识:WLTMS(运输管理系统,即本系统) */ + public static final String SYSTEM_WLTMS = "WLTMS"; + + /** 系统标识:金蝶星瀚 XH */ + public static final String SYSTEM_XH = "XH"; + + /** 锁状态:锁定失败(补偿定时任务的重试对象) */ + public static final String STATUS_FAILED = "failed"; + + /** 锁状态:金蝶锁定中(源单冻结,不可编辑/作废) */ + public static final String STATUS_LOCKED = "locked"; + + /** 锁状态:已解锁(终态;同单重推生成新锁记录) */ + public static final String STATUS_UNLOCKED = "unlocked"; + + /** WLTMS 单据类型:正式结算单(应付/应收共用,按结算单 settlementType 区分金蝶目标类型) */ + public static final String SOURCE_SETTLEMENT = "SETTLEMENT"; + + /** WLTMS 单据类型:开票申请单 */ + public static final String SOURCE_INVOICE_APPLY = "INVOICE_APPLY"; + + /** WLTMS 单据类型:付款申请单(映射预留,本期不锁) */ + public static final String SOURCE_PAY_APPLY = "PAY_APPLY"; + + /** WLTMS 单据类型:收票单(映射预留,本期不锁) */ + public static final String SOURCE_RECEIVED_INVOICE = "RECEIVED_INVOICE"; + + /** 金蝶目标单据类型:财务应付单(A102-2,应付结算单推送后锁定) */ + public static final String TARGET_AP_BILL = "ApFin_other_BT_S"; + + /** 金蝶目标单据类型:财务应收单(A302-2,应收结算单推送后锁定) */ + public static final String TARGET_AR_BILL = "arfin_other_BT_S"; + + /** 金蝶目标单据类型:开票单(A303-1 开票申请单,按发票张次逐张一行锁记录) */ + public static final String TARGET_INVOICE_BILL = "cdm_paybill_bank_elect_BT_S"; + + /** 金蝶目标单据类型:付款申请单(映射预留,本期不锁) */ + public static final String TARGET_PAY_APPLY = "ap_payapply_oth_BT_S"; + + /** 金蝶目标单据类型:收票单(映射预留,本期不锁) */ + public static final String TARGET_RECEIVED_INVOICE = "cdm_recbill_bank_BT_S"; + + /** 锁定重试上限:补偿任务超过该次数不再自动重试,人工排查(失败原因见 fail_reason) */ + public static final int RETRY_MAX_COUNT = 5; + + /** 金蝶目标单据类型 -> WLTMS 单据类型反查映射(解锁回调按 targetBillType 分发废除还原用) */ + public static final Map TARGET_TO_SOURCE_MAP = Map.ofEntries( + Map.entry(TARGET_AP_BILL, SOURCE_SETTLEMENT), + Map.entry(TARGET_AR_BILL, SOURCE_SETTLEMENT), + Map.entry(TARGET_INVOICE_BILL, SOURCE_INVOICE_APPLY), + Map.entry(TARGET_PAY_APPLY, SOURCE_PAY_APPLY), + Map.entry(TARGET_RECEIVED_INVOICE, SOURCE_RECEIVED_INVOICE) + ); + + private KingdeeLockConstant() { + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/TransportBusiConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/TransportBusiConstant.java new file mode 100644 index 00000000..cf5e82b3 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/TransportBusiConstant.java @@ -0,0 +1,54 @@ +/** + * 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.constant; + +/** + * 运输业务固定值。 + * + * @author Chill + */ +public final class TransportBusiConstant { + + /** 付款类型:结算付款(关联正式结算单的正式付款)。 */ + public static final String PAYMENT_TYPE_SETTLEMENT = "settlement_payment"; + + /** 审批状态:草稿(允许编辑、删除)。 */ + public static final String DRAFT = "draft"; + + /** 审批状态:审批中(审核节点,允许通过/驳回)。 */ + public static final String REVIEWING = "reviewing"; + + /** 审批状态:审批通过。 */ + public static final String APPROVED = "approved"; + + /** 审批状态:已驳回(允许再编辑后重新提交)。 */ + public static final String RETURNED = "returned"; + + /** 审批状态:已作废(终态)。 */ + public static final String VOIDED = "voided"; + + private TransportBusiConstant() { + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiCustomerController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiCustomerController.java new file mode 100644 index 00000000..5faea7c8 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiCustomerController.java @@ -0,0 +1,75 @@ +/** + * 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.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiCustomerVO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO; +import org.springblade.thirdparty.bfi.service.IBfiService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 业财融合(BFI)客商查询控制器 + * + * @author Chill + */ +@RestController +@AllArgsConstructor +@RequestMapping("/bfi") +@Tag(name = "BFI客商", description = "业财融合客商查询接口") +public class BfiCustomerController extends BladeController { + + private final IBfiService bfiService; + + @GetMapping("/customer/query-by-credit-code") + @Operation(summary = "按统一社会信用代码查询金蝶客商", description = "新增客商时查询金蝶是否已有该客商,已审核则回填工商信息") + public R queryByCreditCode( + @Parameter(description = "统一社会信用代码", required = true) @RequestParam String creditCode) { + if (StringUtil.isBlank(creditCode)) { + return R.fail("统一社会信用代码不能为空"); + } + BfiCustomerQueryDTO param = new BfiCustomerQueryDTO(); + param.setPageNo(1); + param.setPageSize(1); + param.setSocietycreditcode(List.of(creditCode.trim().toUpperCase())); + BfiPageVO page = bfiService.queryCustomer(param); + List rows = page == null ? null : page.getRows(); + return R.data(rows == null || rows.isEmpty() ? null : rows.get(0)); + } + +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiExchangeRateController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiExchangeRateController.java new file mode 100644 index 00000000..ebf87635 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/BfiExchangeRateController.java @@ -0,0 +1,76 @@ +/** + * 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.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.StringUtil; +import org.springblade.thirdparty.bfi.pojo.vo.BfiExchangeRateVO; +import org.springblade.thirdparty.bfi.service.IBfiService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; +import java.util.List; + +/** + * 业财融合(BFI)汇率查询控制器 + * + * @author Chill + */ +@RestController +@AllArgsConstructor +@RequestMapping("/bfi") +@Tag(name = "BFI汇率", description = "业财融合汇率查询接口") +public class BfiExchangeRateController extends BladeController { + + private final IBfiService bfiService; + + @GetMapping("/exchange-rate") + @Operation(summary = "BFI汇率查询", description = "根据结算币种编码和汇率日期查询BFI汇率") + public R getBfiExchangeRate( + @RequestParam String currencyCode, + @RequestParam(required = false) String effectdate) { + LocalDate date = StringUtil.isBlank(effectdate) ? LocalDate.now() : LocalDate.parse(effectdate); + List rates = bfiService.queryExchangeRate(date, date); + if (rates == null || rates.isEmpty()) { + return R.fail("未查询到该日期的BFI汇率数据"); + } + BfiExchangeRateVO matched = rates.stream() + .filter(r -> currencyCode.equalsIgnoreCase(r.getOrgcurNumber())) + .findFirst() + .orElse(null); + if (matched == null) { + return R.fail("未查询到币种 " + currencyCode + " 的汇率数据"); + } + return R.data(matched); + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CustomerArchiveController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CustomerArchiveController.java index c111098f..d0998aa3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CustomerArchiveController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/CustomerArchiveController.java @@ -31,6 +31,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springblade.core.boot.ctrl.BladeController; import org.springblade.core.excel.util.ExcelUtil; import org.springblade.core.mp.support.Condition; @@ -61,6 +62,7 @@ import java.util.List; * * @author Chill */ +@Slf4j @RestController @AllArgsConstructor @PreAuth(menu = "customer_archive") @@ -134,13 +136,21 @@ public class CustomerArchiveController extends BladeController { } /** - * 审核通过 + * 审核通过(通过后自动推送金蝶,推送失败不影响审批结果) */ @PostMapping("/approve") @ApiOperationSupport(order = 6) @Operation(summary = "审核通过", description = "传入id") public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) { - return R.status(customerArchiveService.approve(id)); + boolean result = customerArchiveService.approve(id); + if (result) { + try { + customerArchiveService.syncKingdee(id); + } catch (Exception exception) { + log.error("客商审批通过后推送金蝶异常,id={}", id, exception); + } + } + return R.status(result); } /** @@ -174,6 +184,16 @@ public class CustomerArchiveController extends BladeController { return R.status(customerArchiveService.removeDraft(ids)); } + /** + * 推送金蝶(审批通过后失败重推入口) + */ + @PostMapping("/sync-kingdee") + @ApiOperationSupport(order = 12) + @Operation(summary = "推送金蝶", description = "传入id,仅审批通过的客商可推送,已推送/金蝶已有自动幂等跳过") + public R syncKingdee(@Parameter(description = "主键", required = true) @RequestParam Long id) { + return R.data(customerArchiveService.syncKingdee(id)); + } + /** * 评分明细模板 */ 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 49535ce5..01a53d18 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 @@ -22,17 +22,22 @@ import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; +import org.springblade.transport.listener.KingdeeApprovalSyncListener; 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.FormalSettlementStatusRequest; import org.springblade.transport.pojo.dto.FormalSettlementInvoiceClaimRequest; +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.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.IKingdeeBillLockService; import org.springblade.transport.service.IPreSettlementService; import org.springblade.transport.service.IReceiptFlowService; import org.springframework.web.bind.annotation.GetMapping; @@ -59,8 +64,10 @@ import java.math.RoundingMode; @Tag(name = "正式结算单", description = "正式结算单管理") public class FormalSettlementController extends BladeController { private final IFormalSettlementService formalSettlementService; + private final IKingdeeBillLockService kingdeeBillLockService; private final IPreSettlementService preSettlementService; private final IReceiptFlowService receiptFlowService; + private final IInvoiceReceiptService invoiceReceiptService; @GetMapping("/list") @ApiOperationSupport(order = 1) @@ -190,7 +197,19 @@ public class FormalSettlementController extends BladeController { @PostMapping("/approve") @ApiOperationSupport(order = 11) @Operation(summary = "审批通过") - public R approve(@RequestBody FormalSettlementStatusRequest request) { formalSettlementService.approve(request); return R.success("审批通过"); } + public R approve(@RequestBody FormalSettlementStatusRequest request) { + formalSettlementService.approve(request); + return approveResponse(); + } + + /** 审批接口统一响应:审批已生效,自动推送失败原因以警告文案返回(data=false 供前端区分提示样式) */ + private R approveResponse() { + String syncFailure = KingdeeApprovalSyncListener.pollSyncFailure(); + if (syncFailure != null) { + return R.data(Boolean.FALSE, "审批通过,但自动同步金蝶失败:" + syncFailure); + } + return R.success("审批通过"); + } @PostMapping("/return") @ApiOperationSupport(order = 12) @@ -204,11 +223,23 @@ public class FormalSettlementController extends BladeController { @PostMapping("/sync-kingdee") @ApiOperationSupport(order = 14) - @Operation(summary = "推送金蝶应付单") + @Operation(summary = "推送金蝶(应付单/应收单)") public R syncKingdee(@RequestParam Long id) { return R.data(formalSettlementService.syncKingdee(id)); } - @GetMapping("/detail-fees") + @PostMapping("/sync-kingdee-ar-result") @ApiOperationSupport(order = 15) + @Operation(summary = "手动勾稽金蝶应收单(A302-4 回写纠偏)") + public R syncKingdeeArResult() { return R.data(formalSettlementService.syncKingdeeArResult()); } + + @PostMapping("/retry-kingdee-bill-lock") + @ApiOperationSupport(order = 17) + @Operation(summary = "手动补偿金蝶单据锁定(扫锁定失败锁记录重调lockBusiData,应付/应收/开票通用)") + public R retryKingdeeBillLock() { + return R.data(kingdeeBillLockService.retryFailedLocks()); + } + + @GetMapping("/detail-fees") + @ApiOperationSupport(order = 16) @Operation(summary = "正式结算货物费用快照") public R> detailFees(@RequestParam Long detailId) { return R.data(formalSettlementService.detailFees(detailId)); @@ -250,4 +281,11 @@ public class FormalSettlementController extends BladeController { public R>> receiptClaims(@RequestParam Long formalSettlementId) { return R.data(receiptFlowService.settlementClaims(formalSettlementId)); } + + @GetMapping("/invoice-pool") + @ApiOperationSupport(order = 21) + @Operation(summary = "金蝶票据池镜像分页查询(认领查询)") + public R> invoicePool(KingdeeInvoicePoolQueryDTO query) { + return R.data(invoiceReceiptService.invoicePoolPage(query)); + } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceApplicationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceApplicationController.java index c3ac9cc3..6d2ab648 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceApplicationController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/InvoiceApplicationController.java @@ -35,6 +35,7 @@ import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; +import org.springblade.transport.listener.KingdeeApprovalSyncListener; import org.springblade.transport.pojo.dto.InvoiceApplicationSaveRequest; import org.springblade.transport.pojo.dto.InvoiceApplicationStatusRequest; import org.springblade.transport.pojo.entity.FormalSettlementDetail; @@ -131,6 +132,15 @@ public class InvoiceApplicationController extends BladeController { @Operation(summary = "审批通过开票申请") public R approve(@RequestBody InvoiceApplicationStatusRequest request) { invoiceApplicationService.approve(request); + return approveResponse(); + } + + /** 审批接口统一响应:审批已生效,自动推送失败原因以警告文案返回(data=false 供前端区分提示样式) */ + private R approveResponse() { + String syncFailure = KingdeeApprovalSyncListener.pollSyncFailure(); + if (syncFailure != null) { + return R.data(Boolean.FALSE, "审批通过,但自动同步金蝶失败:" + syncFailure); + } return R.success("审批通过"); } @@ -156,4 +166,11 @@ public class InvoiceApplicationController extends BladeController { public R syncKingdee(@RequestParam Long id) { return R.data(invoiceApplicationService.syncKingdee(id)); } + + @PostMapping("/sync-kingdee-invoice-result") + @ApiOperationSupport(order = 13) + @Operation(summary = "手动回写金蝶开票结果(A303-5)") + public R syncKingdeeInvoiceResult() { + return R.data(invoiceApplicationService.syncKingdeeInvoiceResult()); + } } 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 1a01d3c0..59eb30b6 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 @@ -29,16 +29,19 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import org.springblade.core.boot.ctrl.BladeController; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; +import org.springblade.transport.listener.KingdeeApprovalSyncListener; 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 +83,24 @@ 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("/sync-invoice-pool") + @ApiOperationSupport(order = 13) + @Operation(summary = "手动触发金蝶全票池发票同步(窗口拉取+镜像全列upsert)") + public R syncInvoicePool() { + return R.data(invoiceReceiptService.syncKingdeeInvoicePool()); + } + + @GetMapping("/invoice-pool/attachment/download") + @ApiOperationSupport(order = 17) + @Operation(summary = "金蝶票池发票文件下载(A801-2 流式回源)", + description = "按发票号定位金蝶侧文件:本地镜像优先,镜像未落库实时回源 A801-1;成功返回文件流,失败返回 JSON 错误") + public void downloadPoolAttachment(@RequestParam String invoiceNo, HttpServletResponse response) { + invoiceReceiptService.downloadPoolAttachment(invoiceNo, response); } @GetMapping("/settlement-candidates") @@ -129,6 +147,15 @@ public class InvoiceReceiptController extends BladeController { @Operation(summary = "审批通过收票登记") public R approve(@RequestBody InvoiceReceiptStatusRequest request) { invoiceReceiptService.approve(request); + return approveResponse(); + } + + /** 审批接口统一响应:审批已生效,自动推送失败原因以警告文案返回(data=false 供前端区分提示样式) */ + private R approveResponse() { + String syncFailure = KingdeeApprovalSyncListener.pollSyncFailure(); + if (syncFailure != null) { + return R.data(Boolean.FALSE, "审批通过,但自动同步金蝶失败:" + syncFailure); + } return R.success("审批通过"); } @@ -148,10 +175,17 @@ public class InvoiceReceiptController extends BladeController { return R.success("作废成功"); } - @PostMapping("/sync-kingdee") - @ApiOperationSupport(order = 12) - @Operation(summary = "同步金蝶发票状态") - public R syncKingdee(@RequestParam Long id) { - return R.data(invoiceReceiptService.syncKingdee(id)); + @PostMapping("/push-kingdee") + @ApiOperationSupport(order = 15) + @Operation(summary = "批量推送金蝶收票单(A303-2 收票单保存 + A302-6 收票指定应付)") + public R pushKingdee(@RequestBody List ids) { + return R.data(invoiceReceiptService.pushKingdeeReceivedInvoice(ids)); + } + + @PostMapping("/sync-kingdee-result") + @ApiOperationSupport(order = 16) + @Operation(summary = "手动触发金蝶收票单结果回写(A801-3 异常回收/指定补偿)") + public R syncKingdeeResult() { + return R.data(invoiceReceiptService.syncKingdeeReceivedInvoiceResult()); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/KingdeeBfiPublicController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/KingdeeBfiPublicController.java new file mode 100644 index 00000000..ade6c501 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/KingdeeBfiPublicController.java @@ -0,0 +1,65 @@ +/** + * 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. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.controller; + +import com.alibaba.fastjson2.JSON; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tenant.annotation.TenantIgnore; +import org.springblade.thirdparty.bfi.pojo.dto.BfiBillLockDTO; +import org.springblade.thirdparty.bfi.pojo.vo.BfiBillLockResultVO; +import org.springblade.transport.service.IKingdeeBillLockService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 金蝶单据锁定回调 控制器 + *

+ * unLockNotifyBusiData:金蝶(星瀚)侧解锁/删单后回调 WLTMS 送结果, + * 无登录态公开端点(模式同 CustomerArchivePublicController); + * operationType=DELETE 时 WLTMS 废除源单并还原关联数据。 + * 应答与 lockBusiData 出参同构:{updateCount, updateResultList},逐单处理(1-成功,0-未匹配/失败) + * + * @author Chill + */ +@Slf4j +@RestController +@AllArgsConstructor +@TenantIgnore +// @PreAuth(AuthConstant.PERMIT_ALL) +@RequestMapping("kingdee-bfi") +@Tag(name = "金蝶单据锁定回调", description = "金蝶星瀚调用:单据解锁/删单通知") +public class KingdeeBfiPublicController { + + private final IKingdeeBillLockService kingdeeBillLockService; + + /** + * 单据解锁通知(unLockNotifyBusiData):多张单据编号逗号拼接批量回调,逐张处理 + * + * @param param 金蝶回调参数 + * @return 处理结果,与 lockBusiData 出参同构 + */ + @PostMapping("unLockNotifyBusiData") + @Operation(summary = "单据解锁通知", description = "金蝶侧解锁/删单后回调;operationType=DELETE 时废除源单并还原关联数据") + public BfiBillLockResultVO unLockNotifyBusiData(@RequestBody BfiBillLockDTO param) { + log.info("金蝶单据解锁回调:{}", JSON.toJSONString(param)); + try { + return BfiBillLockResultVO.of(kingdeeBillLockService.onUnlockNotify(param)); + } catch (Exception exception) { + log.error("金蝶单据解锁回调处理失败:lockAction={},operationType={},targetBillType={},targetBillNo={}", + param == null ? null : param.getLockAction(), param == null ? null : param.getOperationType(), + param == null ? null : param.getTargetBillType(), param == null ? null : param.getTargetBillNo(), exception); + return BfiBillLockResultVO.of(0); + } + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java index e2c6aafe..92a7f5a2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java @@ -29,6 +29,7 @@ import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; +import org.springblade.transport.listener.KingdeeApprovalSyncListener; import org.springblade.transport.pojo.dto.PaymentApplicationSaveRequest; import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest; import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO; @@ -81,7 +82,19 @@ public class PaymentApplicationController extends BladeController { @PostMapping("/approve") @ApiOperationSupport(order = 7) @Operation(summary = "审批通过付款申请") - public R approve(@RequestBody PaymentApplicationStatusRequest request) { paymentApplicationService.approve(request); return R.success("审批通过"); } + public R approve(@RequestBody PaymentApplicationStatusRequest request) { + paymentApplicationService.approve(request); + return approveResponse(); + } + + /** 审批接口统一响应:审批已生效,自动推送失败原因以警告文案返回(data=false 供前端区分提示样式) */ + private R approveResponse() { + String syncFailure = KingdeeApprovalSyncListener.pollSyncFailure(); + if (syncFailure != null) { + return R.data(Boolean.FALSE, "审批通过,但自动同步金蝶失败:" + syncFailure); + } + return R.success("审批通过"); + } @PostMapping("/return") @ApiOperationSupport(order = 8) @Operation(summary = "驳回付款申请") @@ -100,4 +113,8 @@ public class PaymentApplicationController extends BladeController { public R> syncKingdeeBatch(@RequestBody List ids) { return R.data(paymentApplicationService.syncKingdeeBatch(ids)); } + @PostMapping("/sync-kingdee-result") + @ApiOperationSupport(order = 12) + @Operation(summary = "手动回写金蝶付款结果(A103-2/A103-4)") + public R syncKingdeeResult() { return R.data(paymentApplicationService.syncKingdeePaymentResult()); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptClaimRecordController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptClaimRecordController.java index 7cfd3f85..3db05ad5 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptClaimRecordController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptClaimRecordController.java @@ -84,7 +84,15 @@ public class ReceiptClaimRecordController extends BladeController { @PostMapping("/void") @ApiOperationSupport(order = 4) @Operation(summary = "作废认领记录并生成金蝶认领冲单") - public R voidClaim(@RequestParam Long id) { - return R.data(receiptClaimRecordService.voidClaim(id)); + public R voidClaim(@RequestParam Long id) { + receiptClaimRecordService.voidClaim(id); + return R.success("作废成功"); + } + + @PostMapping("/query-kingdee-result") + @ApiOperationSupport(order = 5) + @Operation(summary = "查询金蝶认领结果(A301-1 收款单查询回写)") + public R queryKingdeeResult() { + return R.data(receiptClaimRecordService.refreshKingdeeResult()); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptFlowController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptFlowController.java index 4aa3f02b..1bbee361 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptFlowController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceiptFlowController.java @@ -35,8 +35,8 @@ import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.secure.annotation.PreAuth; import org.springblade.core.tool.api.R; +import org.springblade.transport.pojo.dto.ReceiptClaimPushResult; import org.springblade.transport.pojo.dto.ReceiptClaimRequest; -import org.springblade.transport.pojo.dto.ReceiptFlowSyncRequest; import org.springblade.transport.pojo.vo.ReceiptFlowVO; import org.springblade.transport.service.IReceiptFlowService; import org.springframework.web.bind.annotation.GetMapping; @@ -95,8 +95,16 @@ public class ReceiptFlowController extends BladeController { @PostMapping("/sync") @ApiOperationSupport(order = 5) - @Operation(summary = "手动同步金蝶收款流水") - public R sync(@RequestBody(required = false) ReceiptFlowSyncRequest request) { - return R.data(receiptFlowService.sync(request)); + @Operation(summary = "手动同步金蝶收款流水(A301-2 收款认领公告查询)") + public R sync() { + return R.data(receiptFlowService.sync()); + } + + @PostMapping("/push-kingdee") + @ApiOperationSupport(order = 6) + @Operation(summary = "推送金蝶收款认领(A301-3 收款认领结果推送,仅认领完成流水可推送)") + public R pushKingdee(@RequestParam Long flowId) { + ReceiptClaimPushResult result = receiptFlowService.pushClaimToKingdee(flowId); + return result.isSuccess() ? R.data(result) : R.fail(result.getMessage()); } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceivablePayableDetailController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceivablePayableDetailController.java index 9790e3ee..c8cd804a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceivablePayableDetailController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ReceivablePayableDetailController.java @@ -133,13 +133,15 @@ public class ReceivablePayableDetailController extends BladeController { @ApiOperationSupport(order = 8) @Operation(summary = "转结算候选明细") public R>> transferCandidates(Query query, + @RequestParam(required = false) String documentNo, + @RequestParam(required = false) String contractNo, @RequestParam(required = false) String contractName, @RequestParam(required = false) String batchNo, @RequestParam(required = false) String generateStartDate, @RequestParam(required = false) String generateEndDate, @RequestParam(required = false) String settlementBillType, @RequestParam(required = false) String settlementType) { - return R.data(detailService.transferCandidates(Condition.getPage(query), contractName, batchNo, + return R.data(detailService.transferCandidates(Condition.getPage(query), documentNo, contractNo, contractName, batchNo, generateStartDate, generateEndDate, settlementBillType, settlementType)); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java new file mode 100644 index 00000000..9652742f --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/event/KingdeeBillApprovedEvent.java @@ -0,0 +1,44 @@ +/** + * 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.event; + +/** + * 单据审批通过事件:审批事务提交后由 {@code KingdeeApprovalSyncListener} 消费, + * 自动推送对应单据至金蝶(星瀚),省掉页面手动点「同步金蝶」。 + *

+ * bizType 复用 MkProcessBizHandlers 既有取值;只在审批状态真正变为 approved 的那次发布, + * 重复回调被 approve 状态校验拒绝,天然不重复推送。 + * + * @author Chill + */ +public record KingdeeBillApprovedEvent(String bizType, Long id) { + + /** 正式结算单(应付 A102-2 / 应收 A302-2) */ + public static final String BIZ_FORMAL_SETTLEMENT = "formal-settlement"; + + /** 付款申请(A103-1 正式 / A103-3 预付) */ + public static final String BIZ_PAYMENT_APPLICATION = "payment-application"; + + /** 开票申请(A303-1) */ + public static final String BIZ_INVOICE_APPLICATION = "invoice-application"; + + /** 收票登记(A303-2 + A302-6) */ + public static final String BIZ_INVOICE_RECEIPT = "invoice-receipt"; +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java index 6632ef44..ebe4eec2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/excel/CustomerArchiveExcel.java @@ -111,4 +111,7 @@ public class CustomerArchiveExcel implements Serializable { @ExcelProperty("审核通过时间") private LocalDateTime approvedTime; + @ExcelProperty("金蝶同步状态") + private String kingdeeStatusName; + } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java new file mode 100644 index 00000000..185a0c56 --- /dev/null +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/listener/KingdeeApprovalSyncListener.java @@ -0,0 +1,140 @@ +/** + * 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.listener; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.transport.config.BfiProperties; +import org.springblade.transport.event.KingdeeBillApprovedEvent; +import org.springblade.transport.service.IFormalSettlementService; +import org.springblade.transport.service.IInvoiceApplicationService; +import org.springblade.transport.service.IInvoiceReceiptService; +import org.springblade.transport.service.IPaymentApplicationService; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.util.List; + +/** + * 单据审批通过自动同步金蝶监听器:审批事务提交(AFTER_COMMIT)后复用手动按钮同一 Service 方法推送, + * 与客商档案审批后自动推送同线程模式。 + *

+ * 不变式:审批结果不受推送影响——监听器整体 try/catch 吞异常,推送异常只落 failed + 日志; + * 失败单据由 {@code KingdeeAutoSyncRetryTask} 定时重推、手动按钮兜底。 + *

+ * 推送失败原因通过 {@link #pollSyncFailure()} 传递给审批接口响应:AFTER_COMMIT 监听与 approve + * 同线程同请求,Controller 在 approve 返回后取走原因拼进提示文案;OA 回调路径无人取走, + * 由监听入口清空兜底,不会串单。 + *

+ * ponytail: 同步推送占用审批请求线程,若 BFI 网关大面积变慢导致审批接口可感知变慢, + * 再把路由调用提交到守护线程池(MkProcessMessageService 已有同款裸 ScheduledExecutorService 模式)。 + *

+ * 域开关(Nacos thirdParty.bfi.autoSync.*)默认全部关闭,与既有定时任务开关风格一致; + * 关闭某域 = 该域回到现状(审批不自动推送、手动按钮照用)。 + * + * @author Chill + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class KingdeeApprovalSyncListener { + + private final IFormalSettlementService formalSettlementService; + private final IPaymentApplicationService paymentApplicationService; + private final IInvoiceApplicationService invoiceApplicationService; + private final IInvoiceReceiptService invoiceReceiptService; + private final BfiProperties bfiProperties; + + /** 当前审批请求线程的自动推送失败原因,供 Controller 取走提示前端(见类注释线程说明) */ + private static final ThreadLocal SYNC_FAILURE = new ThreadLocal<>(); + + /** + * 取走并清除当前审批请求的自动推送失败原因;null 表示推送成功或未触发推送。 + * 须在 service.approve 返回后立即调用(监听器在审批事务提交时同步执行完毕)。 + */ + public static String pollSyncFailure() { + String failure = SYNC_FAILURE.get(); + SYNC_FAILURE.remove(); + return failure; + } + + /** + * 事务提交后同步推送。fallbackExecution=true 兜底:approve 未开启事务时事件不再被默认丢弃。 + * + * @param event 审批通过事件(bizType + 单据id) + */ + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true) + public void onBillApproved(KingdeeBillApprovedEvent event) { + SYNC_FAILURE.remove(); + try { + switch (event.bizType()) { + case KingdeeBillApprovedEvent.BIZ_FORMAL_SETTLEMENT -> pushSettlement(event.id()); + case KingdeeBillApprovedEvent.BIZ_PAYMENT_APPLICATION -> pushPayment(event.id()); + case KingdeeBillApprovedEvent.BIZ_INVOICE_APPLICATION -> pushInvoiceApply(event.id()); + case KingdeeBillApprovedEvent.BIZ_INVOICE_RECEIPT -> pushReceipt(event.id()); + default -> log.warn("审批通过自动同步金蝶跳过:未知业务类型 bizType={} id={}", event.bizType(), event.id()); + } + } catch (Exception exception) { + // 推送失败只记日志(失败状态已在推送方法内独立事务落库),不影响审批结果,交给重推任务与手动按钮兜底; + // 原因暂存线程变量,由审批 Controller 取走提示前端 + log.error("审批通过自动同步金蝶失败 bizType={} id={}", event.bizType(), event.id(), exception); + String reason = exception.getMessage() == null ? "金蝶接口调用异常" : exception.getMessage(); + // 手动按钮同文案已带"推送金蝶失败:"前缀,拼提示时去掉避免重复 + SYNC_FAILURE.set(reason.startsWith("推送金蝶失败:") ? reason.substring("推送金蝶失败:".length()) : reason); + } + } + + private void pushSettlement(Long id) { + if (!bfiProperties.getAutoSync().isSettlement()) { + return; + } + log.info("正式结算单[{}]审批通过自动同步金蝶开始", id); + String billNo = formalSettlementService.syncKingdee(id); + log.info("正式结算单[{}]审批通过自动同步金蝶完成:billNo={}", id, billNo); + } + + private void pushPayment(Long id) { + if (!bfiProperties.getAutoSync().isPayment()) { + return; + } + log.info("付款申请[{}]审批通过自动同步金蝶开始", id); + String kingdeeBillId = paymentApplicationService.syncKingdee(id); + log.info("付款申请[{}]审批通过自动同步金蝶完成:kingdeeBillId={}", id, kingdeeBillId); + } + + private void pushInvoiceApply(Long id) { + if (!bfiProperties.getAutoSync().isInvoiceApply()) { + return; + } + log.info("开票申请[{}]审批通过自动同步金蝶开始", id); + String billNo = invoiceApplicationService.syncKingdee(id); + log.info("开票申请[{}]审批通过自动同步金蝶完成:billNo={}", id, billNo); + } + + private void pushReceipt(Long id) { + if (!bfiProperties.getAutoSync().isReceipt()) { + return; + } + log.info("收票登记[{}]审批通过自动推送金蝶收票单开始", id); + String summary = invoiceReceiptService.pushKingdeeReceivedInvoice(List.of(id), true); + log.info("收票登记[{}]审批通过自动推送金蝶收票单完成:{}", id, summary); + } +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml index 6da293f2..31f15b31 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/CustomerArchiveMapper.xml @@ -48,6 +48,9 @@ + + + + SELECT + + FROM blade_kingdee_invoice_pool p + WHERE p.is_deleted = 0 AND p.status = 1 AND p.tenant_id = #{tenantId} + + + AND p.invoice_no LIKE CONCAT('%', #{query.invoiceNo}, '%') + + + AND p.issuer_name LIKE CONCAT('%', #{query.salerName}, '%') + + + AND p.receiver_name LIKE CONCAT('%', #{query.buyerName}, '%') + + + AND p.invoice_type_code = #{query.invoiceType} + + + AND p.saler_tax_no = #{query.salerTaxNo} + + + AND p.buyer_tax_no = #{query.buyerTaxNo} + + + AND p.invoice_date >= #{query.invoiceDateStart} + + + AND p.invoice_date <= #{query.invoiceDateEnd} + + + AND + + + AND NOT + + + AND + + + AND NOT + + ORDER BY p.source_updated_time DESC, p.create_time DESC + + diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/ReceiptClaimMapper.xml b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/ReceiptClaimMapper.xml index 07a25531..b1337bbe 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/ReceiptClaimMapper.xml +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/mapper/ReceiptClaimMapper.xml @@ -36,6 +36,7 @@ f.summary, f.transaction_time, f.detail_serial_no, + f.push_status, (SELECT GROUP_CONCAT(rcs.formal_settlement_no ORDER BY rcs.id SEPARATOR ',') FROM blade_receipt_claim_settlement rcs WHERE rcs.receipt_claim_id = c.id AND rcs.is_deleted = 0) AS associated_settlement_nos @@ -91,6 +92,9 @@ AND c.kingdee_bill_status = #{query.kingdeeBillStatus} + + AND f.push_status = #{query.pushStatus} +