From 809b4eb63ad47d58dc65e991f6d9d0994989bad3 Mon Sep 17 00:00:00 2001 From: b2894lxlx <517289602@qq.com> Date: Thu, 3 Sep 2026 22:40:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=9F=BA=E7=A1=80=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E3=80=81=E8=BD=A6=E8=88=B9=E5=8A=A1=E3=80=81=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E3=80=81=E5=90=88=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/pojo/entity/FeeItem.java | 6 + .../transport/pojo/entity/ContractManage.java | 8 +- .../system/excel/FeeItemExcel.java | 4 + .../system/excel/FeeItemExportExcel.java | 4 + .../excel/FeeItemImportFailureExcel.java | 4 + .../system/mapper/FeeItemMapper.xml | 1 + .../service/impl/FeeItemServiceImpl.java | 17 +- .../controller/ContractManageController.java | 2 +- .../impl/ContractManageServiceImpl.java | 203 +++++++++++++++++- .../service/impl/ProjectApplyServiceImpl.java | 3 + .../ReceivablePayableDetailServiceImpl.java | 34 +-- doc/sql/bladex/bladex.dameng.all.create.sql | 1 + doc/sql/bladex/bladex.kingbase.all.create.sql | 1 + doc/sql/bladex/bladex.mysql.all.create.sql | 1 + doc/sql/bladex/bladex.oracle.all.create.sql | 1 + doc/sql/bladex/bladex.postgres.all.create.sql | 1 + .../bladex/bladex.sqlserver.all.create.sql | 1 + doc/sql/bladex/bladex.yashan.all.create.sql | 1 + ..._manage_billing_plan_tax_rate_20260903.sql | 5 + ...act_manage_change_attachments_20260903.sql | 3 + doc/sql/transport/blade_fee_item.sql | 1 + .../blade_fee_item_tax_rate_20260903.sql | 5 + .../blade_project_contract_management.sql | 3 +- 23 files changed, 279 insertions(+), 31 deletions(-) create mode 100644 doc/sql/transport/blade_contract_manage_billing_plan_tax_rate_20260903.sql create mode 100644 doc/sql/transport/blade_contract_manage_change_attachments_20260903.sql create mode 100644 doc/sql/transport/blade_fee_item_tax_rate_20260903.sql diff --git a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/FeeItem.java b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/FeeItem.java index 8b8e68c..cd371d5 100644 --- a/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/FeeItem.java +++ b/blade-service-api/blade-system-api/src/main/java/org/springblade/system/pojo/entity/FeeItem.java @@ -34,6 +34,7 @@ import lombok.EqualsAndHashCode; import org.springblade.core.mp.base.BaseEntity; import java.io.Serial; +import java.math.BigDecimal; /** * 费用项实体类 @@ -64,6 +65,11 @@ public class FeeItem extends BaseEntity { */ @Schema(description = "费用项代码") private String englishName; + /** + * 税率(百分比) + */ + @Schema(description = "税率(百分比)") + private BigDecimal taxRate; /** * 备注 */ diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ContractManage.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ContractManage.java index 6b5755e..f703836 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ContractManage.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/ContractManage.java @@ -150,7 +150,7 @@ public class ContractManage extends TenantEntity { @Schema(description = "其它附件JSON") private String attachmentsJson; - @Schema(description = "计费方案JSON") + @Schema(description = "计费方案JSON(含规则税率)") private String billingPlanJson; @Schema(description = "结算生成规则JSON") @@ -172,11 +172,17 @@ public class ContractManage extends TenantEntity { private String changeRecordJson; @Schema(description = "变更内容") + @TableField(updateStrategy = FieldStrategy.ALWAYS) private String changeContent; @Schema(description = "变更原因") + @TableField(updateStrategy = FieldStrategy.ALWAYS) private String changeReason; + @Schema(description = "待审批变更附件JSON") + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String changeAttachmentsJson; + @Schema(description = "终止原因") private String terminateReason; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExcel.java index a1a8cfc..3048164 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExcel.java @@ -34,6 +34,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; +import java.math.BigDecimal; /** * 费用项 Excel @@ -60,6 +61,9 @@ public class FeeItemExcel implements Serializable { @ExcelProperty("*费用项") private String name; + @ExcelProperty("*税率") + private BigDecimal taxRate; + @ExcelIgnore private String errorMessage; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExportExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExportExcel.java index a87a46b..aedffeb 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExportExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemExportExcel.java @@ -15,6 +15,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; +import java.math.BigDecimal; import java.util.Date; /** @@ -40,6 +41,9 @@ public class FeeItemExportExcel implements Serializable { @ExcelProperty("费用项") private String name; + @ExcelProperty("税率") + private BigDecimal taxRate; + @ExcelProperty("备注") private String remark; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemImportFailureExcel.java b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemImportFailureExcel.java index d485b64..445bbd7 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemImportFailureExcel.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/excel/FeeItemImportFailureExcel.java @@ -33,6 +33,7 @@ import lombok.Data; import java.io.Serial; import java.io.Serializable; +import java.math.BigDecimal; /** * 费用项导入失败 Excel @@ -56,6 +57,9 @@ public class FeeItemImportFailureExcel implements Serializable { @ExcelProperty("*费用项") private String name; + @ExcelProperty("*税率") + private BigDecimal taxRate; + @ExcelProperty("导入失败原因") private String failureReason; diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/FeeItemMapper.xml b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/FeeItemMapper.xml index 698956a..b9bf3d9 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/mapper/FeeItemMapper.xml +++ b/blade-service/blade-system/src/main/java/org/springblade/system/mapper/FeeItemMapper.xml @@ -16,6 +16,7 @@ + diff --git a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java index 86125e3..a3825a4 100644 --- a/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java +++ b/blade-service/blade-system/src/main/java/org/springblade/system/service/impl/FeeItemServiceImpl.java @@ -47,6 +47,7 @@ import org.springblade.system.service.IFeeItemService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -66,6 +67,8 @@ public class FeeItemServiceImpl extends BaseServiceImpl private static final int NAME_MAX_LENGTH = 50; private static final int ENGLISH_NAME_MAX_LENGTH = 100; private static final int REMARK_MAX_LENGTH = 200; + private static final BigDecimal TAX_RATE_MIN = BigDecimal.ZERO; + private static final BigDecimal TAX_RATE_MAX = new BigDecimal("100"); @Override public IPage selectFeeItemPage(IPage page, FeeItemVO feeItem) { @@ -115,7 +118,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl save(feeItem); } catch (Exception exception) { String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败"; - failureList.add(toImportFailureExcel(excel, message)); + failureList.add(toImportFailureExcel(excel, "第" + (index + 2) + "行:" + message)); } } return failureList; @@ -156,6 +159,16 @@ public class FeeItemServiceImpl extends BaseServiceImpl if (Func.isNotEmpty(feeItem.getEnglishName()) && feeItem.getEnglishName().length() > ENGLISH_NAME_MAX_LENGTH) { throw new ServiceException("费用项代码不能超过100字"); } + BigDecimal taxRate = feeItem.getTaxRate(); + if (taxRate == null) { + throw new ServiceException("税率不能为空"); + } + if (taxRate.compareTo(TAX_RATE_MIN) < 0 || taxRate.compareTo(TAX_RATE_MAX) > 0) { + throw new ServiceException("税率必须在0到100之间"); + } + if (taxRate.stripTrailingZeros().scale() > 2) { + throw new ServiceException("税率最多保留2位小数"); + } if (Func.isNotEmpty(feeItem.getRemark()) && feeItem.getRemark().length() > REMARK_MAX_LENGTH) { throw new ServiceException("备注不能超过200个字"); } @@ -192,6 +205,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl feeItem.setFeeCategory(resolveFeeCategory(excel.getFeeCategory())); feeItem.setEnglishName(trimToNull(excel.getEnglishName())); feeItem.setName(trimToEmpty(excel.getName())); + feeItem.setTaxRate(excel.getTaxRate()); feeItem.setStatus(STATUS_ENABLED); return feeItem; } @@ -212,6 +226,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl failureExcel.setFeeCategory(excel.getFeeCategory()); failureExcel.setEnglishName(excel.getEnglishName()); failureExcel.setName(excel.getName()); + failureExcel.setTaxRate(excel.getTaxRate()); failureExcel.setFailureReason(failureReason); return failureExcel; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java index 412d674..92a11df 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java @@ -131,7 +131,7 @@ public class ContractManageController extends BladeController { @ApiOperationSupport(order = 10) @Operation(summary = "发起变更", description = "传入id、changeContent和changeReason") public R startChange(@Parameter(description = "主键", required = true) @RequestParam Long id, - @RequestParam String changeContent, + @RequestParam(required = false) String changeContent, @RequestParam String changeReason) { return R.status(contractManageService.startChange(id, changeContent, changeReason)); } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java index f572c84..1c37acb 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java @@ -42,16 +42,18 @@ import org.springblade.transport.wrapper.ContractManageWrapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * 合同管理 服务实现类 @@ -72,6 +74,9 @@ public class ContractManageServiceImpl extends BaseServiceImpl selectContractManagePage(IPage page, ContractManageVO contractManage) { @@ -93,7 +98,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl beforeData = buildContractChangeSnapshot(source); source.setContractName(request.getContractName()); source.setPartyB(request.getPartyB()); source.setStartDate(request.getStartDate()); source.setEndDate(request.getEndDate()); source.setContractFormat(request.getContractFormat()); source.setSettlementMode(request.getSettlementMode()); source.setLegalSealFlag(request.getLegalSealFlag()); source.setCopyCount(request.getCopyCount()); @@ -237,8 +271,16 @@ public class ContractManageServiceImpl extends BaseServiceImpl afterData = buildContractChangeSnapshot(source); + retainChangedSnapshotFields(beforeData, afterData); + source.setApprovalStatus(STATUS_CHANGE_REVIEWING); + source.setChangeContent(TransportBusinessSupport.trimToNull(request.getChangeContent())); + source.setChangeReason(changeReason); + source.setCurrentNode("合同变更审批"); + source.setCurrentProcessor("待处理"); + appendChangeRecord(source, "合同信息变更", changeReason, STATUS_CHANGE_REVIEWING, "审核中", beforeData, afterData); + return updateById(source); } @Override @@ -299,6 +341,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl plan) { + if (!(plan.get("rules") instanceof List rules)) return; + for (int index = 0; index < rules.size(); index++) { + Object ruleValue = rules.get(index); + if (!(ruleValue instanceof Map rule)) continue; + Object value = rule.get("taxRate"); + if (value == null || String.valueOf(value).isBlank()) { + throw new ServiceException("计费方案第" + (index + 1) + "行税率不能为空"); + } + String taxRateText = String.valueOf(value).trim(); + if (!taxRateText.matches("\\d+(\\.\\d{1,2})?")) { + throw new ServiceException("计费方案第" + (index + 1) + "行税率格式不正确"); + } + BigDecimal taxRate; + try { + taxRate = new BigDecimal(taxRateText); + } catch (NumberFormatException exception) { + throw new ServiceException("计费方案第" + (index + 1) + "行税率格式不正确"); + } + if (taxRate.compareTo(TAX_RATE_MIN) < 0 || taxRate.compareTo(TAX_RATE_MAX) > 0) { + throw new ServiceException("计费方案第" + (index + 1) + "行税率必须在0到100之间"); + } + if (taxRate.stripTrailingZeros().scale() > 2) { + throw new ServiceException("计费方案第" + (index + 1) + "行税率最多保留2位小数"); + } + } + } + private boolean hasConfiguredValue(Object value) { if (value instanceof Collection values) { return values.stream().anyMatch(this::hasConfiguredValue); @@ -597,6 +669,29 @@ public class ContractManageServiceImpl extends BaseServiceImpl beforeData, Map afterData) { List> records = parseChangeRecords(contractManage.getChangeRecordJson()); Map record = new LinkedHashMap<>(); record.put("changeDate", LocalDate.now().toString()); record.put("handlerUserId", AuthUtil.getUserId()); record.put("handlerUserName", AuthUtil.getUserName()); record.put("changeType", changeType); + record.put("changeContent", TransportBusinessSupport.trimToNull(contractManage.getChangeContent())); record.put("changeReason", TransportBusinessSupport.trimToNull(changeReason)); record.put("status", status); record.put("statusName", statusName); + if (beforeData != null && afterData != null) { + record.put("beforeData", beforeData); + record.put("afterData", afterData); + } records.add(record); contractManage.setChangeRecordJson(JsonUtil.toJson(records)); } + private Map buildContractChangeSnapshot(ContractManage contractManage) { + Map data = new LinkedHashMap<>(); + data.put("contractName", contractManage.getContractName()); + data.put("partyB", contractManage.getPartyB()); + data.put("startDate", contractManage.getStartDate()); + data.put("endDate", contractManage.getEndDate()); + data.put("contractFormat", contractManage.getContractFormat()); + data.put("settlementMode", contractManage.getSettlementMode()); + data.put("legalSealFlag", contractManage.getLegalSealFlag()); + data.put("copyCount", contractManage.getCopyCount()); + data.put("settlementCurrency", contractManage.getSettlementCurrency()); + data.put("invoiceCycle", contractManage.getInvoiceCycle()); + data.put("paymentDays", contractManage.getPaymentDays()); + data.put("remark", contractManage.getRemark()); + data.put("feeGenerationMode", contractManage.getFeeGenerationMode()); + data.put("billingPlanJson", contractManage.getBillingPlanJson()); + data.put("settlementRuleJson", contractManage.getSettlementRuleJson()); + data.put("preSettlementConfigJson", contractManage.getPreSettlementConfigJson()); + data.put("formalSettlementConfigJson", contractManage.getFormalSettlementConfigJson()); + data.put("paymentRatioJson", contractManage.getPaymentRatioJson()); + data.put("contractFileJson", contractManage.getContractFileJson()); + data.put("attachmentsJson", contractManage.getAttachmentsJson()); + data.put("changeAttachmentsJson", contractManage.getChangeAttachmentsJson()); + return data; + } + + private void retainChangedSnapshotFields(Map beforeData, Map afterData) { + Set changedFields = new HashSet<>(); + beforeData.forEach((field, value) -> { + if (!Objects.equals(value, afterData.get(field))) { + changedFields.add(field); + } + }); + beforeData.keySet().retainAll(changedFields); + afterData.keySet().retainAll(changedFields); + } + private void updateLatestReviewingChangeRecord(ContractManage contractManage, String status, String statusName) { List> records = parseChangeRecords(contractManage.getChangeRecordJson()); for (int index = records.size() - 1; index >= 0; index--) { @@ -669,6 +811,47 @@ public class ContractManageServiceImpl extends BaseServiceImpl merged = parseJsonArray(existingJson); + List pending = parseJsonArray(pendingJson); + if (pending.isEmpty()) { + return TransportBusinessSupport.trimToNull(existingJson); + } + Set keys = new HashSet<>(); + merged.forEach(item -> keys.add(attachmentKey(item))); + pending.forEach(item -> { + if (item != null && keys.add(attachmentKey(item))) { + merged.add(item); + } + }); + return merged.isEmpty() ? null : JsonUtil.toJson(merged); + } + + @SuppressWarnings("unchecked") + private List parseJsonArray(String value) { + if (Func.isEmpty(value)) { + return new ArrayList<>(); + } + try { + Object parsed = JsonUtil.parse(value, List.class); + return parsed instanceof List list ? new ArrayList<>((List) list) : new ArrayList<>(); + } catch (Exception ignored) { + return new ArrayList<>(); + } + } + + private String attachmentKey(Object attachment) { + if (attachment instanceof Map map) { + for (String field : List.of("url", "link", "fileUrl", "downloadUrl", "uid", "originalName", "name")) { + Object value = map.get(field); + if (value != null && !String.valueOf(value).isBlank()) { + return field + ":" + value; + } + } + } + return String.valueOf(attachment); + } + @SuppressWarnings("unchecked") private List> parseChangeRecords(String value) { if (Func.isEmpty(value)) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java index d4494d0..9dbbd54 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ProjectApplyServiceImpl.java @@ -297,6 +297,9 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl goods, String fallbackName) { Object actualPath = goods.get("cargoTypePath"); if (matchesCargoTypePath(expectedPath, actualPath)) return true; - String actualCode = firstNotBlank( - goods.get("cargoTypeCode"), - goods.get("secondCargoTypeCode"), - goods.get("firstCargoTypeCode")); - if (!isBlank(expectedCode) && Func.isNotEmpty(actualCode)) { - String expectedCodeText = String.valueOf(expectedCode).trim(); - if (expectedCodeText.equals(actualCode)) return true; - if (isFirstLevelCargoType(expectedPath, expectedCodeText) - && actualCode.startsWith(expectedCodeText)) return true; - } + if (matchesCargoTypeCode(expectedPath, expectedCode, goods)) return true; if (isBlank(expectedName)) return false; return matchesCondition(expectedName, stringValue(goods, "cargoType", fallbackName)) || matchesCondition(expectedName, stringValue(goods, "secondCargoTypeName", "")) || matchesCondition(expectedName, stringValue(goods, "firstCargoTypeName", "")); } + /** + * 一级货物类型使用父级编码匹配二级货物编码。历史货物明细可能同时存在多个编码字段, + * 不能只取第一个非空值,否则二级编码会遮蔽可用于前缀匹配的一级编码。 + */ + private boolean matchesCargoTypeCode(Object expectedPath, Object expectedCode, + Map goods) { + if (isBlank(expectedCode)) return false; + String expected = String.valueOf(expectedCode).trim(); + boolean firstLevel = isFirstLevelCargoType(expectedPath, expected); + return java.util.stream.Stream.of(goods.get("cargoTypeCode"), goods.get("secondCargoTypeCode"), + goods.get("firstCargoTypeCode")) + .filter(value -> !isBlank(value)) + .map(value -> String.valueOf(value).trim()) + .anyMatch(actual -> expected.equals(actual) || (firstLevel && actual.startsWith(expected))); + } + private boolean matchesCargoTypePath(Object expectedPath, Object actualPath) { if (!(expectedPath instanceof Collection expectedValues) || !(actualPath instanceof Collection actualValues) @@ -978,13 +985,6 @@ public class ReceivablePayableDetailServiceImpl return expectedCode.matches("\\d{2}"); } - private String firstNotBlank(Object... values) { - for (Object value : values) { - if (!isBlank(value)) return String.valueOf(value).trim(); - } - return ""; - } - private String resolveRegionCode(Long addressId) { if (addressId == null) return ""; try { diff --git a/doc/sql/bladex/bladex.dameng.all.create.sql b/doc/sql/bladex/bladex.dameng.all.create.sql index 2eefffb..5ee240e 100644 --- a/doc/sql/bladex/bladex.dameng.all.create.sql +++ b/doc/sql/bladex/bladex.dameng.all.create.sql @@ -6521,6 +6521,7 @@ CREATE TABLE "BLADEX"."BLADE_FEE_ITEM" ( "ID" NUMBER(20,0) NOT NULL, "NAME" NVARCHAR2(50) NOT NULL, "ENGLISH_NAME" NVARCHAR2(100), + "TAX_RATE" NUMBER(6,2) DEFAULT 0 NOT NULL, "CREATE_USER" NUMBER(20,0), "CREATE_DEPT" NUMBER(20,0), "CREATE_TIME" DATE, diff --git a/doc/sql/bladex/bladex.kingbase.all.create.sql b/doc/sql/bladex/bladex.kingbase.all.create.sql index 1f4104f..b7bf2e5 100644 --- a/doc/sql/bladex/bladex.kingbase.all.create.sql +++ b/doc/sql/bladex/bladex.kingbase.all.create.sql @@ -2565,6 +2565,7 @@ CREATE TABLE "blade_fee_item" ( "fee_category" varchar(50) NOT NULL, "name" varchar(50) NOT NULL, "english_name" varchar(100), + "tax_rate" numeric(6,2) NOT NULL DEFAULT 0.00, "create_user" int8, "create_dept" int8, "create_time" timestamp(6), diff --git a/doc/sql/bladex/bladex.mysql.all.create.sql b/doc/sql/bladex/bladex.mysql.all.create.sql index 5e52c13..8728449 100644 --- a/doc/sql/bladex/bladex.mysql.all.create.sql +++ b/doc/sql/bladex/bladex.mysql.all.create.sql @@ -1721,6 +1721,7 @@ CREATE TABLE `blade_fee_item` ( `fee_category` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '费用类型', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '费用项', `english_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '费用项代码', + `tax_rate` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '税率(百分比)', `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `create_user` bigint NULL DEFAULT NULL COMMENT '创建人', `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', diff --git a/doc/sql/bladex/bladex.oracle.all.create.sql b/doc/sql/bladex/bladex.oracle.all.create.sql index 97b9efd..ac2ac91 100644 --- a/doc/sql/bladex/bladex.oracle.all.create.sql +++ b/doc/sql/bladex/bladex.oracle.all.create.sql @@ -6755,6 +6755,7 @@ CREATE TABLE "BLADE_FEE_ITEM" ( "ID" NUMBER(20,0) NOT NULL, "NAME" NVARCHAR2(50) NOT NULL, "ENGLISH_NAME" NVARCHAR2(100), + "TAX_RATE" NUMBER(6,2) DEFAULT 0 NOT NULL, "CREATE_USER" NUMBER(20,0), "CREATE_DEPT" NUMBER(20,0), "CREATE_TIME" DATE, diff --git a/doc/sql/bladex/bladex.postgres.all.create.sql b/doc/sql/bladex/bladex.postgres.all.create.sql index 1f4104f..b7bf2e5 100644 --- a/doc/sql/bladex/bladex.postgres.all.create.sql +++ b/doc/sql/bladex/bladex.postgres.all.create.sql @@ -2565,6 +2565,7 @@ CREATE TABLE "blade_fee_item" ( "fee_category" varchar(50) NOT NULL, "name" varchar(50) NOT NULL, "english_name" varchar(100), + "tax_rate" numeric(6,2) NOT NULL DEFAULT 0.00, "create_user" int8, "create_dept" int8, "create_time" timestamp(6), diff --git a/doc/sql/bladex/bladex.sqlserver.all.create.sql b/doc/sql/bladex/bladex.sqlserver.all.create.sql index 8c64a8a..632f801 100644 --- a/doc/sql/bladex/bladex.sqlserver.all.create.sql +++ b/doc/sql/bladex/bladex.sqlserver.all.create.sql @@ -7730,6 +7730,7 @@ CREATE TABLE [dbo].[blade_fee_item] ( [fee_category] nvarchar(50) NOT NULL, [name] nvarchar(50) NOT NULL, [english_name] nvarchar(100) NULL, + [tax_rate] decimal(6,2) NOT NULL CONSTRAINT [df_blade_fee_item_tax_rate] DEFAULT (0.00), [create_user] bigint NULL, [create_dept] bigint NULL, [create_time] datetime2(0) NULL, diff --git a/doc/sql/bladex/bladex.yashan.all.create.sql b/doc/sql/bladex/bladex.yashan.all.create.sql index c40d914..dc9da3b 100644 --- a/doc/sql/bladex/bladex.yashan.all.create.sql +++ b/doc/sql/bladex/bladex.yashan.all.create.sql @@ -28645,6 +28645,7 @@ CREATE TABLE "BLADE_FEE_ITEM" ( "ID" NUMBER(20,0) NOT NULL, "NAME" NVARCHAR2(50) NOT NULL, "ENGLISH_NAME" NVARCHAR2(100), + "TAX_RATE" NUMBER(6,2) DEFAULT 0 NOT NULL, "CREATE_USER" NUMBER(20,0), "CREATE_DEPT" NUMBER(20,0), "CREATE_TIME" DATE, diff --git a/doc/sql/transport/blade_contract_manage_billing_plan_tax_rate_20260903.sql b/doc/sql/transport/blade_contract_manage_billing_plan_tax_rate_20260903.sql new file mode 100644 index 0000000..9c42a81 --- /dev/null +++ b/doc/sql/transport/blade_contract_manage_billing_plan_tax_rate_20260903.sql @@ -0,0 +1,5 @@ +-- 合同计费方案规则新增税率配置。 +-- 税率按规则存储于 billing_plan_json 的 taxRate 属性,无需新增独立表字段。 + +ALTER TABLE `blade_contract_manage` + MODIFY COLUMN `billing_plan_json` text DEFAULT NULL COMMENT '计费方案JSON(含运输方式、默认方案及规则税率配置)'; diff --git a/doc/sql/transport/blade_contract_manage_change_attachments_20260903.sql b/doc/sql/transport/blade_contract_manage_change_attachments_20260903.sql new file mode 100644 index 0000000..a7b002e --- /dev/null +++ b/doc/sql/transport/blade_contract_manage_change_attachments_20260903.sql @@ -0,0 +1,3 @@ +-- 合同变更附件暂存字段,审批通过后归集到其它附件 +ALTER TABLE `blade_contract_manage` + ADD COLUMN `change_attachments_json` text DEFAULT NULL COMMENT '待审批变更附件JSON' AFTER `change_reason`; diff --git a/doc/sql/transport/blade_fee_item.sql b/doc/sql/transport/blade_fee_item.sql index f9585ba..b2f69e8 100644 --- a/doc/sql/transport/blade_fee_item.sql +++ b/doc/sql/transport/blade_fee_item.sql @@ -7,6 +7,7 @@ CREATE TABLE `blade_fee_item` ( `fee_category` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '费用类型', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '费用项', `english_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '费用项代码', + `tax_rate` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '税率(百分比)', `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `create_user` bigint NULL DEFAULT NULL COMMENT '创建人', `create_dept` bigint NULL DEFAULT NULL COMMENT '创建部门', diff --git a/doc/sql/transport/blade_fee_item_tax_rate_20260903.sql b/doc/sql/transport/blade_fee_item_tax_rate_20260903.sql new file mode 100644 index 0000000..9ae5b4e --- /dev/null +++ b/doc/sql/transport/blade_fee_item_tax_rate_20260903.sql @@ -0,0 +1,5 @@ +-- 费用项新增税率字段(百分比) +-- 现有数据以 0.00 初始化,后续新增或修改费用项必须填写 0-100 范围内的税率。 + +ALTER TABLE `blade_fee_item` + ADD COLUMN `tax_rate` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '税率(百分比)' AFTER `english_name`; diff --git a/doc/sql/transport/blade_project_contract_management.sql b/doc/sql/transport/blade_project_contract_management.sql index 2b9b7b1..2f2d194 100644 --- a/doc/sql/transport/blade_project_contract_management.sql +++ b/doc/sql/transport/blade_project_contract_management.sql @@ -144,12 +144,13 @@ CREATE TABLE `blade_contract_manage` ( `billing_enabled` int(11) DEFAULT '0' COMMENT '计费信息开关', `contract_file_json` text DEFAULT NULL COMMENT '合同主文件JSON', `attachments_json` text DEFAULT NULL COMMENT '其它附件JSON', - `billing_plan_json` text DEFAULT NULL COMMENT '计费方案JSON(含运输方式及默认方案配置)', + `billing_plan_json` text DEFAULT NULL COMMENT '计费方案JSON(含运输方式、默认方案及规则税率配置)', `settlement_rule_json` text DEFAULT NULL COMMENT '结算生成规则JSON', `reconciliation_json` text DEFAULT NULL COMMENT '对账配置JSON', `change_record_json` text DEFAULT NULL COMMENT '变更记录JSON', `change_content` text DEFAULT NULL COMMENT '变更内容', `change_reason` varchar(500) DEFAULT NULL COMMENT '变更原因', + `change_attachments_json` text DEFAULT NULL COMMENT '待审批变更附件JSON', `terminate_reason` varchar(500) DEFAULT NULL COMMENT '终止原因', `remark` varchar(2000) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`id`) USING BTREE,