调整结算模块
This commit is contained in:
+4
@@ -47,6 +47,10 @@ public class CommonCargoVO extends CommonCargo {
|
||||
@Schema(description = "是否查看全部组织")
|
||||
private Integer allDept;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "规格型号(匹配规格或型号)")
|
||||
private String specificationModel;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否只读")
|
||||
private Boolean readonly;
|
||||
|
||||
+2
-2
@@ -30,12 +30,12 @@ public class CargoReconciliationExcel implements Serializable {
|
||||
@ExcelProperty("货物类型") private String cargoType;
|
||||
@ExcelProperty("规格") private String specification;
|
||||
@ExcelProperty("型号") private String model;
|
||||
@ExcelProperty("运输量") @NumberFormat("0.000000") private BigDecimal transportQuantity;
|
||||
@ExcelProperty("运输总量") @NumberFormat("0.000000") private BigDecimal transportQuantity;
|
||||
@ExcelProperty("运输单价") @NumberFormat("0.00") private BigDecimal unitPrice;
|
||||
@ExcelProperty("里程(KM)") @NumberFormat("0.00") private BigDecimal mileage;
|
||||
@ExcelProperty("运输费") @NumberFormat("0.00") private BigDecimal freightAmount;
|
||||
@ExcelProperty("费用项目名称1") @NumberFormat("0.00") private BigDecimal feeItemOne;
|
||||
@ExcelProperty("费用项目名称2") @NumberFormat("0.00") private BigDecimal feeItemTwo;
|
||||
@ExcelProperty("结算金额") @NumberFormat("0.00") private BigDecimal settlementAmount;
|
||||
@ExcelProperty("结算费用合计") @NumberFormat("0.00") private BigDecimal settlementAmount;
|
||||
@ExcelIgnore private String errorMessage;
|
||||
}
|
||||
|
||||
+4
@@ -238,6 +238,10 @@ public class CommonCargoServiceImpl extends BaseServiceImpl<CommonCargoMapper, C
|
||||
if (Func.isNotEmpty(commonCargo.getPackageType())) {
|
||||
queryWrapper.like(CommonCargo::getPackageType, commonCargo.getPackageType());
|
||||
}
|
||||
if (Func.isNotEmpty(commonCargo.getSpecificationModel())) {
|
||||
queryWrapper.and(wrapper -> wrapper.like(CommonCargo::getSpecification, commonCargo.getSpecificationModel())
|
||||
.or().like(CommonCargo::getModel, commonCargo.getSpecificationModel()));
|
||||
}
|
||||
if (Func.isNotEmpty(commonCargo.getSpecification())) {
|
||||
queryWrapper.like(CommonCargo::getSpecification, commonCargo.getSpecification());
|
||||
}
|
||||
|
||||
+99
-21
@@ -52,18 +52,20 @@ import org.springblade.transport.pojo.vo.TransportReconciliationVO;
|
||||
import org.springblade.transport.service.ITransportReconciliationService;
|
||||
import org.springblade.transport.wrapper.TransportReconciliationWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.Collator;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
@@ -217,7 +219,7 @@ public class TransportReconciliationServiceImpl
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
validExternals.add(buildVehicleExternal(id, index, row));
|
||||
validExternals.add(buildVehicleExternal(id, index, row));
|
||||
} catch (Exception exception) {
|
||||
VehicleReconciliationFailureExcel failure = new VehicleReconciliationFailureExcel();
|
||||
BeanUtil.copyProperties(row, failure);
|
||||
@@ -226,10 +228,6 @@ public class TransportReconciliationServiceImpl
|
||||
failures.add(failure);
|
||||
}
|
||||
}
|
||||
if (Func.isNotEmpty(failures)) {
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return failures;
|
||||
}
|
||||
for (TransportReconciliationExternal external : validExternals) {
|
||||
if (externalMapper.insert(external) <= 0) throw new ServiceException("外部账单保存失败");
|
||||
}
|
||||
@@ -242,7 +240,7 @@ public class TransportReconciliationServiceImpl
|
||||
TransportReconciliationExternal external = new TransportReconciliationExternal();
|
||||
BeanUtil.copyProperties(row, external);
|
||||
external.setReconciliationId(reconciliationId);
|
||||
external.setExternalLineNo(index + 2);
|
||||
external.setExternalLineNo(index + 1);
|
||||
external.setActualDepartureTime(parseTime(row.getActualDepartureTime(), "实际发货时间"));
|
||||
external.setActualCompletionTime(parseTimeNullable(row.getActualCompletionTime(), "实际完成时间"));
|
||||
external.setFeeItemsJson(JsonUtil.toJson(Map.of("费用项目1", money(row.getFeeItemOne()))));
|
||||
@@ -312,32 +310,95 @@ public class TransportReconciliationServiceImpl
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<CargoReconciliationFailureExcel> importCargoes(Long id, List<CargoReconciliationExcel> rows) {
|
||||
if (Func.isEmpty(rows)) throw new ServiceException("导入数据不能为空");
|
||||
TransportReconciliation bill = editable(id);
|
||||
if (!CARGO.equals(bill.getReconciliationMode())) throw new ServiceException("当前对账模式不是货物明细对账");
|
||||
resetExternal(id);
|
||||
List<CargoReconciliationFailureExcel> failures = new ArrayList<>();
|
||||
List<TransportReconciliationExternal> validExternals = new ArrayList<>();
|
||||
for (int index = 0; index < rows.size(); index++) {
|
||||
CargoReconciliationExcel row = rows.get(index);
|
||||
List<String> validationErrors = validateImportCargo(row);
|
||||
if (Func.isNotEmpty(validationErrors)) {
|
||||
CargoReconciliationFailureExcel failure = new CargoReconciliationFailureExcel();
|
||||
BeanUtil.copyProperties(row, failure);
|
||||
failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(validationErrors));
|
||||
failures.add(failure);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
validateExternal(row.getVehicleNo(), row.getCargoName(), row.getTransportQuantity(), row.getSettlementAmount());
|
||||
TransportReconciliationExternal external = new TransportReconciliationExternal();
|
||||
BeanUtil.copyProperties(row, external);
|
||||
external.setReconciliationId(id); external.setExternalLineNo(index + 2);
|
||||
external.setActualDepartureTime(parseTime(row.getActualDepartureTime(), "实际发货时间"));
|
||||
external.setActualCompletionTime(parseTimeNullable(row.getActualCompletionTime(), "实际完成时间"));
|
||||
external.setFeeItemsJson(JsonUtil.toJson(Map.of("费用项目名称1", money(row.getFeeItemOne()), "费用项目名称2", money(row.getFeeItemTwo()))));
|
||||
external.setMatchStatus(UNMATCHED); external.setSuspectedDuplicate(false);
|
||||
external.setRawDataJson(JsonUtil.toJson(row)); externalMapper.insert(external);
|
||||
validExternals.add(buildCargoExternal(id, index, row));
|
||||
} catch (Exception exception) {
|
||||
CargoReconciliationFailureExcel failure = new CargoReconciliationFailureExcel();
|
||||
BeanUtil.copyProperties(row, failure); failure.setErrorMessage(exception.getMessage());
|
||||
BeanUtil.copyProperties(row, failure);
|
||||
failure.setErrorMessage(org.springblade.common.excel.ImportFailureExcelUtil.formatErrorMessage(List.of(
|
||||
exception instanceof ServiceException ? exception.getMessage() : "导入失败")));
|
||||
failures.add(failure);
|
||||
}
|
||||
}
|
||||
for (TransportReconciliationExternal external : validExternals) {
|
||||
if (externalMapper.insert(external) <= 0) throw new ServiceException("外部账单保存失败");
|
||||
}
|
||||
refreshStats(id);
|
||||
return failures;
|
||||
}
|
||||
|
||||
private TransportReconciliationExternal buildCargoExternal(Long reconciliationId, int index,
|
||||
CargoReconciliationExcel row) {
|
||||
TransportReconciliationExternal external = new TransportReconciliationExternal();
|
||||
BeanUtil.copyProperties(row, external);
|
||||
external.setReconciliationId(reconciliationId);
|
||||
external.setExternalLineNo(index + 1);
|
||||
external.setActualDepartureTime(parseTime(row.getActualDepartureTime(), "实际发货时间"));
|
||||
external.setActualCompletionTime(parseTimeNullable(row.getActualCompletionTime(), "实际完成时间"));
|
||||
external.setFeeItemsJson(JsonUtil.toJson(Map.of("费用项目名称1", money(row.getFeeItemOne()), "费用项目名称2", money(row.getFeeItemTwo()))));
|
||||
external.setMatchStatus(UNMATCHED);
|
||||
external.setSuspectedDuplicate(false);
|
||||
external.setRawDataJson(JsonUtil.toJson(row));
|
||||
return external;
|
||||
}
|
||||
|
||||
private List<String> validateImportCargo(CargoReconciliationExcel row) {
|
||||
List<String> validationErrors = new ArrayList<>();
|
||||
if (row == null) {
|
||||
validationErrors.add("导入数据不能为空");
|
||||
return validationErrors;
|
||||
}
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
Func.isEmpty(row.getVehicleNo()), "车牌号不能为空");
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
Func.isEmpty(row.getCargoName()), "货物名称不能为空");
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
Func.isEmpty(row.getActualDepartureTime()), "实际发货时间不能为空");
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
Func.isEmpty(row.getTransportQuantity()), "运输总量不能为空");
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
Func.isEmpty(row.getSettlementAmount()), "结算费用合计不能为空");
|
||||
|
||||
addImportLengthError(validationErrors, row.getVehicleNo(), 30, "车牌号不能超过30字");
|
||||
addImportLengthError(validationErrors, row.getDepartureAddress(), 500, "发货地址不能超过500字");
|
||||
addImportLengthError(validationErrors, row.getArrivalAddress(), 500, "到货地址不能超过500字");
|
||||
addImportLengthError(validationErrors, row.getCargoName(), 500, "货物名称不能超过500字");
|
||||
addImportLengthError(validationErrors, row.getCargoType(), 500, "货物类型不能超过500字");
|
||||
addImportLengthError(validationErrors, row.getSpecification(), 200, "规格不能超过200字");
|
||||
addImportLengthError(validationErrors, row.getModel(), 200, "型号不能超过200字");
|
||||
|
||||
addImportDecimalErrors(validationErrors, row.getTransportQuantity(), "运输总量", 6);
|
||||
addImportDecimalErrors(validationErrors, row.getMileage(), "里程(KM)", 2);
|
||||
addImportDecimalErrors(validationErrors, row.getUnitPrice(), "运输单价", 2);
|
||||
addImportDecimalErrors(validationErrors, row.getFreightAmount(), "运输费", 2);
|
||||
addImportDecimalErrors(validationErrors, row.getFeeItemOne(), "费用项目名称1", 2);
|
||||
addImportDecimalErrors(validationErrors, row.getFeeItemTwo(), "费用项目名称2", 2);
|
||||
addImportDecimalErrors(validationErrors, row.getSettlementAmount(), "结算费用合计", 2);
|
||||
|
||||
LocalDateTime departureTime = parseImportTime(row.getActualDepartureTime(), "实际发货时间", validationErrors);
|
||||
LocalDateTime completionTime = parseImportTime(row.getActualCompletionTime(), "实际完成时间", validationErrors);
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors,
|
||||
departureTime != null && completionTime != null && completionTime.isBefore(departureTime),
|
||||
"实际完成时间不能早于实际发货时间");
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void autoMatch(Long id) {
|
||||
@@ -685,7 +746,7 @@ public class TransportReconciliationServiceImpl
|
||||
private void validateExternal(String vehicleNo, String cargoName, BigDecimal quantity, BigDecimal amount) {
|
||||
if (Func.isEmpty(vehicleNo)) throw new ServiceException("车牌号不能为空");
|
||||
if (Func.isEmpty(cargoName)) throw new ServiceException("货物名称不能为空");
|
||||
nonNegative(quantity, "运输量"); nonNegative(amount, "结算金额");
|
||||
nonNegative(quantity, "运输总量"); nonNegative(amount, "结算费用合计");
|
||||
}
|
||||
|
||||
private LocalDateTime parseTime(String value, String field) {
|
||||
@@ -707,12 +768,29 @@ public class TransportReconciliationServiceImpl
|
||||
throw new ServiceException(field + "格式应为yyyy-MM-dd HH:mm:ss或yyyy-MM-dd");
|
||||
}
|
||||
|
||||
private String vehicleKey(TransportReconciliationInternal row) { return key(row.getVehicleNo(), row.getCargoName(), row.getActualDepartureTime(), row.getBatchNo(), row.getTransportQuantity()); }
|
||||
private String vehicleKey(TransportReconciliationExternal row) { return key(row.getVehicleNo(), row.getCargoName(), row.getActualDepartureTime(), row.getBatchNo(), row.getTransportQuantity()); }
|
||||
private String vehicleKey(TransportReconciliationInternal row) { return key(row.getVehicleNo(), sortedCargoNames(row.getCargoName()), row.getActualDepartureTime(), row.getBatchNo(), row.getTransportQuantity()); }
|
||||
private String vehicleKey(TransportReconciliationExternal row) { return key(row.getVehicleNo(), sortedCargoNames(row.getCargoName()), row.getActualDepartureTime(), row.getBatchNo(), row.getTransportQuantity()); }
|
||||
private String cargoKey(TransportReconciliationInternal row) { return key(row.getVehicleNo(), row.getDepartureAddress(), row.getArrivalAddress(), row.getCargoName(), row.getActualDepartureTime(), row.getTransportQuantity()); }
|
||||
private String cargoKey(TransportReconciliationExternal row) { return key(row.getVehicleNo(), row.getDepartureAddress(), row.getArrivalAddress(), row.getCargoName(), row.getActualDepartureTime(), row.getTransportQuantity()); }
|
||||
private String key(Object... values) { StringBuilder builder = new StringBuilder(); for (Object value : values) builder.append(normal(value)).append('|'); return builder.toString(); }
|
||||
private String normal(Object value) { if (value == null) return ""; if (value instanceof BigDecimal decimal) return decimal.stripTrailingZeros().toPlainString(); return value.toString().trim().replaceAll("\\s+", "").toLowerCase(); }
|
||||
private String normal(Object value) {
|
||||
if (value == null) return "";
|
||||
if (value instanceof BigDecimal decimal) return decimal.stripTrailingZeros().toPlainString();
|
||||
if (value instanceof List<?> list) return JsonUtil.toJson(list);
|
||||
return value.toString().trim().replaceAll("\\s+", "").toLowerCase();
|
||||
}
|
||||
private List<String> sortedCargoNames(String cargoName) {
|
||||
if (Func.isEmpty(cargoName)) return List.of();
|
||||
Collator collator = Collator.getInstance(Locale.CHINA);
|
||||
return Arrays.stream(cargoName.split("、"))
|
||||
.map(String::trim)
|
||||
.filter(Func::isNotEmpty)
|
||||
.sorted((left, right) -> {
|
||||
int initialResult = collator.compare(left.substring(0, 1), right.substring(0, 1));
|
||||
return initialResult == 0 ? collator.compare(left, right) : initialResult;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
private String firstNotEmpty(String... values) {
|
||||
for (String value : values) if (Func.isNotEmpty(value)) return value;
|
||||
return null;
|
||||
|
||||
+13
-3
@@ -233,9 +233,19 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
|
||||
@Override
|
||||
public List<WaybillExcel> exportWaybill(WaybillVO waybill, String ids) {
|
||||
LambdaQueryWrapper<Waybill> queryWrapper = buildQuery(waybill);
|
||||
if (Func.isNotEmpty(ids)) {
|
||||
queryWrapper.in(Waybill::getId, Func.toLongList(ids));
|
||||
TransportBusinessSupport.validateAllDept(waybill.getAllDept(), "运单管理");
|
||||
List<Long> idList = Func.toLongList(ids);
|
||||
LambdaQueryWrapper<Waybill> queryWrapper;
|
||||
if (Func.isNotEmpty(idList)) {
|
||||
queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
|
||||
if (!Objects.equals(waybill.getAllDept(), 1)) {
|
||||
queryWrapper.eq(Waybill::getDeptId, TransportBusinessSupport.currentDeptId("运单管理"));
|
||||
} else if (Func.isNotEmpty(waybill.getDeptId())) {
|
||||
queryWrapper.eq(Waybill::getDeptId, waybill.getDeptId());
|
||||
}
|
||||
queryWrapper.in(Waybill::getId, idList).orderByDesc(Waybill::getCreateTime);
|
||||
} else {
|
||||
queryWrapper = buildQuery(waybill);
|
||||
}
|
||||
return list(queryWrapper).stream().map(record -> {
|
||||
WaybillExcel excel = new WaybillExcel();
|
||||
|
||||
Reference in New Issue
Block a user