+1
@@ -101,6 +101,7 @@ public class FormalSettlementController extends BladeController {
|
||||
excel.setSettlementAmount(formatMoney(vo.getSettlementAmount(), vo.getCurrency()));
|
||||
excel.setBusinessModelName(vo.getBusinessModelName());
|
||||
excel.setTransportTypeName(vo.getTransportTypeName());
|
||||
excel.setSealTypes(vo.getSealTypes());
|
||||
excel.setLocalSettlementAmount(formatMoney(vo.getLocalSettlementAmount(), vo.getLocalCurrency()));
|
||||
excel.setExchangeRate(vo.getExchangeRate() == null ? "" : vo.getExchangeRate().stripTrailingZeros().toPlainString());
|
||||
excel.setInvoiceStatusName(invoiceStatusName(vo.getInvoiceStatus(), vo.getSettlementType()));
|
||||
|
||||
+1
@@ -252,6 +252,7 @@ public class PreSettlementController extends BladeController {
|
||||
excel.setSettlementAmount(formatMoney(vo.getSettlementAmount(), vo.getCurrency()));
|
||||
excel.setBusinessModelName(vo.getBusinessModelName());
|
||||
excel.setTransportTypeName(vo.getTransportTypeName());
|
||||
excel.setSealTypes(vo.getSealTypes());
|
||||
excel.setLocalSettlementAmount(formatMoney(vo.getLocalSettlementAmount(), vo.getLocalCurrency()));
|
||||
excel.setExchangeRate(vo.getExchangeRate() == null ? "" : vo.getExchangeRate().stripTrailingZeros().toPlainString());
|
||||
excel.setAdvanceAppliedAmount(formatMoney(vo.getAdvanceAppliedAmount(), vo.getCurrency()));
|
||||
|
||||
+2
@@ -43,6 +43,8 @@ public class FormalSettlementExcel implements Serializable {
|
||||
private String businessModelName;
|
||||
@ExcelProperty("运输方式")
|
||||
private String transportTypeName;
|
||||
@ExcelProperty("用印类型")
|
||||
private String sealTypes;
|
||||
@ExcelProperty("本位币结算金额")
|
||||
private String localSettlementAmount;
|
||||
@ExcelProperty("结算汇率")
|
||||
|
||||
+2
@@ -54,6 +54,8 @@ public class PreSettlementExcel implements Serializable {
|
||||
private String businessModelName;
|
||||
@ExcelProperty("运输方式")
|
||||
private String transportTypeName;
|
||||
@ExcelProperty("用印类型")
|
||||
private String sealTypes;
|
||||
@ExcelProperty("本位币结算金额")
|
||||
private String localSettlementAmount;
|
||||
@ExcelProperty("结算汇率")
|
||||
|
||||
+1
@@ -651,6 +651,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
if (first == null) copyHeader(contract, settlementType, directDetails.get(0), settlement); else copyHeader(first, settlement);
|
||||
settlement.setBusinessModel(limit(request.getBusinessModel(), 64));
|
||||
settlement.setTransportType(limit(request.getTransportType(), 64));
|
||||
settlement.setSealTypes(TransportBusinessSupport.normalizeSettlementSealTypes(request.getSealTypes()));
|
||||
settlement.setExchangeRateDate(request.getExchangeRateDate());
|
||||
settlement.setExchangeRate(request.getExchangeRate() == null ? BigDecimal.ONE : positive(request.getExchangeRate(), "结算汇率"));
|
||||
BigDecimal sourceAmount = sources.stream().map(PreSettlement::getSettlementAmount).map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
+1
@@ -417,6 +417,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
||||
fillContract(settlement, contract);
|
||||
settlement.setBusinessModel(limitText(request.getBusinessModel(), 64, "业务模式"));
|
||||
settlement.setTransportType(limitText(request.getTransportType(), 64, "运输方式"));
|
||||
settlement.setSealTypes(TransportBusinessSupport.normalizeSettlementSealTypes(request.getSealTypes()));
|
||||
settlement.setExchangeRateDate(request.getExchangeRateDate());
|
||||
settlement.setExchangeRate(request.getExchangeRate());
|
||||
settlement.setAttachmentsJson(request.getAttachmentsJson());
|
||||
|
||||
+124
-1
@@ -63,10 +63,12 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -345,6 +347,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
|
||||
/**
|
||||
* 将变更单可回写字段映射为临时 ProjectApply,供 copyChangeFields 复用。
|
||||
* 项目材料:以变更单原材料为基础,按调整材料替换/追加后回写原项目档案。
|
||||
*/
|
||||
private ProjectApply toProjectApplySnapshot(ProjectChange change) {
|
||||
ProjectApply snapshot = new ProjectApply();
|
||||
@@ -378,12 +381,132 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
snapshot.setCustomerJson(change.getCustomerJson());
|
||||
snapshot.setCarrierJson(change.getCarrierJson());
|
||||
snapshot.setSituationRemark(change.getSituationRemark());
|
||||
snapshot.setAttachmentsJson(change.getAttachmentsJson());
|
||||
snapshot.setAttachmentsJson(applyAdjustAttachments(change.getAttachmentsJson(), change.getAdjustAttachmentsJson()));
|
||||
snapshot.setChangeContent(change.getChangeContent());
|
||||
snapshot.setChangeReason(change.getChangeReason());
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批通过后应用调整材料:有原附件则替换,无原附件则追加;新材料同步到项目档案【项目材料】。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private String applyAdjustAttachments(String attachmentsJson, String adjustAttachmentsJson) {
|
||||
List<Map<String, Object>> materials = parseAttachmentMapList(attachmentsJson);
|
||||
List<Map<String, Object>> adjustRows = parseAttachmentMapList(adjustAttachmentsJson);
|
||||
if (adjustRows.isEmpty()) {
|
||||
return TransportBusinessSupport.trimToNull(attachmentsJson);
|
||||
}
|
||||
Set<Integer> replacedIndexes = new HashSet<>();
|
||||
for (Map<String, Object> adjustRow : adjustRows) {
|
||||
if (adjustRow == null || adjustRow.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> nextMaterial = toProjectMaterialRow(adjustRow);
|
||||
if (nextMaterial.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
int originIndex = findOriginAttachmentIndex(materials, adjustRow, replacedIndexes);
|
||||
if (originIndex >= 0) {
|
||||
materials.set(originIndex, nextMaterial);
|
||||
replacedIndexes.add(originIndex);
|
||||
} else {
|
||||
materials.add(nextMaterial);
|
||||
}
|
||||
}
|
||||
return materials.isEmpty() ? null : JsonUtil.toJson(materials);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Map<String, Object>> parseAttachmentMapList(String value) {
|
||||
if (Func.isEmpty(value)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
Object parsed = JsonUtil.parse(value, List.class);
|
||||
if (!(parsed instanceof List<?> list)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
for (Object item : list) {
|
||||
if (item instanceof Map<?, ?> map) {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
map.forEach((key, val) -> row.put(String.valueOf(key), val));
|
||||
rows.add(row);
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
} catch (Exception ignored) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> toProjectMaterialRow(Map<String, Object> adjustRow) {
|
||||
Map<String, Object> material = new LinkedHashMap<>(adjustRow);
|
||||
material.remove("adjustReason");
|
||||
material.remove("originAttachment");
|
||||
material.remove("originKey");
|
||||
return material;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private int findOriginAttachmentIndex(List<Map<String, Object>> materials, Map<String, Object> adjustRow,
|
||||
Set<Integer> replacedIndexes) {
|
||||
Object originObj = adjustRow.get("originAttachment");
|
||||
Map<String, Object> origin = null;
|
||||
if (originObj instanceof Map<?, ?> map) {
|
||||
origin = new LinkedHashMap<>();
|
||||
Map<String, Object> finalOrigin = origin;
|
||||
map.forEach((key, val) -> finalOrigin.put(String.valueOf(key), val));
|
||||
}
|
||||
String originKey = String.valueOf(adjustRow.getOrDefault("originKey", "")).trim();
|
||||
for (int index = 0; index < materials.size(); index++) {
|
||||
if (replacedIndexes.contains(index)) {
|
||||
continue;
|
||||
}
|
||||
Map<String, Object> current = materials.get(index);
|
||||
if (Func.isNotEmpty(originKey) && originKey.equals(attachmentIdentity(current))) {
|
||||
return index;
|
||||
}
|
||||
if (origin != null && attachmentIdentity(current).equals(attachmentIdentity(origin))) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String attachmentIdentity(Map<String, Object> row) {
|
||||
if (row == null || row.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String uid = String.valueOf(row.getOrDefault("uid", "")).trim();
|
||||
if (Func.isNotEmpty(uid) && !"null".equals(uid)) {
|
||||
return "uid:" + uid;
|
||||
}
|
||||
for (String field : List.of("url", "link", "fileUrl", "downloadUrl", "src", "domain")) {
|
||||
String value = String.valueOf(row.getOrDefault(field, "")).trim();
|
||||
if (Func.isNotEmpty(value) && !"null".equals(value)) {
|
||||
return "url:" + value;
|
||||
}
|
||||
}
|
||||
String fileType = String.valueOf(row.getOrDefault("fileType", "")).trim();
|
||||
String name = firstAttachmentName(row);
|
||||
if (Func.isNotEmpty(fileType) || Func.isNotEmpty(name)) {
|
||||
return "name:" + fileType + "|" + name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private String firstAttachmentName(Map<String, Object> row) {
|
||||
for (String field : List.of("originalName", "name", "fileName")) {
|
||||
String value = String.valueOf(row.getOrDefault(field, "")).trim();
|
||||
if (Func.isNotEmpty(value) && !"null".equals(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean removeDraft(String ids) {
|
||||
|
||||
+2
@@ -126,6 +126,7 @@ public class ProjectChangeServiceImpl extends BaseServiceImpl<ProjectChangeMappe
|
||||
projectChange.setChangeDate(LocalDate.now());
|
||||
projectChange.setChangeContent(null);
|
||||
projectChange.setChangeReason(null);
|
||||
projectChange.setAdjustAttachmentsJson(null);
|
||||
projectChange.setBeforeDataJson(JsonUtil.toJson(buildSnapshot(projectApply)));
|
||||
projectChange.setApprovalStatus(STATUS_DRAFT);
|
||||
projectChange.setCurrentNode("草稿");
|
||||
@@ -350,6 +351,7 @@ public class ProjectChangeServiceImpl extends BaseServiceImpl<ProjectChangeMappe
|
||||
target.setCarrierJson(source.getCarrierJson());
|
||||
target.setSituationRemark(source.getSituationRemark());
|
||||
target.setAttachmentsJson(source.getAttachmentsJson());
|
||||
target.setAdjustAttachmentsJson(source.getAdjustAttachmentsJson());
|
||||
if (source.getReceivableDays() != null && source.getReceivableDays() == -1) {
|
||||
target.setReceivableDays(null);
|
||||
}
|
||||
|
||||
+56
-4
@@ -34,6 +34,7 @@ import org.springblade.system.cache.RoleDataScopeCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.enums.RoleDataScopeType;
|
||||
import org.springblade.system.pojo.vo.RoleDataScopeVO;
|
||||
import org.springblade.transport.mapper.ProjectApplyMapper;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
@@ -61,6 +62,8 @@ public final class TransportBusinessSupport {
|
||||
public static final String DATA_SOURCE_MANUAL = "手工创建";
|
||||
public static final String DATA_SOURCE_PLAN_DISPATCH = "计划调度";
|
||||
public static final String DATA_SOURCE_EXTERNAL = "外部系统";
|
||||
/** 结算单用印类型固定选项(展示顺序固定) */
|
||||
public static final List<String> SETTLEMENT_SEAL_TYPE_OPTIONS = List.of("公章", "业务章");
|
||||
/** 站点名(省+市+区县)列宽:blade_waybill.departure_name / arrival_name 为 varchar(100) */
|
||||
public static final int REGION_NAME_MAX_LENGTH = 100;
|
||||
|
||||
@@ -134,7 +137,8 @@ public final class TransportBusinessSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析指定菜单下的角色数据权限(超管视为全部组织),并与用户「数据管理权限」组织取并集。
|
||||
* 解析指定菜单下的角色数据权限(超管视为全部组织)。
|
||||
* 全部组织、本人创建以角色权限为准;所在部门及下级按用户数据权限组织。
|
||||
*/
|
||||
public static RoleDataScopeVO resolveRoleDataScope(String menuCode) {
|
||||
if (isAdministrator()) {
|
||||
@@ -166,7 +170,8 @@ public final class TransportBusinessSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析最宽角色数据权限,并与用户「数据管理权限」组织取并集。
|
||||
* 解析最宽角色数据权限。
|
||||
* 全部组织、本人创建以角色权限为准;所在部门及下级按用户数据权限组织。
|
||||
*/
|
||||
public static RoleDataScopeVO resolveWidestDataScope() {
|
||||
if (isAdministrator()) {
|
||||
@@ -177,9 +182,12 @@ public final class TransportBusinessSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色组织权限与用户数据管理组织取并集。
|
||||
* 按角色数据权限类型决定是否采用用户数据权限组织。
|
||||
* <p>
|
||||
* 全部组织 / 本人创建 不因用户管理组织改变;其余类型将用户管理组织(含下级)并入 deptIds。
|
||||
* 全部组织、本人创建以角色权限为准,不按用户数据权限收窄。
|
||||
* 所在部门及下级改为用户「数据管理权限」组织(含下级);同时存在自定义组织时取并集。
|
||||
* 自定义组织仍与用户数据权限组织取并集。
|
||||
* 所在部门及下级且用户未配置数据权限组织时,返回不可命中的组织,避免回落到所属组织或全部数据。
|
||||
*/
|
||||
private static RoleDataScopeVO mergeUserManageDepts(RoleDataScopeVO scope) {
|
||||
if (scope == null || scope.isAllDept() || scope.isOwnCreate()) {
|
||||
@@ -187,6 +195,19 @@ public final class TransportBusinessSupport {
|
||||
}
|
||||
Long userId = AuthUtil.getUserId();
|
||||
List<Long> manageDeptIds = UserCache.getManageDeptIds(userId);
|
||||
if (RoleDataScopeType.DEPT_AND_CHILD.matches(scope.getScopeType())) {
|
||||
LinkedHashSet<Long> deptIds = new LinkedHashSet<>();
|
||||
if (Func.isNotEmpty(scope.getDeptIds())) {
|
||||
deptIds.addAll(scope.getDeptIds());
|
||||
}
|
||||
if (Func.isNotEmpty(manageDeptIds)) {
|
||||
deptIds.addAll(manageDeptIds);
|
||||
}
|
||||
if (deptIds.isEmpty()) {
|
||||
return RoleDataScopeVO.ofDepts(RoleDataScopeType.DEPT_AND_CHILD.getCode(), List.of(-1L));
|
||||
}
|
||||
return RoleDataScopeVO.ofDepts(RoleDataScopeType.DEPT_AND_CHILD.getCode(), List.copyOf(deptIds));
|
||||
}
|
||||
if (Func.isEmpty(manageDeptIds)) {
|
||||
return scope;
|
||||
}
|
||||
@@ -495,6 +516,37 @@ public final class TransportBusinessSupport {
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化结算单用印类型:仅允许「公章」「业务章」,多选用顿号拼接,并按固定顺序去重。
|
||||
*
|
||||
* @param value 原始用印类型(支持顿号、逗号、空白分隔)
|
||||
* @return 归一化后的用印类型,未选择时返回 null
|
||||
*/
|
||||
public static String normalizeSettlementSealTypes(String value) {
|
||||
String source = trimToNull(value);
|
||||
if (Func.isEmpty(source)) {
|
||||
return null;
|
||||
}
|
||||
String[] parts = source.split("[、,,\\s]+");
|
||||
LinkedHashSet<String> selected = new LinkedHashSet<>();
|
||||
for (String part : parts) {
|
||||
String item = trimToNull(part);
|
||||
if (Func.isEmpty(item)) {
|
||||
continue;
|
||||
}
|
||||
if (!SETTLEMENT_SEAL_TYPE_OPTIONS.contains(item)) {
|
||||
throw new ServiceException("用印类型仅支持:公章、业务章");
|
||||
}
|
||||
selected.add(item);
|
||||
}
|
||||
if (selected.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return SETTLEMENT_SEAL_TYPE_OPTIONS.stream()
|
||||
.filter(selected::contains)
|
||||
.collect(Collectors.joining("、"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化运输计划数据来源,保证列表只出现约定的三种来源。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user