fix bug
This commit is contained in:
+15
@@ -55,5 +55,20 @@ public class ReceivablePayableAdjustFeeRequest implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "动态费用项目")
|
@Schema(description = "动态费用项目")
|
||||||
private Map<String, BigDecimal> feeItems;
|
private Map<String, BigDecimal> feeItems;
|
||||||
|
|
||||||
|
@Schema(description = "是否手工费用行")
|
||||||
|
private Boolean manualFee;
|
||||||
|
|
||||||
|
@Schema(description = "手工费用项目名称")
|
||||||
|
private String feeItemName;
|
||||||
|
|
||||||
|
@Schema(description = "手工费用类型:charge/deduct")
|
||||||
|
private String feeType;
|
||||||
|
|
||||||
|
@Schema(description = "手工费用金额")
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String remark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-3
@@ -24,12 +24,12 @@ package org.springblade.transport.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||||
|
import cn.idev.excel.FastExcel;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springblade.core.boot.ctrl.BladeController;
|
import org.springblade.core.boot.ctrl.BladeController;
|
||||||
import org.springblade.core.excel.util.ExcelUtil;
|
|
||||||
import org.springblade.core.mp.support.Condition;
|
import org.springblade.core.mp.support.Condition;
|
||||||
import org.springblade.core.mp.support.Query;
|
import org.springblade.core.mp.support.Query;
|
||||||
import org.springblade.core.secure.annotation.PreAuth;
|
import org.springblade.core.secure.annotation.PreAuth;
|
||||||
@@ -43,6 +43,7 @@ import org.springblade.transport.pojo.vo.ReceivablePayableChangeRecordVO;
|
|||||||
import org.springblade.transport.pojo.vo.ReceivablePayableDetailVO;
|
import org.springblade.transport.pojo.vo.ReceivablePayableDetailVO;
|
||||||
import org.springblade.transport.pojo.vo.ReceivablePayableFeeDetailVO;
|
import org.springblade.transport.pojo.vo.ReceivablePayableFeeDetailVO;
|
||||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
|
import org.springblade.system.cache.DictCache;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
@@ -51,7 +52,13 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应收应付明细控制器
|
* 应收应付明细控制器
|
||||||
@@ -160,7 +167,56 @@ public class ReceivablePayableDetailController extends BladeController {
|
|||||||
@ApiOperationSupport(order = 10)
|
@ApiOperationSupport(order = 10)
|
||||||
@Operation(summary = "导出应收应付明细")
|
@Operation(summary = "导出应收应付明细")
|
||||||
public void exportReceivablePayableDetail(ReceivablePayableDetailVO query, HttpServletResponse response) {
|
public void exportReceivablePayableDetail(ReceivablePayableDetailVO query, HttpServletResponse response) {
|
||||||
IPage<ReceivablePayableDetailVO> page = detailService.selectPage(Condition.getPage(new Query()), query);
|
List<ReceivablePayableDetailVO> records = detailService.selectList(query);
|
||||||
ExcelUtil.export(response, "应收应付明细" + DateUtil.time(), "应收应付明细", page.getRecords(), ReceivablePayableDetailVO.class);
|
Set<String> feeItemNames = new LinkedHashSet<>();
|
||||||
|
records.forEach(row -> {
|
||||||
|
if (row.getFeeItems() != null) feeItemNames.addAll(row.getFeeItems().keySet());
|
||||||
|
});
|
||||||
|
|
||||||
|
List<List<String>> head = new ArrayList<>();
|
||||||
|
String[] baseHeaders = {"单据号", "项目名称", "所属组织", "费用日期", "客商名称", "合同编号", "合同名称", "来源",
|
||||||
|
"预结算单号", "正式结算单号", "运单号", "车号", "运输类型", "货物名称", "货物类型", "运输总量", "里程(KM)",
|
||||||
|
"批次号", "运输单价"};
|
||||||
|
for (String header : baseHeaders) head.add(List.of(header));
|
||||||
|
feeItemNames.forEach(name -> head.add(List.of(name)));
|
||||||
|
for (String header : new String[] {"费用合计", "状态", "创建人", "创建时间"}) head.add(List.of(header));
|
||||||
|
|
||||||
|
List<List<Object>> rows = records.stream().map(row -> {
|
||||||
|
List<Object> values = new ArrayList<>();
|
||||||
|
values.add(row.getDocumentNo()); values.add(row.getProjectName()); values.add(row.getDeptName()); values.add(row.getFeeDate());
|
||||||
|
values.add(row.getCustomerName()); values.add(row.getContractNo()); values.add(row.getContractName()); values.add(row.getSourceType());
|
||||||
|
values.add(row.getPreSettlementNo()); values.add(row.getFormalSettlementNo()); values.add(row.getWaybillNo()); values.add(row.getVehicleNo());
|
||||||
|
values.add(transportTypeName(row.getTransportType())); values.add(row.getCargoName()); values.add(row.getCargoType());
|
||||||
|
values.add(row.getTransportQuantity()); values.add(row.getMileage() != null && row.getMileage().compareTo(BigDecimal.valueOf(-1)) == 0 ? null : row.getMileage());
|
||||||
|
values.add(row.getBatchNo()); values.add(money(row.getUnitPrice(), row.getCurrency()));
|
||||||
|
feeItemNames.forEach(name -> values.add(money(decimal(row.getFeeItems() == null ? null : row.getFeeItems().get(name)), row.getCurrency())));
|
||||||
|
values.add(money(row.getTotalAmount(), row.getCurrency())); values.add(row.getSettlementStatusName()); values.add(row.getCreateUserName()); values.add(row.getCreateTime());
|
||||||
|
return values;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("应收应付明细" + DateUtil.time(), StandardCharsets.UTF_8) + ".xlsx");
|
||||||
|
try {
|
||||||
|
FastExcel.write(response.getOutputStream()).head(head).sheet("应收应付明细").doWrite(rows);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new IllegalStateException("导出应收应付明细失败", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String transportTypeName(String value) {
|
||||||
|
if (value == null || value.isBlank()) return value;
|
||||||
|
String name = DictCache.getValue("transport_type", value);
|
||||||
|
return name == null || name.isBlank() ? value : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String money(BigDecimal value, String currency) {
|
||||||
|
if (value == null) return "-";
|
||||||
|
return value.setScale(2, java.math.RoundingMode.HALF_UP).toPlainString() + " " + (currency == null || currency.isBlank() ? "RMB" : currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal decimal(Object value) {
|
||||||
|
if (value == null || String.valueOf(value).isBlank()) return null;
|
||||||
|
try { return new BigDecimal(String.valueOf(value)); } catch (NumberFormatException ignored) { return null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -42,6 +42,9 @@ public interface ILoadingManageService extends BaseService<LoadingManage> {
|
|||||||
|
|
||||||
boolean complete(Long id);
|
boolean complete(Long id);
|
||||||
|
|
||||||
|
/** 运单完成后检查关联运单状态,全部完成时自动完成配载单。 */
|
||||||
|
boolean completeIfAllWaybillsCompleted(String loadingNo);
|
||||||
|
|
||||||
BusinessRemoveResultVO batchComplete(String ids);
|
BusinessRemoveResultVO batchComplete(String ids);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -28,6 +28,7 @@ import org.springblade.transport.pojo.dto.ReceivablePayableAdjustFeeRequest;
|
|||||||
import org.springblade.transport.pojo.dto.ReceivablePayableGenerateRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableGenerateRequest;
|
||||||
import org.springblade.transport.pojo.dto.ReceivablePayableTransferRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableTransferRequest;
|
||||||
import org.springblade.transport.pojo.dto.ReceivablePayableUpdateFeeRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableUpdateFeeRequest;
|
||||||
|
import org.springblade.transport.pojo.entity.MasterOrder;
|
||||||
import org.springblade.transport.pojo.entity.ReceivablePayableDetail;
|
import org.springblade.transport.pojo.entity.ReceivablePayableDetail;
|
||||||
import org.springblade.transport.pojo.vo.ReceivablePayableChangeRecordVO;
|
import org.springblade.transport.pojo.vo.ReceivablePayableChangeRecordVO;
|
||||||
import org.springblade.transport.pojo.vo.ReceivablePayableDetailVO;
|
import org.springblade.transport.pojo.vo.ReceivablePayableDetailVO;
|
||||||
@@ -45,6 +46,8 @@ public interface IReceivablePayableDetailService extends BaseService<ReceivableP
|
|||||||
|
|
||||||
IPage<ReceivablePayableDetailVO> selectPage(IPage<ReceivablePayableDetail> page, ReceivablePayableDetailVO query);
|
IPage<ReceivablePayableDetailVO> selectPage(IPage<ReceivablePayableDetail> page, ReceivablePayableDetailVO query);
|
||||||
|
|
||||||
|
List<ReceivablePayableDetailVO> selectList(ReceivablePayableDetailVO query);
|
||||||
|
|
||||||
ReceivablePayableFeeDetailVO feeDetail(Long id);
|
ReceivablePayableFeeDetailVO feeDetail(Long id);
|
||||||
|
|
||||||
IPage<ReceivablePayableChangeRecordVO> changeRecords(IPage<?> page, Long detailId);
|
IPage<ReceivablePayableChangeRecordVO> changeRecords(IPage<?> page, Long detailId);
|
||||||
@@ -69,4 +72,7 @@ public interface IReceivablePayableDetailService extends BaseService<ReceivableP
|
|||||||
|
|
||||||
/** 完成运单后按合同系统计费模式自动生成应收、应付明细。 */
|
/** 完成运单后按合同系统计费模式自动生成应收、应付明细。 */
|
||||||
void generateForCompletedWaybills(List<Long> waybillIds);
|
void generateForCompletedWaybills(List<Long> waybillIds);
|
||||||
|
|
||||||
|
/** 关闭总单调度后按合同系统计费模式自动生成总单应收、应付明细。 */
|
||||||
|
void generateForClosedMasterOrder(MasterOrder masterOrder);
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-5
@@ -263,8 +263,8 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
LoadingManage loadingManage = loadEditable(id, true);
|
LoadingManage loadingManage = loadEditable(id, true);
|
||||||
if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_RUNNING)
|
if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_RUNNING)
|
||||||
&& !(Objects.equals(loadingManage.getBusinessStatus(), STATUS_PENDING)
|
&& !(Objects.equals(loadingManage.getBusinessStatus(), STATUS_PENDING)
|
||||||
&& allAssociatedWaybillsRunning(loadingManage))) {
|
&& allAssociatedWaybillsCompletable(loadingManage))) {
|
||||||
throw new ServiceException("仅进行中状态允许完成");
|
throw new ServiceException("仅进行中,或关联运单全部进行中/已完成的待执行配载单允许完成");
|
||||||
}
|
}
|
||||||
loadingManage.setBusinessStatus(STATUS_COMPLETED);
|
loadingManage.setBusinessStatus(STATUS_COMPLETED);
|
||||||
boolean result = updateById(loadingManage);
|
boolean result = updateById(loadingManage);
|
||||||
@@ -272,6 +272,38 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean completeIfAllWaybillsCompleted(String loadingNo) {
|
||||||
|
if (Func.isEmpty(loadingNo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LoadingManage loadingManage = getOne(Wrappers.<LoadingManage>lambdaQuery()
|
||||||
|
.eq(LoadingManage::getLoadingNo, loadingNo)
|
||||||
|
.eq(LoadingManage::getIsDeleted, 0)
|
||||||
|
.last("FOR UPDATE"), false);
|
||||||
|
if (loadingManage == null
|
||||||
|
|| Objects.equals(loadingManage.getBusinessStatus(), STATUS_COMPLETED)
|
||||||
|
|| Objects.equals(loadingManage.getBusinessStatus(), STATUS_CANCELLED)
|
||||||
|
|| Objects.equals(loadingManage.getBusinessStatus(), STATUS_DRAFT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<Long> waybillIdList = waybillIds(loadingManage.getWaybillIdsJson());
|
||||||
|
if (Func.isEmpty(waybillIdList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<Waybill> waybillList = waybillMapper.selectList(Wrappers.<Waybill>lambdaQuery()
|
||||||
|
.eq(Waybill::getIsDeleted, 0)
|
||||||
|
.in(Waybill::getId, waybillIdList));
|
||||||
|
boolean allCompleted = waybillList.size() == waybillIdList.size()
|
||||||
|
&& waybillList.stream().allMatch(waybill -> Objects.equals(waybill.getBusinessStatus(), STATUS_COMPLETED));
|
||||||
|
if (!allCompleted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
loadingManage.setBusinessStatus(STATUS_COMPLETED);
|
||||||
|
return updateById(loadingManage);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public BusinessRemoveResultVO batchComplete(String ids) {
|
public BusinessRemoveResultVO batchComplete(String ids) {
|
||||||
@@ -292,7 +324,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean allAssociatedWaybillsRunning(LoadingManage loadingManage) {
|
private boolean allAssociatedWaybillsCompletable(LoadingManage loadingManage) {
|
||||||
List<Long> waybillIdList = waybillIds(loadingManage.getWaybillIdsJson());
|
List<Long> waybillIdList = waybillIds(loadingManage.getWaybillIdsJson());
|
||||||
if (Func.isEmpty(waybillIdList)) {
|
if (Func.isEmpty(waybillIdList)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -300,8 +332,11 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
|||||||
List<Waybill> waybillList = waybillMapper.selectList(Wrappers.<Waybill>lambdaQuery()
|
List<Waybill> waybillList = waybillMapper.selectList(Wrappers.<Waybill>lambdaQuery()
|
||||||
.eq(Waybill::getIsDeleted, 0)
|
.eq(Waybill::getIsDeleted, 0)
|
||||||
.in(Waybill::getId, waybillIdList));
|
.in(Waybill::getId, waybillIdList));
|
||||||
return waybillList.size() == waybillIdList.size()
|
if (waybillList.size() != waybillIdList.size()) {
|
||||||
&& waybillList.stream().allMatch(waybill -> Objects.equals(waybill.getBusinessStatus(), STATUS_RUNNING));
|
return false;
|
||||||
|
}
|
||||||
|
return waybillList.stream().allMatch(waybill -> Objects.equals(waybill.getBusinessStatus(), STATUS_RUNNING))
|
||||||
|
|| waybillList.stream().allMatch(waybill -> Objects.equals(waybill.getBusinessStatus(), STATUS_COMPLETED));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<LoadingManage> buildQuery(LoadingManageVO loadingManage) {
|
private LambdaQueryWrapper<LoadingManage> buildQuery(LoadingManageVO loadingManage) {
|
||||||
|
|||||||
+9
-2
@@ -24,6 +24,7 @@ import org.springblade.transport.pojo.vo.MasterOrderVO;
|
|||||||
import org.springblade.transport.service.IMasterOrderService;
|
import org.springblade.transport.service.IMasterOrderService;
|
||||||
import org.springblade.transport.service.IContractManageService;
|
import org.springblade.transport.service.IContractManageService;
|
||||||
import org.springblade.transport.service.IProjectApplyService;
|
import org.springblade.transport.service.IProjectApplyService;
|
||||||
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
import org.springblade.transport.service.ITransportPlanService;
|
import org.springblade.transport.service.ITransportPlanService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
@@ -53,13 +54,15 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
private final ITransportPlanService transportPlanService;
|
private final ITransportPlanService transportPlanService;
|
||||||
private final IProjectApplyService projectApplyService;
|
private final IProjectApplyService projectApplyService;
|
||||||
private final IContractManageService contractManageService;
|
private final IContractManageService contractManageService;
|
||||||
|
private final IReceivablePayableDetailService receivablePayableDetailService;
|
||||||
|
|
||||||
public MasterOrderServiceImpl(IWaybillService waybillService, ITransportPlanService transportPlanService, IProjectApplyService projectApplyService,
|
public MasterOrderServiceImpl(IWaybillService waybillService, ITransportPlanService transportPlanService, IProjectApplyService projectApplyService,
|
||||||
IContractManageService contractManageService) {
|
IContractManageService contractManageService, IReceivablePayableDetailService receivablePayableDetailService) {
|
||||||
this.waybillService = waybillService;
|
this.waybillService = waybillService;
|
||||||
this.transportPlanService = transportPlanService;
|
this.transportPlanService = transportPlanService;
|
||||||
this.projectApplyService = projectApplyService;
|
this.projectApplyService = projectApplyService;
|
||||||
this.contractManageService = contractManageService;
|
this.contractManageService = contractManageService;
|
||||||
|
this.receivablePayableDetailService = receivablePayableDetailService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -124,7 +127,11 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
|||||||
throw new ServiceException("当前状态不允许关闭调度");
|
throw new ServiceException("当前状态不允许关闭调度");
|
||||||
}
|
}
|
||||||
masterOrder.setBusinessStatus("closed");
|
masterOrder.setBusinessStatus("closed");
|
||||||
return updateById(masterOrder);
|
boolean updated = updateById(masterOrder);
|
||||||
|
if (updated) {
|
||||||
|
receivablePayableDetailService.generateForClosedMasterOrder(masterOrder);
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+14
-5
@@ -653,6 +653,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
Set<Long> existingSourceIds = existingDetails.stream().map(PreSettlementDetail::getSourceDetailId)
|
Set<Long> existingSourceIds = existingDetails.stream().map(PreSettlementDetail::getSourceDetailId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
List<Long> addedIds = distinctIds.stream().filter(id -> !existingSourceIds.contains(id)).toList();
|
List<Long> addedIds = distinctIds.stream().filter(id -> !existingSourceIds.contains(id)).toList();
|
||||||
|
List<ReceivablePayableDetail> addedSources = new ArrayList<>();
|
||||||
if (!addedIds.isEmpty()) {
|
if (!addedIds.isEmpty()) {
|
||||||
List<ReceivablePayableDetail> sources = sourceDetailMapper.selectBatchIds(addedIds);
|
List<ReceivablePayableDetail> sources = sourceDetailMapper.selectBatchIds(addedIds);
|
||||||
if (sources.size() != addedIds.size()) {
|
if (sources.size() != addedIds.size()) {
|
||||||
@@ -683,12 +684,20 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
if (affected != 1) {
|
if (affected != 1) {
|
||||||
throw new ServiceException("单据" + source.getDocumentNo() + "已被其他预结算单选择");
|
throw new ServiceException("单据" + source.getDocumentNo() + "已被其他预结算单选择");
|
||||||
}
|
}
|
||||||
saveChange(settlement.getId(), "结算明细项", null, "新增",
|
addedSources.add(source);
|
||||||
"新增单据号" + source.getDocumentNo(), "");
|
|
||||||
}
|
}
|
||||||
updateById(settlement);
|
updateById(settlement);
|
||||||
}
|
}
|
||||||
renumberDetails(settlement.getId());
|
renumberDetails(settlement.getId());
|
||||||
|
if (!addedSources.isEmpty()) {
|
||||||
|
Map<Long, Integer> detailLineMap = listDetails(settlement.getId()).stream()
|
||||||
|
.collect(Collectors.toMap(PreSettlementDetail::getSourceDetailId, PreSettlementDetail::getLineNo,
|
||||||
|
(left, right) -> left));
|
||||||
|
for (ReceivablePayableDetail source : addedSources) {
|
||||||
|
saveChange(settlement.getId(), "结算明细项", detailLineMap.get(source.getId()), "新增",
|
||||||
|
"新增单据号" + source.getDocumentNo(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCandidate(PreSettlement settlement, ReceivablePayableDetail source,
|
private void validateCandidate(PreSettlement settlement, ReceivablePayableDetail source,
|
||||||
@@ -883,7 +892,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
row.setManualFlag(1);
|
row.setManualFlag(1);
|
||||||
if (row.getId() == null) summaryFeeMapper.insert(row); else summaryFeeMapper.updateById(row);
|
if (row.getId() == null) summaryFeeMapper.insert(row); else summaryFeeMapper.updateById(row);
|
||||||
retainedManualIds.add(row.getId());
|
retainedManualIds.add(row.getId());
|
||||||
saveChange(settlementId, "合计费用项", row.getLineNo(), requestRow.getId() == null ? "新增" : "调整",
|
saveChange(settlementId, "合计费用项", null, requestRow.getId() == null ? "新增" : "调整",
|
||||||
row.getFeeItem() + "金额" + row.getSettlementAmount(), "");
|
row.getFeeItem() + "金额" + row.getSettlementAmount(), "");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -907,14 +916,14 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
|||||||
row.setRemark(limitRemark(requestRow.getRemark(), 50));
|
row.setRemark(limitRemark(requestRow.getRemark(), 50));
|
||||||
summaryFeeMapper.updateById(row);
|
summaryFeeMapper.updateById(row);
|
||||||
if (before.compareTo(row.getAdjustAmount()) != 0) {
|
if (before.compareTo(row.getAdjustAmount()) != 0) {
|
||||||
saveChange(settlementId, "合计费用项", row.getLineNo(), "调整",
|
saveChange(settlementId, "合计费用项", null, "调整",
|
||||||
"【调整金额】从【" + before + "】调整为【" + row.getAdjustAmount() + "】", "");
|
"【调整金额】从【" + before + "】调整为【" + row.getAdjustAmount() + "】", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (PreSettlementSummaryFee manualRow : manualRows) {
|
for (PreSettlementSummaryFee manualRow : manualRows) {
|
||||||
if (!retainedManualIds.contains(manualRow.getId())) {
|
if (!retainedManualIds.contains(manualRow.getId())) {
|
||||||
summaryFeeMapper.deleteById(manualRow.getId());
|
summaryFeeMapper.deleteById(manualRow.getId());
|
||||||
saveChange(settlementId, "合计费用项", manualRow.getLineNo(), "删除",
|
saveChange(settlementId, "合计费用项", null, "删除",
|
||||||
"删除" + manualRow.getFeeItem() + "费用" + manualRow.getSettlementAmount(), "");
|
"删除" + manualRow.getFeeItem() + "费用" + manualRow.getSettlementAmount(), "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+360
-13
@@ -36,6 +36,7 @@ import org.springblade.system.cache.UserCache;
|
|||||||
import org.springblade.transport.mapper.ReceivablePayableCargoFeeMapper;
|
import org.springblade.transport.mapper.ReceivablePayableCargoFeeMapper;
|
||||||
import org.springblade.transport.mapper.ReceivablePayableChangeRecordMapper;
|
import org.springblade.transport.mapper.ReceivablePayableChangeRecordMapper;
|
||||||
import org.springblade.transport.mapper.ReceivablePayableDetailMapper;
|
import org.springblade.transport.mapper.ReceivablePayableDetailMapper;
|
||||||
|
import org.springblade.transport.mapper.MasterOrderMapper;
|
||||||
import org.springblade.transport.pojo.dto.ReceivablePayableAdjustFeeRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableAdjustFeeRequest;
|
||||||
import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest;
|
import org.springblade.transport.pojo.dto.FormalSettlementSaveRequest;
|
||||||
import org.springblade.transport.pojo.dto.ReceivablePayableGenerateRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableGenerateRequest;
|
||||||
@@ -44,6 +45,7 @@ import org.springblade.transport.pojo.dto.ReceivablePayableTransferRequest;
|
|||||||
import org.springblade.transport.pojo.dto.ReceivablePayableUpdateFeeRequest;
|
import org.springblade.transport.pojo.dto.ReceivablePayableUpdateFeeRequest;
|
||||||
import org.springblade.transport.pojo.entity.ContractManage;
|
import org.springblade.transport.pojo.entity.ContractManage;
|
||||||
import org.springblade.transport.pojo.entity.CommonAddress;
|
import org.springblade.transport.pojo.entity.CommonAddress;
|
||||||
|
import org.springblade.transport.pojo.entity.MasterOrder;
|
||||||
import org.springblade.transport.pojo.entity.ReceivablePayableCargoFee;
|
import org.springblade.transport.pojo.entity.ReceivablePayableCargoFee;
|
||||||
import org.springblade.transport.pojo.entity.ReceivablePayableChangeRecord;
|
import org.springblade.transport.pojo.entity.ReceivablePayableChangeRecord;
|
||||||
import org.springblade.transport.pojo.entity.ReceivablePayableDetail;
|
import org.springblade.transport.pojo.entity.ReceivablePayableDetail;
|
||||||
@@ -91,8 +93,11 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
extends BaseServiceImpl<ReceivablePayableDetailMapper, ReceivablePayableDetail>
|
extends BaseServiceImpl<ReceivablePayableDetailMapper, ReceivablePayableDetail>
|
||||||
implements IReceivablePayableDetailService {
|
implements IReceivablePayableDetailService {
|
||||||
|
|
||||||
|
private static final String SOURCE_MASTER_ORDER = "总单系统生成";
|
||||||
|
|
||||||
private final ReceivablePayableCargoFeeMapper cargoFeeMapper;
|
private final ReceivablePayableCargoFeeMapper cargoFeeMapper;
|
||||||
private final ReceivablePayableChangeRecordMapper changeRecordMapper;
|
private final ReceivablePayableChangeRecordMapper changeRecordMapper;
|
||||||
|
private final MasterOrderMapper masterOrderMapper;
|
||||||
private final IWaybillService waybillService;
|
private final IWaybillService waybillService;
|
||||||
private final IContractManageService contractManageService;
|
private final IContractManageService contractManageService;
|
||||||
private final ICommonAddressService commonAddressService;
|
private final ICommonAddressService commonAddressService;
|
||||||
@@ -101,6 +106,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
|
|
||||||
public ReceivablePayableDetailServiceImpl(ReceivablePayableCargoFeeMapper cargoFeeMapper,
|
public ReceivablePayableDetailServiceImpl(ReceivablePayableCargoFeeMapper cargoFeeMapper,
|
||||||
ReceivablePayableChangeRecordMapper changeRecordMapper,
|
ReceivablePayableChangeRecordMapper changeRecordMapper,
|
||||||
|
MasterOrderMapper masterOrderMapper,
|
||||||
IWaybillService waybillService,
|
IWaybillService waybillService,
|
||||||
IContractManageService contractManageService,
|
IContractManageService contractManageService,
|
||||||
ICommonAddressService commonAddressService,
|
ICommonAddressService commonAddressService,
|
||||||
@@ -108,6 +114,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
@Lazy IFormalSettlementService formalSettlementService) {
|
@Lazy IFormalSettlementService formalSettlementService) {
|
||||||
this.cargoFeeMapper = cargoFeeMapper;
|
this.cargoFeeMapper = cargoFeeMapper;
|
||||||
this.changeRecordMapper = changeRecordMapper;
|
this.changeRecordMapper = changeRecordMapper;
|
||||||
|
this.masterOrderMapper = masterOrderMapper;
|
||||||
this.waybillService = waybillService;
|
this.waybillService = waybillService;
|
||||||
this.contractManageService = contractManageService;
|
this.contractManageService = contractManageService;
|
||||||
this.commonAddressService = commonAddressService;
|
this.commonAddressService = commonAddressService;
|
||||||
@@ -120,6 +127,11 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
return ReceivablePayableDetailWrapper.build().pageVO(page(page, buildQuery(query)));
|
return ReceivablePayableDetailWrapper.build().pageVO(page(page, buildQuery(query)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReceivablePayableDetailVO> selectList(ReceivablePayableDetailVO query) {
|
||||||
|
return ReceivablePayableDetailWrapper.build().listVO(list(buildQuery(query)));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReceivablePayableFeeDetailVO feeDetail(Long id) {
|
public ReceivablePayableFeeDetailVO feeDetail(Long id) {
|
||||||
ReceivablePayableDetail detail = getExisting(id);
|
ReceivablePayableDetail detail = getExisting(id);
|
||||||
@@ -127,6 +139,10 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
.eq(ReceivablePayableCargoFee::getDetailId, detail.getId())
|
.eq(ReceivablePayableCargoFee::getDetailId, detail.getId())
|
||||||
.eq(ReceivablePayableCargoFee::getIsDeleted, 0)
|
.eq(ReceivablePayableCargoFee::getIsDeleted, 0)
|
||||||
.orderByAsc(ReceivablePayableCargoFee::getCreateTime));
|
.orderByAsc(ReceivablePayableCargoFee::getCreateTime));
|
||||||
|
// 运输量的单位取明细对应运单单位,兼容历史费用行误存计费单位的情况。
|
||||||
|
if (Func.isNotEmpty(detail.getQuantityUnit())) {
|
||||||
|
rows.forEach(row -> row.setQuantityUnit(detail.getQuantityUnit()));
|
||||||
|
}
|
||||||
ReceivablePayableFeeDetailVO result = buildFeeDetail(rows);
|
ReceivablePayableFeeDetailVO result = buildFeeDetail(rows);
|
||||||
LinkedHashSet<String> feeItemNames = new LinkedHashSet<>(contractFeeItemNames(detail.getContractId()));
|
LinkedHashSet<String> feeItemNames = new LinkedHashSet<>(contractFeeItemNames(detail.getContractId()));
|
||||||
feeItemNames.addAll(result.getFeeItemNames());
|
feeItemNames.addAll(result.getFeeItemNames());
|
||||||
@@ -157,6 +173,9 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
if (Func.isEmpty(request.getContractId())) {
|
if (Func.isEmpty(request.getContractId())) {
|
||||||
throw new ServiceException("请选择需要更新费用的合同");
|
throw new ServiceException("请选择需要更新费用的合同");
|
||||||
}
|
}
|
||||||
|
if (Func.isEmpty(request.getBillingPlanId())) {
|
||||||
|
throw new ServiceException("请选择需要更新的合同计费方案");
|
||||||
|
}
|
||||||
List<ReceivablePayableDetail> details = list(buildUpdateQuery(request));
|
List<ReceivablePayableDetail> details = list(buildUpdateQuery(request));
|
||||||
if (Func.isEmpty(details)) {
|
if (Func.isEmpty(details)) {
|
||||||
throw new ServiceException("没有可更新的待结算明细");
|
throw new ServiceException("没有可更新的待结算明细");
|
||||||
@@ -217,14 +236,65 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
.eq(ReceivablePayableCargoFee::getIsDeleted, 0));
|
.eq(ReceivablePayableCargoFee::getIsDeleted, 0));
|
||||||
Map<Long, ReceivablePayableCargoFee> existingMap = existingRows.stream()
|
Map<Long, ReceivablePayableCargoFee> existingMap = existingRows.stream()
|
||||||
.collect(java.util.stream.Collectors.toMap(ReceivablePayableCargoFee::getId, row -> row));
|
.collect(java.util.stream.Collectors.toMap(ReceivablePayableCargoFee::getId, row -> row));
|
||||||
if (request.getRows().size() != existingRows.size()) {
|
Set<Long> submittedExistingIds = request.getRows().stream()
|
||||||
|
.map(ReceivablePayableAdjustFeeRequest.AdjustRow::getId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(java.util.stream.Collectors.toSet());
|
||||||
|
if (!submittedExistingIds.equals(existingMap.keySet())) {
|
||||||
throw new ServiceException("费用调整行数据不完整");
|
throw new ServiceException("费用调整行数据不完整");
|
||||||
}
|
}
|
||||||
Set<String> allowedFeeItems = new LinkedHashSet<>(contractFeeItemNames(detail.getContractId()));
|
Set<String> allowedFeeItems = new LinkedHashSet<>(contractFeeItemNames(detail.getContractId()));
|
||||||
existingRows.forEach(row -> allowedFeeItems.addAll(parseMap(row.getFeeItemsJson()).keySet()));
|
existingRows.forEach(row -> allowedFeeItems.addAll(parseMap(row.getFeeItemsJson()).keySet()));
|
||||||
List<String> changes = new ArrayList<>();
|
List<String> changes = new ArrayList<>();
|
||||||
|
List<ReceivablePayableCargoFee> allRows = new ArrayList<>(existingRows);
|
||||||
for (ReceivablePayableAdjustFeeRequest.AdjustRow adjusted : request.getRows()) {
|
for (ReceivablePayableAdjustFeeRequest.AdjustRow adjusted : request.getRows()) {
|
||||||
ReceivablePayableCargoFee existing = existingMap.get(adjusted.getId());
|
ReceivablePayableCargoFee existing = existingMap.get(adjusted.getId());
|
||||||
|
if (adjusted.getRemark() != null && adjusted.getRemark().length() > 200) {
|
||||||
|
throw new ServiceException("备注不能超过200个字");
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(adjusted.getManualFee())) {
|
||||||
|
if (Func.isEmpty(adjusted.getFeeItemName())) {
|
||||||
|
throw new ServiceException("请填写手工费用项目");
|
||||||
|
}
|
||||||
|
if (!List.of("charge", "deduct").contains(adjusted.getFeeType())) {
|
||||||
|
throw new ServiceException("手工费用类型不正确");
|
||||||
|
}
|
||||||
|
validateNonNegative(adjusted.getAmount(), "手工费用金额");
|
||||||
|
BigDecimal signedAmount = money(adjusted.getAmount())
|
||||||
|
.multiply("deduct".equals(adjusted.getFeeType()) ? BigDecimal.valueOf(-1) : BigDecimal.ONE);
|
||||||
|
Map<String, BigDecimal> manualItems = new LinkedHashMap<>();
|
||||||
|
manualItems.put(adjusted.getFeeItemName().trim(), signedAmount);
|
||||||
|
boolean newManualRow = existing == null;
|
||||||
|
if (newManualRow) {
|
||||||
|
existing = new ReceivablePayableCargoFee();
|
||||||
|
existing.setDetailId(detail.getId());
|
||||||
|
existing.setLineNo("ADJ-" + System.currentTimeMillis());
|
||||||
|
existing.setOriginalAmount(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
String oldName = existing.getCargoName();
|
||||||
|
BigDecimal oldAmount = money(existing.getAfterAmount());
|
||||||
|
// cargoName 表示运单货物名称,手工收费项名称仅保存到费用项目 JSON。
|
||||||
|
existing.setCargoName(detail.getCargoName());
|
||||||
|
existing.setBillingFactor("手工调整");
|
||||||
|
existing.setBillingType("deduct".equals(adjusted.getFeeType()) ? "手工扣费" : "手工收费");
|
||||||
|
existing.setTransportQuantity(detail.getTransportQuantity());
|
||||||
|
existing.setQuantityUnit(detail.getQuantityUnit());
|
||||||
|
existing.setMileage(detail.getMileage());
|
||||||
|
existing.setFreightAmount(BigDecimal.ZERO);
|
||||||
|
existing.setFeeItemsJson(JsonUtil.toJson(manualItems));
|
||||||
|
existing.setAdjustAmount(signedAmount.subtract(money(existing.getOriginalAmount())));
|
||||||
|
existing.setAfterAmount(signedAmount);
|
||||||
|
existing.setRemark(adjusted.getRemark());
|
||||||
|
if (newManualRow) {
|
||||||
|
cargoFeeMapper.insert(existing);
|
||||||
|
allRows.add(existing);
|
||||||
|
} else {
|
||||||
|
cargoFeeMapper.updateById(existing);
|
||||||
|
}
|
||||||
|
changes.add("【手工费用】从[" + oldName + " " + formatValue(oldAmount) + "]调整为["
|
||||||
|
+ existing.getBillingType() + " " + existing.getCargoName() + " " + formatValue(signedAmount) + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
throw new ServiceException("存在无效的费用调整行");
|
throw new ServiceException("存在无效的费用调整行");
|
||||||
}
|
}
|
||||||
@@ -244,6 +314,10 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
appendChange(changes, "计费数量", existing.getTransportQuantity(), adjusted.getTransportQuantity());
|
appendChange(changes, "计费数量", existing.getTransportQuantity(), adjusted.getTransportQuantity());
|
||||||
appendChange(changes, "里程", existing.getMileage(), adjusted.getMileage());
|
appendChange(changes, "里程", existing.getMileage(), adjusted.getMileage());
|
||||||
appendChange(changes, "运输费", existing.getFreightAmount(), adjusted.getFreightAmount());
|
appendChange(changes, "运输费", existing.getFreightAmount(), adjusted.getFreightAmount());
|
||||||
|
if (!Objects.equals(existing.getRemark(), adjusted.getRemark())) {
|
||||||
|
changes.add("【备注】从[" + Objects.toString(existing.getRemark(), "") + "]调整为["
|
||||||
|
+ Objects.toString(adjusted.getRemark(), "") + "]");
|
||||||
|
}
|
||||||
Map<String, Object> oldFeeItems = parseMap(existing.getFeeItemsJson());
|
Map<String, Object> oldFeeItems = parseMap(existing.getFeeItemsJson());
|
||||||
for (String name : allowedFeeItems) {
|
for (String name : allowedFeeItems) {
|
||||||
appendChange(changes, name, decimal(oldFeeItems.get(name)), money(feeItems.get(name)));
|
appendChange(changes, name, decimal(oldFeeItems.get(name)), money(feeItems.get(name)));
|
||||||
@@ -254,6 +328,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
existing.setMileage(money(adjusted.getMileage()));
|
existing.setMileage(money(adjusted.getMileage()));
|
||||||
existing.setFreightAmount(freightAmount);
|
existing.setFreightAmount(freightAmount);
|
||||||
existing.setFeeItemsJson(JsonUtil.toJson(feeItems));
|
existing.setFeeItemsJson(JsonUtil.toJson(feeItems));
|
||||||
|
existing.setRemark(adjusted.getRemark());
|
||||||
existing.setAdjustAmount(afterAmount.subtract(money(existing.getOriginalAmount())));
|
existing.setAdjustAmount(afterAmount.subtract(money(existing.getOriginalAmount())));
|
||||||
existing.setAfterAmount(afterAmount);
|
existing.setAfterAmount(afterAmount);
|
||||||
cargoFeeMapper.updateById(existing);
|
cargoFeeMapper.updateById(existing);
|
||||||
@@ -264,7 +339,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
for (int i = 0; i < changes.size(); i++) {
|
for (int i = 0; i < changes.size(); i++) {
|
||||||
saveChangeRecord(detail, changes.get(i), request.getAdjustReason(), String.format("%04d", i + 1));
|
saveChangeRecord(detail, changes.get(i), request.getAdjustReason(), String.format("%04d", i + 1));
|
||||||
}
|
}
|
||||||
refreshAdjustedDetail(detail, existingRows);
|
refreshAdjustedDetail(detail, allRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -420,6 +495,50 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW,
|
||||||
|
rollbackFor = Exception.class)
|
||||||
|
public void generateForClosedMasterOrder(MasterOrder masterOrder) {
|
||||||
|
if (masterOrder == null || Func.isEmpty(masterOrder.getId()) || Func.isEmpty(masterOrder.getContractId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContractManage contract = contractManageService.getById(masterOrder.getContractId());
|
||||||
|
if (contract == null || !isSystemGeneration(contract)) return;
|
||||||
|
try {
|
||||||
|
List<Waybill> masterGoods = masterOrderGoods(masterOrder);
|
||||||
|
if (masterGoods.isEmpty()) return;
|
||||||
|
List<ReceivablePayableCargoFee> matchedFees = new ArrayList<>();
|
||||||
|
for (Waybill masterGoodsItem : masterGoods) {
|
||||||
|
String planId = matchedPlanId(masterGoodsItem, contract);
|
||||||
|
if (Func.isEmpty(planId)) continue;
|
||||||
|
matchedFees.addAll(calculatedFees(masterGoodsItem, contract, planId, true));
|
||||||
|
}
|
||||||
|
if (matchedFees.isEmpty()) return;
|
||||||
|
normalizeMasterFeeLines(matchedFees);
|
||||||
|
BigDecimal contractUnitPrice = resolveContractUnitPrice(matchedFees);
|
||||||
|
for (String settlementType : List.of("payable", "receivable")) {
|
||||||
|
if (existsByMasterOrder(masterOrder.getMasterNo(), settlementType)) continue;
|
||||||
|
ReceivablePayableDetail detail = buildMasterOrderDetail(masterOrder, contract, masterGoods,
|
||||||
|
settlementType, matchedFees, contractUnitPrice);
|
||||||
|
save(detail);
|
||||||
|
for (ReceivablePayableCargoFee fee : matchedFees) {
|
||||||
|
ReceivablePayableCargoFee copy = BeanUtil.copyProperties(fee, ReceivablePayableCargoFee.class);
|
||||||
|
copy.setId(null);
|
||||||
|
copy.setDetailId(detail.getId());
|
||||||
|
copy.setWaybillId(null);
|
||||||
|
cargoFeeMapper.insert(copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.error("自动生成总单费用明细失败,masterOrderId:{}, masterNo:{}, contractId:{}, failureReason:{}",
|
||||||
|
masterOrder.getId(), masterOrder.getMasterNo(), masterOrder.getContractId(), exception.getMessage(), exception);
|
||||||
|
if (exception instanceof RuntimeException runtimeException) {
|
||||||
|
throw runtimeException;
|
||||||
|
}
|
||||||
|
throw new RuntimeException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isSystemGeneration(ContractManage contract) {
|
private boolean isSystemGeneration(ContractManage contract) {
|
||||||
if (Func.isNotEmpty(contract.getFeeGenerationMode())) {
|
if (Func.isNotEmpty(contract.getFeeGenerationMode())) {
|
||||||
return "system".equalsIgnoreCase(contract.getFeeGenerationMode()) || "系统生成".equals(contract.getFeeGenerationMode());
|
return "system".equalsIgnoreCase(contract.getFeeGenerationMode()) || "系统生成".equals(contract.getFeeGenerationMode());
|
||||||
@@ -525,9 +644,12 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
LambdaQueryWrapper<Waybill> wrapper = Wrappers.<Waybill>lambdaQuery()
|
LambdaQueryWrapper<Waybill> wrapper = Wrappers.<Waybill>lambdaQuery()
|
||||||
.eq(Waybill::getIsDeleted, 0)
|
.eq(Waybill::getIsDeleted, 0)
|
||||||
.eq(Waybill::getContractId, request.getContractId())
|
.eq(Waybill::getContractId, request.getContractId())
|
||||||
.eq(Waybill::getBusinessStatus, "completed")
|
.eq(Waybill::getBusinessStatus, "completed");
|
||||||
.notInSql(Waybill::getId, "select waybill_id from blade_receivable_payable_detail where is_deleted = 0"
|
String targetSettlementType = settlementType(request.getSettlementType());
|
||||||
+ (Func.isNotEmpty(request.getSettlementType()) ? " and settlement_type = '" + settlementType(request.getSettlementType()) + "'" : ""));
|
wrapper.notInSql(Waybill::getId,
|
||||||
|
"select waybill_id from blade_receivable_payable_detail"
|
||||||
|
+ " where is_deleted = 0 and waybill_id is not null and settlement_type = '"
|
||||||
|
+ targetSettlementType + "'");
|
||||||
if (Func.isNotEmpty(request.getBatchNo())) {
|
if (Func.isNotEmpty(request.getBatchNo())) {
|
||||||
wrapper.like(Waybill::getBatchNo, request.getBatchNo());
|
wrapper.like(Waybill::getBatchNo, request.getBatchNo());
|
||||||
}
|
}
|
||||||
@@ -553,7 +675,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ReceivablePayableDetail buildDetail(Waybill waybill, ContractManage contract, String settlementType,
|
private ReceivablePayableDetail buildDetail(Waybill waybill, ContractManage contract, String settlementType,
|
||||||
List<ReceivablePayableCargoFee> fees, BigDecimal unitPrice) {
|
List<ReceivablePayableCargoFee> fees, BigDecimal unitPrice) {
|
||||||
BigDecimal freight = fees.stream().filter(this::isFreight).map(ReceivablePayableCargoFee::getAfterAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal freight = fees.stream().filter(this::isFreight).map(ReceivablePayableCargoFee::getAfterAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
BigDecimal total = fees.stream().map(ReceivablePayableCargoFee::getAfterAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal total = fees.stream().map(ReceivablePayableCargoFee::getAfterAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
BigDecimal other = total.subtract(freight).setScale(2, RoundingMode.HALF_UP);
|
BigDecimal other = total.subtract(freight).setScale(2, RoundingMode.HALF_UP);
|
||||||
@@ -580,7 +702,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
detail.setCargoType(waybill.getCargoType());
|
detail.setCargoType(waybill.getCargoType());
|
||||||
detail.setTransportQuantity(waybill.getQuantity());
|
detail.setTransportQuantity(waybill.getQuantity());
|
||||||
detail.setQuantityUnit(waybill.getQuantityUnit());
|
detail.setQuantityUnit(waybill.getQuantityUnit());
|
||||||
detail.setMileage(waybill.getMileage());
|
detail.setMileage(normalizeGeneratedMileage(waybill.getMileage()));
|
||||||
detail.setBatchNo(waybill.getBatchNo());
|
detail.setBatchNo(waybill.getBatchNo());
|
||||||
detail.setUnitPrice(unitPrice);
|
detail.setUnitPrice(unitPrice);
|
||||||
detail.setCurrency("RMB");
|
detail.setCurrency("RMB");
|
||||||
@@ -593,6 +715,80 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
return detail;
|
return detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReceivablePayableDetail buildMasterOrderDetail(MasterOrder masterOrder, ContractManage contract,
|
||||||
|
List<Waybill> masterGoods, String settlementType,
|
||||||
|
List<ReceivablePayableCargoFee> fees, BigDecimal unitPrice) {
|
||||||
|
Waybill masterWaybill = masterGoods.get(0);
|
||||||
|
ReceivablePayableDetail detail = buildDetail(masterWaybill, contract, settlementType, fees, unitPrice);
|
||||||
|
detail.setSourceType(SOURCE_MASTER_ORDER);
|
||||||
|
detail.setWaybillId(null);
|
||||||
|
detail.setWaybillNo(masterOrder.getMasterNo());
|
||||||
|
detail.setVehicleNo(null);
|
||||||
|
detail.setTransportType(masterOrder.getTransportOrganizationType());
|
||||||
|
detail.setCargoName(joinMasterGoodsField(masterGoods, Waybill::getCargoName));
|
||||||
|
detail.setCargoType(joinMasterGoodsField(masterGoods, Waybill::getCargoType));
|
||||||
|
detail.setTransportQuantity(masterGoods.stream().map(Waybill::getQuantity)
|
||||||
|
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||||
|
detail.setQuantityUnit(commonMasterGoodsValue(masterGoods, Waybill::getQuantityUnit));
|
||||||
|
detail.setMileage(null);
|
||||||
|
detail.setBatchNo(null);
|
||||||
|
detail.setRemark(masterOrder.getRemark());
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Waybill> masterOrderGoods(MasterOrder masterOrder) {
|
||||||
|
List<Waybill> result = new ArrayList<>();
|
||||||
|
for (Map<String, Object> goods : parseList(masterOrder.getGoodsJson())) {
|
||||||
|
BigDecimal quantity = decimal(goods.get("quantity"));
|
||||||
|
if (quantity.compareTo(BigDecimal.ZERO) <= 0) continue;
|
||||||
|
Waybill masterGoodsItem = new Waybill();
|
||||||
|
masterGoodsItem.setProjectId(masterOrder.getProjectId());
|
||||||
|
masterGoodsItem.setProjectName(masterOrder.getProjectName());
|
||||||
|
masterGoodsItem.setContractId(masterOrder.getContractId());
|
||||||
|
masterGoodsItem.setContractName(masterOrder.getContractName());
|
||||||
|
masterGoodsItem.setCustomerName(masterOrder.getCustomerName());
|
||||||
|
masterGoodsItem.setTransportType(masterOrder.getTransportOrganizationType());
|
||||||
|
masterGoodsItem.setCargoName(stringValue(goods, "cargoName"));
|
||||||
|
masterGoodsItem.setCargoType(stringValue(goods, "cargoType"));
|
||||||
|
masterGoodsItem.setSpecification(stringValue(goods, "specification"));
|
||||||
|
masterGoodsItem.setModel(stringValue(goods, "model"));
|
||||||
|
masterGoodsItem.setQuantity(quantity);
|
||||||
|
masterGoodsItem.setQuantityUnit(stringValue(goods, "quantityUnit"));
|
||||||
|
masterGoodsItem.setDepartureName(masterOrder.getDepartureName());
|
||||||
|
masterGoodsItem.setDepartureAddress(masterOrder.getDepartureAddress());
|
||||||
|
masterGoodsItem.setArrivalName(masterOrder.getArrivalName());
|
||||||
|
masterGoodsItem.setArrivalAddress(masterOrder.getArrivalAddress());
|
||||||
|
masterGoodsItem.setEndDate(masterOrder.getPlanEndTime() == null ? LocalDate.now()
|
||||||
|
: masterOrder.getPlanEndTime().toLocalDate());
|
||||||
|
masterGoodsItem.setMasterNo(masterOrder.getMasterNo());
|
||||||
|
masterGoodsItem.setWaybillNo(masterOrder.getMasterNo());
|
||||||
|
masterGoodsItem.setGoodsJson(JsonUtil.toJson(goods));
|
||||||
|
masterGoodsItem.setRemark(masterOrder.getRemark());
|
||||||
|
result.add(masterGoodsItem);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeMasterFeeLines(List<ReceivablePayableCargoFee> fees) {
|
||||||
|
for (int index = 0; index < fees.size(); index++) {
|
||||||
|
ReceivablePayableCargoFee fee = fees.get(index);
|
||||||
|
fee.setWaybillId(null);
|
||||||
|
fee.setLineNo(String.format("%04d", index + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String joinMasterGoodsField(List<Waybill> masterGoods,
|
||||||
|
java.util.function.Function<Waybill, String> getter) {
|
||||||
|
return masterGoods.stream().map(getter).filter(Func::isNotEmpty).distinct()
|
||||||
|
.collect(java.util.stream.Collectors.joining(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String commonMasterGoodsValue(List<Waybill> masterGoods,
|
||||||
|
java.util.function.Function<Waybill, String> getter) {
|
||||||
|
List<String> values = masterGoods.stream().map(getter).filter(Func::isNotEmpty).distinct().toList();
|
||||||
|
return values.size() == 1 ? values.get(0) : "";
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal resolveContractUnitPrice(List<ReceivablePayableCargoFee> fees) {
|
private BigDecimal resolveContractUnitPrice(List<ReceivablePayableCargoFee> fees) {
|
||||||
return fees.stream()
|
return fees.stream()
|
||||||
.filter(this::isFreight)
|
.filter(this::isFreight)
|
||||||
@@ -631,7 +827,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
cargoFee.setQuantityUnit(waybill.getQuantityUnit());
|
cargoFee.setQuantityUnit(waybill.getQuantityUnit());
|
||||||
cargoFee.setPriceUnit(waybill.getPriceUnit());
|
cargoFee.setPriceUnit(waybill.getPriceUnit());
|
||||||
cargoFee.setUnitPrice(unitPrice);
|
cargoFee.setUnitPrice(unitPrice);
|
||||||
cargoFee.setMileage(waybill.getMileage());
|
cargoFee.setMileage(normalizeGeneratedMileage(waybill.getMileage()));
|
||||||
cargoFee.setFreightAmount(freightAmount);
|
cargoFee.setFreightAmount(freightAmount);
|
||||||
cargoFee.setFeeItemsJson(JsonUtil.toJson(feeItems));
|
cargoFee.setFeeItemsJson(JsonUtil.toJson(feeItems));
|
||||||
cargoFee.setOriginalAmount(total);
|
cargoFee.setOriginalAmount(total);
|
||||||
@@ -647,9 +843,7 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
|
|
||||||
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, ContractManage contract, String planId, boolean matchOnly) {
|
private List<ReceivablePayableCargoFee> calculatedFees(Waybill waybill, ContractManage contract, String planId, boolean matchOnly) {
|
||||||
List<Map<String, Object>> plans = parseList(contract == null ? null : contract.getBillingPlanJson());
|
List<Map<String, Object>> plans = parseList(contract == null ? null : contract.getBillingPlanJson());
|
||||||
Map<String, Object> plan = "__matched__".equals(planId) ? plans.stream().filter(this::isDefaultPlan).findFirst().orElseGet(() -> plans.isEmpty() ? null : plans.get(plans.size() - 1)) : plans.stream().filter(item -> Objects.equals(stringValue(item, "id"), planId)
|
Map<String, Object> plan = resolveBillingPlan(plans, planId);
|
||||||
|| Objects.equals(stringValue(item, "planId"), planId)).findFirst()
|
|
||||||
.orElseGet(() -> plans.stream().filter(this::isDefaultPlan).findFirst().orElse(null));
|
|
||||||
if (plan == null || !(plan.get("rules") instanceof List<?>)) return matchOnly ? List.of() : List.of(buildCargoFee(null, waybill));
|
if (plan == null || !(plan.get("rules") instanceof List<?>)) return matchOnly ? List.of() : List.of(buildCargoFee(null, waybill));
|
||||||
List<ReceivablePayableCargoFee> result = new ArrayList<>();
|
List<ReceivablePayableCargoFee> result = new ArrayList<>();
|
||||||
int line = 1;
|
int line = 1;
|
||||||
@@ -664,9 +858,9 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
fee.setWaybillId(waybill.getId()); fee.setLineNo(String.format("%04d", line++));
|
fee.setWaybillId(waybill.getId()); fee.setLineNo(String.format("%04d", line++));
|
||||||
fee.setCargoName(stringValue(rule, "feeItem", "费用")); fee.setCargoType(waybill.getCargoType());
|
fee.setCargoName(stringValue(rule, "feeItem", "费用")); fee.setCargoType(waybill.getCargoType());
|
||||||
fee.setBillingFactor(stringValue(rule, "billingElement", "")); fee.setBillingType(stringValue(rule, "billingType", ""));
|
fee.setBillingFactor(stringValue(rule, "billingElement", "")); fee.setBillingType(stringValue(rule, "billingType", ""));
|
||||||
fee.setTransportQuantity(measure(rule, waybill)); fee.setQuantityUnit(stringValue(rule, "billingUnit", waybill.getQuantityUnit()));
|
fee.setTransportQuantity(measure(rule, waybill)); fee.setQuantityUnit(waybill.getQuantityUnit());
|
||||||
fee.setPriceUnit(stringValue(rule, "billingUnit", waybill.getPriceUnit())); fee.setUnitPrice(decimal(rule.get("unitPrice")));
|
fee.setPriceUnit(stringValue(rule, "billingUnit", waybill.getPriceUnit())); fee.setUnitPrice(decimal(rule.get("unitPrice")));
|
||||||
fee.setMileage(waybill.getMileage()); fee.setFreightAmount(isFreightRule(rule) ? amount : BigDecimal.ZERO);
|
fee.setMileage(normalizeGeneratedMileage(waybill.getMileage())); fee.setFreightAmount(isFreightRule(rule) ? amount : BigDecimal.ZERO);
|
||||||
fee.setFeeItemsJson(JsonUtil.toJson(Map.of(fee.getCargoName(), amount)));
|
fee.setFeeItemsJson(JsonUtil.toJson(Map.of(fee.getCargoName(), amount)));
|
||||||
fee.setOriginalAmount(amount); fee.setAdjustAmount(BigDecimal.ZERO); fee.setAfterAmount(amount); fee.setRemark(stringValue(rule, "remark", waybill.getRemark()));
|
fee.setOriginalAmount(amount); fee.setAdjustAmount(BigDecimal.ZERO); fee.setAfterAmount(amount); fee.setRemark(stringValue(rule, "remark", waybill.getRemark()));
|
||||||
result.add(fee);
|
result.add(fee);
|
||||||
@@ -674,6 +868,24 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
return result.isEmpty() && !matchOnly ? List.of(buildCargoFee(null, waybill)) : result;
|
return result.isEmpty() && !matchOnly ? List.of(buildCargoFee(null, waybill)) : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> resolveBillingPlan(List<Map<String, Object>> plans, String planId) {
|
||||||
|
if ("__matched__".equals(planId)) {
|
||||||
|
return plans.stream().filter(this::isDefaultPlan).findFirst()
|
||||||
|
.orElseGet(() -> plans.isEmpty() ? null : plans.get(plans.size() - 1));
|
||||||
|
}
|
||||||
|
if (Func.isEmpty(planId)) {
|
||||||
|
return plans.stream().filter(this::isDefaultPlan).findFirst()
|
||||||
|
.orElseGet(() -> plans.isEmpty() ? null : plans.get(plans.size() - 1));
|
||||||
|
}
|
||||||
|
return plans.stream().filter(plan -> Objects.equals(stringValue(plan, "id"), planId)
|
||||||
|
|| Objects.equals(stringValue(plan, "planId"), planId)
|
||||||
|
|| Objects.equals(stringValue(plan, "name"), planId)
|
||||||
|
|| Objects.equals(stringValue(plan, "planName"), planId)
|
||||||
|
|| Objects.equals(stringValue(plan, "billingPlanName"), planId))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new ServiceException("合同计费方案不存在或已变更,请重新选择"));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isDefaultPlan(Map<String, Object> plan) {
|
private boolean isDefaultPlan(Map<String, Object> plan) {
|
||||||
Object value = plan.get("defaultPlan");
|
Object value = plan.get("defaultPlan");
|
||||||
return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value));
|
return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value));
|
||||||
@@ -851,6 +1063,10 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void rebuildDetailFee(ReceivablePayableDetail detail, String billingPlanId) {
|
private void rebuildDetailFee(ReceivablePayableDetail detail, String billingPlanId) {
|
||||||
|
if (SOURCE_MASTER_ORDER.equals(detail.getSourceType())) {
|
||||||
|
rebuildMasterOrderDetailFee(detail, billingPlanId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Waybill waybill = waybillService.getById(detail.getWaybillId());
|
Waybill waybill = waybillService.getById(detail.getWaybillId());
|
||||||
if (waybill == null) {
|
if (waybill == null) {
|
||||||
throw new ServiceException("关联运单不存在");
|
throw new ServiceException("关联运单不存在");
|
||||||
@@ -869,6 +1085,38 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
updateById(detail);
|
updateById(detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void rebuildMasterOrderDetailFee(ReceivablePayableDetail detail, String billingPlanId) {
|
||||||
|
MasterOrder masterOrder = masterOrderMapper.selectOne(Wrappers.<MasterOrder>lambdaQuery()
|
||||||
|
.eq(MasterOrder::getMasterNo, detail.getWaybillNo())
|
||||||
|
.eq(MasterOrder::getIsDeleted, 0));
|
||||||
|
if (masterOrder == null) {
|
||||||
|
throw new ServiceException("关联总单不存在");
|
||||||
|
}
|
||||||
|
ContractManage contract = contractManageService.getById(detail.getContractId());
|
||||||
|
List<Waybill> masterGoods = masterOrderGoods(masterOrder);
|
||||||
|
List<ReceivablePayableCargoFee> fees = masterGoods.stream()
|
||||||
|
.flatMap(goods -> calculatedFees(goods, contract, billingPlanId).stream()).toList();
|
||||||
|
normalizeMasterFeeLines(fees);
|
||||||
|
cargoFeeMapper.delete(Wrappers.<ReceivablePayableCargoFee>lambdaQuery()
|
||||||
|
.eq(ReceivablePayableCargoFee::getDetailId, detail.getId()));
|
||||||
|
fees.forEach(fee -> {
|
||||||
|
fee.setDetailId(detail.getId());
|
||||||
|
fee.setWaybillId(null);
|
||||||
|
cargoFeeMapper.insert(fee);
|
||||||
|
});
|
||||||
|
BigDecimal freight = fees.stream().filter(this::isFreight)
|
||||||
|
.map(ReceivablePayableCargoFee::getAfterAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
BigDecimal total = fees.stream().map(ReceivablePayableCargoFee::getAfterAmount)
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
detail.setFreightAmount(freight);
|
||||||
|
detail.setOtherFeeAmount(total.subtract(freight));
|
||||||
|
detail.setTotalAmount(total);
|
||||||
|
detail.setUnitPrice(resolveContractUnitPrice(fees));
|
||||||
|
detail.setFeeItemsJson(JsonUtil.toJson(fees.stream().collect(HashMap<String, BigDecimal>::new,
|
||||||
|
(map, fee) -> map.put(fee.getCargoName(), fee.getAfterAmount()), HashMap::putAll)));
|
||||||
|
updateById(detail);
|
||||||
|
}
|
||||||
|
|
||||||
private void closeDetails(List<Long> ids, String settlementType) {
|
private void closeDetails(List<Long> ids, String settlementType) {
|
||||||
if (Func.isEmpty(ids)) {
|
if (Func.isEmpty(ids)) {
|
||||||
throw new ServiceException("请选择需要关闭的明细");
|
throw new ServiceException("请选择需要关闭的明细");
|
||||||
@@ -927,6 +1175,14 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
.eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0;
|
.eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean existsByMasterOrder(String masterNo, String settlementType) {
|
||||||
|
return count(Wrappers.<ReceivablePayableDetail>lambdaQuery()
|
||||||
|
.eq(ReceivablePayableDetail::getSourceType, SOURCE_MASTER_ORDER)
|
||||||
|
.eq(ReceivablePayableDetail::getWaybillNo, masterNo)
|
||||||
|
.eq(ReceivablePayableDetail::getSettlementType, settlementType)
|
||||||
|
.eq(ReceivablePayableDetail::getIsDeleted, 0)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private String settlementType(String value) {
|
private String settlementType(String value) {
|
||||||
if (Func.isEmpty(value)) return "receivable";
|
if (Func.isEmpty(value)) return "receivable";
|
||||||
if (!List.of("receivable", "payable").contains(value)) {
|
if (!List.of("receivable", "payable").contains(value)) {
|
||||||
@@ -960,9 +1216,96 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
map.put("transportType", waybill.getTransportType());
|
map.put("transportType", waybill.getTransportType());
|
||||||
map.put("carrierType", waybill.getCarrierType());
|
map.put("carrierType", waybill.getCarrierType());
|
||||||
map.put("cargoInfo", waybill.getCargoName());
|
map.put("cargoInfo", waybill.getCargoName());
|
||||||
|
map.put("departureAddress", waybill.getDepartureAddress());
|
||||||
|
map.put("departureContact", waybill.getDepartureContact());
|
||||||
|
map.put("arrivalAddress", waybill.getArrivalAddress());
|
||||||
|
map.put("arrivalContact", waybill.getArrivalContact());
|
||||||
|
map.put("unitPrice", waybill.getUnitPrice());
|
||||||
|
map.put("freight", waybillFreight(waybill));
|
||||||
|
map.put("otherFeeTotal", waybillOtherFee(waybill));
|
||||||
|
map.put("freightTotal", waybillFreightTotal(waybill));
|
||||||
|
map.put("contractName", waybill.getContractName());
|
||||||
|
map.put("planName", waybill.getPlanName());
|
||||||
|
map.put("batchNo", waybill.getBatchNo());
|
||||||
|
map.put("originalNo", waybill.getOriginalNo());
|
||||||
|
map.put("masterNo", waybill.getMasterNo());
|
||||||
|
map.put("remark", Func.isNotEmpty(waybill.getRemark()) ? waybill.getRemark() : waybill.getTaskRemark());
|
||||||
|
map.put("createTime", waybill.getCreateTime());
|
||||||
|
map.put("updateTime", waybill.getUpdateTime());
|
||||||
|
map.put("businessStatusName", waybillStatusName(waybill.getBusinessStatus()));
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal waybillFreight(Waybill waybill) {
|
||||||
|
Map<String, Object> freight = waybillFreightMap(waybill);
|
||||||
|
Object value = firstValue(freight, "freight", "freightAmount", "transportFee");
|
||||||
|
if (value != null) return decimal(value);
|
||||||
|
if (freight.get("freightItems") instanceof List<?> items) {
|
||||||
|
BigDecimal total = BigDecimal.ZERO;
|
||||||
|
boolean hasAmount = false;
|
||||||
|
for (Object item : items) {
|
||||||
|
if (!(item instanceof Map<?, ?> source)) continue;
|
||||||
|
Object amount = firstValue(stringMap(source), "freightAmount", "amount", "totalAmount");
|
||||||
|
if (amount == null) continue;
|
||||||
|
total = total.add(decimal(amount));
|
||||||
|
hasAmount = true;
|
||||||
|
}
|
||||||
|
if (hasAmount) return total;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal waybillOtherFee(Waybill waybill) {
|
||||||
|
Map<String, Object> freight = waybillFreightMap(waybill);
|
||||||
|
Object value = firstValue(freight, "otherFeeTotal", "otherFreightAmount", "otherAmount");
|
||||||
|
return value == null ? waybill.getOtherFeeTotal() : decimal(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal waybillFreightTotal(Waybill waybill) {
|
||||||
|
Map<String, Object> freight = waybillFreightMap(waybill);
|
||||||
|
Object total = firstValue(freight, "freightTotal", "totalFreight", "totalFreightAmount", "totalAmount");
|
||||||
|
if (total != null) return decimal(total);
|
||||||
|
BigDecimal freightAmount = waybillFreight(waybill);
|
||||||
|
BigDecimal otherFeeAmount = waybillOtherFee(waybill);
|
||||||
|
if (freightAmount == null && otherFeeAmount == null) return null;
|
||||||
|
return money(freightAmount).add(money(otherFeeAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> waybillFreightMap(Waybill waybill) {
|
||||||
|
if (Func.isEmpty(waybill.getFreightJson())) return new LinkedHashMap<>();
|
||||||
|
try {
|
||||||
|
Object parsed = JsonUtil.parse(waybill.getFreightJson(), Object.class);
|
||||||
|
if (parsed instanceof Map<?, ?> source) return stringMap(source);
|
||||||
|
if (parsed instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Map<?, ?> source) {
|
||||||
|
return stringMap(source);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// 兼容历史费用 JSON 异常数据。
|
||||||
|
}
|
||||||
|
return new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> stringMap(Map<?, ?> source) {
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>();
|
||||||
|
source.forEach((key, value) -> result.put(String.valueOf(key), value));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object firstValue(Map<String, Object> map, String... keys) {
|
||||||
|
for (String key : keys) if (map.get(key) != null && !String.valueOf(map.get(key)).isBlank()) return map.get(key);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String waybillStatusName(String status) {
|
||||||
|
return switch (status == null ? "" : status) {
|
||||||
|
case "completed" -> "已完成";
|
||||||
|
case "processing", "running", "in_progress", "inProgress" -> "进行中";
|
||||||
|
case "pending", "created" -> "待执行";
|
||||||
|
case "cancelled", "canceled" -> "已取消";
|
||||||
|
default -> status;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> candidateMap(ReceivablePayableDetailVO detail) {
|
private Map<String, Object> candidateMap(ReceivablePayableDetailVO detail) {
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
map.put("id", detail.getId());
|
map.put("id", detail.getId());
|
||||||
@@ -1038,6 +1381,10 @@ public class ReceivablePayableDetailServiceImpl
|
|||||||
return value == null ? BigDecimal.ZERO : value.setScale(2, RoundingMode.HALF_UP);
|
return value == null ? BigDecimal.ZERO : value.setScale(2, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal normalizeGeneratedMileage(BigDecimal mileage) {
|
||||||
|
return mileage != null && mileage.compareTo(BigDecimal.valueOf(-1)) == 0 ? null : mileage;
|
||||||
|
}
|
||||||
|
|
||||||
private String formatMoney(BigDecimal value) {
|
private String formatMoney(BigDecimal value) {
|
||||||
return money(value).toPlainString();
|
return money(value).toPlainString();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -425,7 +425,7 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl<ShippingTemplat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private synchronized String nextCode() {
|
private synchronized String nextCode() {
|
||||||
String prefix = "MBJH-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
String prefix = "MB-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||||
List<ShippingTemplate> latestList = list(Wrappers.<ShippingTemplate>lambdaQuery()
|
List<ShippingTemplate> latestList = list(Wrappers.<ShippingTemplate>lambdaQuery()
|
||||||
.select(ShippingTemplate::getTemplateCode)
|
.select(ShippingTemplate::getTemplateCode)
|
||||||
.likeRight(ShippingTemplate::getTemplateCode, prefix)
|
.likeRight(ShippingTemplate::getTemplateCode, prefix)
|
||||||
|
|||||||
+51
-3
@@ -37,11 +37,13 @@ import java.time.LocalDate;
|
|||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import org.springblade.transport.mapper.WaybillMapper;
|
import org.springblade.transport.mapper.WaybillMapper;
|
||||||
import org.springblade.transport.pojo.entity.LoadingManage;
|
import org.springblade.transport.pojo.entity.LoadingManage;
|
||||||
|
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||||
import org.springblade.transport.pojo.entity.Waybill;
|
import org.springblade.transport.pojo.entity.Waybill;
|
||||||
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
import org.springblade.transport.pojo.vo.BusinessRemoveResultVO;
|
||||||
import org.springblade.transport.pojo.vo.LoadingManageVO;
|
import org.springblade.transport.pojo.vo.LoadingManageVO;
|
||||||
import org.springblade.transport.pojo.vo.WaybillVO;
|
import org.springblade.transport.pojo.vo.WaybillVO;
|
||||||
import org.springblade.transport.service.ILoadingManageService;
|
import org.springblade.transport.service.ILoadingManageService;
|
||||||
|
import org.springblade.transport.service.IProcessConfigService;
|
||||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||||
import org.springblade.transport.service.IWaybillService;
|
import org.springblade.transport.service.IWaybillService;
|
||||||
import org.springblade.transport.support.TransportBusinessSupport;
|
import org.springblade.transport.support.TransportBusinessSupport;
|
||||||
@@ -72,6 +74,9 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
@jakarta.annotation.Resource
|
@jakarta.annotation.Resource
|
||||||
private ILoadingManageService loadingManageService;
|
private ILoadingManageService loadingManageService;
|
||||||
|
|
||||||
|
@jakarta.annotation.Resource
|
||||||
|
private IProcessConfigService processConfigService;
|
||||||
|
|
||||||
@jakarta.annotation.Resource
|
@jakarta.annotation.Resource
|
||||||
@org.springframework.context.annotation.Lazy
|
@org.springframework.context.annotation.Lazy
|
||||||
private IReceivablePayableDetailService receivablePayableDetailService;
|
private IReceivablePayableDetailService receivablePayableDetailService;
|
||||||
@@ -93,9 +98,14 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
boolean created = Func.isEmpty(waybill.getId());
|
boolean created = Func.isEmpty(waybill.getId());
|
||||||
if (!created) {
|
if (!created) {
|
||||||
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
||||||
|
assertNotLoaded(oldRecord);
|
||||||
waybill.setWaybillNo(oldRecord.getWaybillNo());
|
waybill.setWaybillNo(oldRecord.getWaybillNo());
|
||||||
|
waybill.setLoadingNo(oldRecord.getLoadingNo());
|
||||||
waybill.setDeptId(oldRecord.getDeptId());
|
waybill.setDeptId(oldRecord.getDeptId());
|
||||||
waybill.setDeptName(oldRecord.getDeptName());
|
waybill.setDeptName(oldRecord.getDeptName());
|
||||||
|
} else {
|
||||||
|
waybill.setLoadingNo(null);
|
||||||
|
fillProjectProcessConfig(waybill);
|
||||||
}
|
}
|
||||||
prepare(waybill);
|
prepare(waybill);
|
||||||
if (created && Func.isEmpty(waybill.getWaybillNo())) {
|
if (created && Func.isEmpty(waybill.getWaybillNo())) {
|
||||||
@@ -105,6 +115,33 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
return saveOrUpdate(waybill);
|
return saveOrUpdate(waybill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillProjectProcessConfig(Waybill waybill) {
|
||||||
|
if (Func.isNotEmpty(waybill.getProcessJson()) || Func.isEmpty(waybill.getProjectId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String projectId = String.valueOf(waybill.getProjectId());
|
||||||
|
processConfigService.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||||
|
.eq(ProcessConfig::getStatus, 1)
|
||||||
|
.eq(ProcessConfig::getIsDeleted, 0)
|
||||||
|
.like(ProcessConfig::getProjectIds, projectId)
|
||||||
|
.orderByDesc(ProcessConfig::getCreateTime))
|
||||||
|
.stream()
|
||||||
|
.filter(processConfig -> containsProjectId(processConfig.getProjectIds(), projectId))
|
||||||
|
.map(ProcessConfig::getNodeConfigJson)
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(waybill::setProcessJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsProjectId(String projectIds, String projectId) {
|
||||||
|
if (Func.isEmpty(projectIds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return List.of(projectIds.split(",")).stream()
|
||||||
|
.map(String::trim)
|
||||||
|
.anyMatch(projectId::equals);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public BusinessRemoveResultVO removeWaybill(String ids) {
|
public BusinessRemoveResultVO removeWaybill(String ids) {
|
||||||
@@ -223,20 +260,21 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
target.setPlanId(source.getPlanId());
|
target.setPlanId(source.getPlanId());
|
||||||
target.setPlanName(source.getPlanName());
|
target.setPlanName(source.getPlanName());
|
||||||
target.setMasterNo(source.getMasterNo());
|
target.setMasterNo(source.getMasterNo());
|
||||||
target.setLoadingNo(source.getLoadingNo());
|
target.setLoadingNo(null);
|
||||||
target.setBatchNo(source.getBatchNo());
|
target.setBatchNo(source.getBatchNo());
|
||||||
target.setRelationNo(source.getRelationNo());
|
target.setRelationNo(source.getRelationNo());
|
||||||
target.setCurrentProcessNode(source.getCurrentProcessNode());
|
target.setCurrentProcessNode(source.getCurrentProcessNode());
|
||||||
target.setGoodsJson(source.getGoodsJson());
|
target.setGoodsJson(source.getGoodsJson());
|
||||||
target.setCarrierJson(source.getCarrierJson());
|
target.setCarrierJson(source.getCarrierJson());
|
||||||
target.setTaskInfoJson(source.getTaskInfoJson());
|
target.setTaskInfoJson(source.getTaskInfoJson());
|
||||||
target.setProcessJson(source.getProcessJson());
|
target.setProcessJson(null);
|
||||||
target.setRouteJson(source.getRouteJson());
|
target.setRouteJson(source.getRouteJson());
|
||||||
target.setFreightJson(source.getFreightJson());
|
target.setFreightJson(source.getFreightJson());
|
||||||
target.setAttachmentsJson(source.getAttachmentsJson());
|
target.setAttachmentsJson(source.getAttachmentsJson());
|
||||||
target.setRemark(source.getRemark());
|
target.setRemark(source.getRemark());
|
||||||
target.setBusinessStatus("pending");
|
target.setBusinessStatus("pending");
|
||||||
target.setWaybillNo(nextCode());
|
target.setWaybillNo(nextCode());
|
||||||
|
fillProjectProcessConfig(target);
|
||||||
prepare(target);
|
prepare(target);
|
||||||
validate(target);
|
validate(target);
|
||||||
save(target);
|
save(target);
|
||||||
@@ -247,6 +285,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean changeRoute(Waybill waybill) {
|
public boolean changeRoute(Waybill waybill) {
|
||||||
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
||||||
|
assertNotLoaded(oldRecord);
|
||||||
if ("completed".equals(oldRecord.getBusinessStatus()) || "cancelled".equals(oldRecord.getBusinessStatus())) {
|
if ("completed".equals(oldRecord.getBusinessStatus()) || "cancelled".equals(oldRecord.getBusinessStatus())) {
|
||||||
throw new ServiceException("当前运单状态不允许变更运输路线");
|
throw new ServiceException("当前运单状态不允许变更运输路线");
|
||||||
}
|
}
|
||||||
@@ -265,6 +304,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean cancel(Long id) {
|
public boolean cancel(Long id) {
|
||||||
Waybill waybill = loadEditable(id, true);
|
Waybill waybill = loadEditable(id, true);
|
||||||
|
assertNotLoaded(waybill);
|
||||||
if ("completed".equals(waybill.getBusinessStatus()) || "cancelled".equals(waybill.getBusinessStatus())) {
|
if ("completed".equals(waybill.getBusinessStatus()) || "cancelled".equals(waybill.getBusinessStatus())) {
|
||||||
throw new ServiceException("当前状态不允许取消");
|
throw new ServiceException("当前状态不允许取消");
|
||||||
}
|
}
|
||||||
@@ -276,6 +316,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean reassign(Long id) {
|
public boolean reassign(Long id) {
|
||||||
Waybill waybill = loadEditable(id, true);
|
Waybill waybill = loadEditable(id, true);
|
||||||
|
assertNotLoaded(waybill);
|
||||||
if (!"pending".equals(waybill.getBusinessStatus())) {
|
if (!"pending".equals(waybill.getBusinessStatus())) {
|
||||||
throw new ServiceException("仅待执行运单允许重新派单");
|
throw new ServiceException("仅待执行运单允许重新派单");
|
||||||
}
|
}
|
||||||
@@ -294,6 +335,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
boolean updated = updateById(waybill);
|
boolean updated = updateById(waybill);
|
||||||
if (updated) {
|
if (updated) {
|
||||||
receivablePayableDetailService.generateForCompletedWaybills(List.of(waybill.getId()));
|
receivablePayableDetailService.generateForCompletedWaybills(List.of(waybill.getId()));
|
||||||
|
loadingManageService.completeIfAllWaybillsCompleted(waybill.getLoadingNo());
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
@@ -636,7 +678,13 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldSkipDelete(Waybill waybill) {
|
private boolean shouldSkipDelete(Waybill waybill) {
|
||||||
return false;
|
return Func.isNotEmpty(waybill.getLoadingNo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNotLoaded(Waybill waybill) {
|
||||||
|
if (Func.isNotEmpty(waybill.getLoadingNo())) {
|
||||||
|
throw new ServiceException("运单已关联配载单,请在配载单中修改");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateRoadTaskInfo(Waybill waybill) {
|
private void validateRoadTaskInfo(Waybill waybill) {
|
||||||
|
|||||||
Reference in New Issue
Block a user