调整业务问题
This commit is contained in:
+2
@@ -74,9 +74,11 @@ public class InvoiceApplicationSaveRequest implements Serializable {
|
||||
private String unit;
|
||||
private BigDecimal quantity;
|
||||
private BigDecimal unitPriceNoTax;
|
||||
private BigDecimal amountNoTax;
|
||||
private BigDecimal amountWithTax;
|
||||
private BigDecimal taxRate;
|
||||
private BigDecimal taxAmount;
|
||||
private BigDecimal totalAmount;
|
||||
private String remark;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -51,8 +51,10 @@ public class InvoiceApplicationLine extends TenantEntity {
|
||||
private String unit;
|
||||
private BigDecimal quantity;
|
||||
private BigDecimal unitPriceNoTax;
|
||||
private BigDecimal amountNoTax;
|
||||
private BigDecimal amountWithTax;
|
||||
private BigDecimal taxRate;
|
||||
private BigDecimal taxAmount;
|
||||
private BigDecimal totalAmount;
|
||||
private String remark;
|
||||
}
|
||||
|
||||
+5
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
package org.springblade.transport.pojo.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -65,6 +66,10 @@ public class PreSettlementDetailFee extends TenantEntity {
|
||||
@Schema(description = "费用项JSON")
|
||||
private String feeItemsJson;
|
||||
|
||||
@Schema(description = "命中计费规则JSON")
|
||||
@TableField(exist = false)
|
||||
private String billingRulesJson;
|
||||
|
||||
@Schema(description = "原金额")
|
||||
private BigDecimal originalAmount;
|
||||
|
||||
|
||||
+11
-2
@@ -65,15 +65,24 @@ public class BillLedgerController extends BladeController {
|
||||
return R.data(billLedgerService.availableOptions(keyword, deptId, selectedId));
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@GetMapping("/available-page")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "付款申请可用汇票分页")
|
||||
public R<IPage<BillLedgerVO>> availablePage(Query pageQuery,
|
||||
@RequestParam(required = false) String keyword, @RequestParam(required = false) Long deptId,
|
||||
@RequestParam(required = false) Long selectedId) {
|
||||
return R.data(billLedgerService.availablePage(Condition.getPage(pageQuery), keyword, deptId, selectedId));
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "新增或编辑汇票台账")
|
||||
public R<Long> submit(@RequestBody BillLedgerSaveRequest request) {
|
||||
return R.data(billLedgerService.submit(request));
|
||||
}
|
||||
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "删除汇票台账")
|
||||
public R remove(@RequestParam Long id) {
|
||||
billLedgerService.removeLedger(id);
|
||||
|
||||
+20
-13
@@ -174,15 +174,22 @@ public class WaybillController extends BladeController {
|
||||
return R.status(waybillService.submit(waybill));
|
||||
}
|
||||
|
||||
@PostMapping("/remove")
|
||||
@PostMapping("/save-draft")
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "保存草稿", description = "传入waybill")
|
||||
public R saveDraft(@RequestBody Waybill waybill) {
|
||||
return R.status(waybillService.saveDraft(waybill));
|
||||
}
|
||||
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 11)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R<BusinessRemoveResultVO> remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.data(waybillService.removeWaybill(ids));
|
||||
}
|
||||
|
||||
@GetMapping("/export-waybill-manage")
|
||||
@ApiOperationSupport(order = 11)
|
||||
@ApiOperationSupport(order = 12)
|
||||
@Operation(summary = "导出运单管理")
|
||||
public void exportWaybill(WaybillVO waybill, @RequestParam(required = false) String ids, HttpServletResponse response) {
|
||||
List<WaybillExcel> list = waybillService.exportWaybill(waybill, ids);
|
||||
@@ -190,7 +197,7 @@ public class WaybillController extends BladeController {
|
||||
}
|
||||
|
||||
@PostMapping("/import-waybill-manage")
|
||||
@ApiOperationSupport(order = 12)
|
||||
@ApiOperationSupport(order = 13)
|
||||
@Operation(summary = "导入运单管理", description = "传入excel")
|
||||
public R importWaybill(MultipartFile file, HttpServletResponse response) {
|
||||
List<WaybillExcel> failureList = waybillService.importWaybill(ExcelUtil.read(file, WaybillExcel.class));
|
||||
@@ -202,14 +209,14 @@ public class WaybillController extends BladeController {
|
||||
}
|
||||
|
||||
@GetMapping("/export-template")
|
||||
@ApiOperationSupport(order = 13)
|
||||
@ApiOperationSupport(order = 14)
|
||||
@Operation(summary = "导出模板")
|
||||
public void exportTemplate(HttpServletResponse response) {
|
||||
ExcelUtil.export(response, "运单管理模板", "运单管理导入模板", new ArrayList<WaybillExcel>(), WaybillExcel.class);
|
||||
}
|
||||
|
||||
@GetMapping("/import-batch/export-template")
|
||||
@ApiOperationSupport(order = 14)
|
||||
@ApiOperationSupport(order = 15)
|
||||
@Operation(summary = "导出运单批量导入模板")
|
||||
public void exportImportBatchTemplate(HttpServletResponse response) {
|
||||
ExcelUtil.export(
|
||||
@@ -223,56 +230,56 @@ public class WaybillController extends BladeController {
|
||||
}
|
||||
|
||||
@PostMapping("/copy")
|
||||
@ApiOperationSupport(order = 15)
|
||||
@ApiOperationSupport(order = 16)
|
||||
@Operation(summary = "复制", description = "传入id")
|
||||
public R<WaybillVO> copy(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.data(waybillService.copy(id));
|
||||
}
|
||||
|
||||
@PostMapping("/change-route")
|
||||
@ApiOperationSupport(order = 16)
|
||||
@ApiOperationSupport(order = 17)
|
||||
@Operation(summary = "变更运输路线", description = "传入运单路线与变更记录")
|
||||
public R changeRoute(@RequestBody Waybill waybill) {
|
||||
return R.status(waybillService.changeRoute(waybill));
|
||||
}
|
||||
|
||||
@PostMapping("/maintain-mileage")
|
||||
@ApiOperationSupport(order = 17)
|
||||
@ApiOperationSupport(order = 18)
|
||||
@Operation(summary = "维护里程", description = "仅已完成且未生成结算单的运单允许维护")
|
||||
public R maintainMileage(@RequestBody WaybillMileageRequest request) {
|
||||
return R.status(waybillService.maintainMileage(request));
|
||||
}
|
||||
|
||||
@PostMapping("/cancel")
|
||||
@ApiOperationSupport(order = 18)
|
||||
@ApiOperationSupport(order = 19)
|
||||
@Operation(summary = "取消", description = "传入id")
|
||||
public R cancel(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(waybillService.cancel(id));
|
||||
}
|
||||
|
||||
@PostMapping("/reassign")
|
||||
@ApiOperationSupport(order = 19)
|
||||
@ApiOperationSupport(order = 20)
|
||||
@Operation(summary = "重新派单", description = "传入id")
|
||||
public R reassign(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(waybillService.reassign(id));
|
||||
}
|
||||
|
||||
@PostMapping("/complete")
|
||||
@ApiOperationSupport(order = 20)
|
||||
@ApiOperationSupport(order = 21)
|
||||
@Operation(summary = "完成", description = "传入id")
|
||||
public R complete(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
return R.status(waybillService.complete(id));
|
||||
}
|
||||
|
||||
@PostMapping("/batch-complete")
|
||||
@ApiOperationSupport(order = 21)
|
||||
@ApiOperationSupport(order = 22)
|
||||
@Operation(summary = "批量完成", description = "传入ids")
|
||||
public R<BusinessRemoveResultVO> batchComplete(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.data(waybillService.batchComplete(ids));
|
||||
}
|
||||
|
||||
@PostMapping("/road-loading")
|
||||
@ApiOperationSupport(order = 22)
|
||||
@ApiOperationSupport(order = 23)
|
||||
@Operation(summary = "公路配载", description = "传入ids")
|
||||
public R<LoadingManageVO> roadLoading(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.data(waybillService.roadLoading(ids));
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ public interface IBillLedgerService extends BaseService<BillLedger> {
|
||||
BillLedgerVO detail(Long id);
|
||||
Map<String, Long> expiryCounts();
|
||||
List<BillLedgerVO> availableOptions(String keyword, Long deptId, Long selectedId);
|
||||
IPage<BillLedgerVO> availablePage(IPage<BillLedger> page, String keyword, Long deptId, Long selectedId);
|
||||
Long submit(BillLedgerSaveRequest request);
|
||||
void removeLedger(Long id);
|
||||
}
|
||||
|
||||
+1
@@ -43,6 +43,7 @@ public interface IWaybillService extends BaseService<Waybill> {
|
||||
IPage<WaybillVO> selectWaybillPage(IPage<Waybill> page, WaybillVO waybill);
|
||||
WaybillVO detail(Long id);
|
||||
boolean submit(Waybill waybill);
|
||||
boolean saveDraft(Waybill waybill);
|
||||
BusinessRemoveResultVO removeWaybill(String ids);
|
||||
List<WaybillExcel> exportWaybill(WaybillVO waybill, String ids);
|
||||
List<WaybillExcel> importWaybill(List<WaybillExcel> data);
|
||||
|
||||
+19
-2
@@ -7,6 +7,7 @@ package org.springblade.transport.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
@@ -86,6 +87,23 @@ public class BillLedgerServiceImpl extends BaseServiceImpl<BillLedgerMapper, Bil
|
||||
|
||||
@Override
|
||||
public List<BillLedgerVO> availableOptions(String keyword, Long deptId, Long selectedId) {
|
||||
return availableList(keyword, deptId, selectedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<BillLedgerVO> availablePage(IPage<BillLedger> page, String keyword, Long deptId,
|
||||
Long selectedId) {
|
||||
List<BillLedgerVO> available = availableList(keyword, deptId, selectedId);
|
||||
long current = Math.max(page.getCurrent(), 1);
|
||||
long size = Math.max(page.getSize(), 1);
|
||||
long from = Math.min((current - 1) * size, available.size());
|
||||
long to = Math.min(from + size, available.size());
|
||||
Page<BillLedgerVO> result = new Page<>(current, size, available.size());
|
||||
result.setRecords(available.subList((int) from, (int) to));
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<BillLedgerVO> availableList(String keyword, Long deptId, Long selectedId) {
|
||||
return list(Wrappers.<BillLedger>lambdaQuery()
|
||||
.and(Func.isNotEmpty(keyword), wrapper -> wrapper.like(BillLedger::getBillNo, keyword)
|
||||
.or().like(BillLedger::getIssuerName, keyword)
|
||||
@@ -94,8 +112,7 @@ public class BillLedgerServiceImpl extends BaseServiceImpl<BillLedgerMapper, Bil
|
||||
.gt(BillLedger::getAvailableBalance, BigDecimal.ZERO)
|
||||
.or(selectedId != null, child -> child.eq(BillLedger::getId, selectedId)))
|
||||
.orderByAsc(BillLedger::getMaturityDate)
|
||||
.orderByDesc(BillLedger::getCreateTime)
|
||||
.last("limit 200")).stream()
|
||||
.orderByDesc(BillLedger::getCreateTime)).stream()
|
||||
.filter(item -> selectedId != null && Objects.equals(item.getId(), selectedId)
|
||||
|| departmentAvailable(item, deptId))
|
||||
.map(item -> BillLedgerWrapper.build().entityVO(item))
|
||||
|
||||
+34
-11
@@ -47,6 +47,7 @@ import org.springblade.transport.mapper.InvoiceApplicationMapper;
|
||||
import org.springblade.transport.mapper.InvoiceApplicationRecordMapper;
|
||||
import org.springblade.transport.mapper.InvoiceApplicationSettlementMapper;
|
||||
import org.springblade.transport.mapper.InvoiceApplicationSheetMapper;
|
||||
import org.springblade.transport.mapper.WaybillMapper;
|
||||
import org.springblade.transport.pojo.dto.InvoiceApplicationSaveRequest;
|
||||
import org.springblade.transport.pojo.dto.InvoiceApplicationStatusRequest;
|
||||
import org.springblade.transport.pojo.entity.CustomerArchive;
|
||||
@@ -61,6 +62,7 @@ import org.springblade.transport.pojo.entity.InvoiceApplicationLine;
|
||||
import org.springblade.transport.pojo.entity.InvoiceApplicationRecord;
|
||||
import org.springblade.transport.pojo.entity.InvoiceApplicationSettlement;
|
||||
import org.springblade.transport.pojo.entity.InvoiceApplicationSheet;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.vo.InvoiceApplicationSheetVO;
|
||||
import org.springblade.transport.pojo.vo.InvoiceApplicationVO;
|
||||
import org.springblade.transport.service.IInvoiceApplicationService;
|
||||
@@ -100,6 +102,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
private static final String VOIDED = "voided";
|
||||
private final InvoiceApplicationSettlementMapper settlementRelationMapper;
|
||||
private final InvoiceApplicationSheetMapper sheetMapper;
|
||||
private final WaybillMapper waybillMapper;
|
||||
private final InvoiceApplicationLineMapper lineMapper;
|
||||
private final InvoiceApplicationDetailMapper applicationDetailMapper;
|
||||
private final InvoiceApplicationRecordMapper recordMapper;
|
||||
@@ -237,8 +240,23 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
List<Long> ids = distinctIds(settlementIds);
|
||||
if (ids.isEmpty()) return List.of();
|
||||
assertCompatible(ids.stream().map(this::availableSettlement).toList());
|
||||
return formalSettlementDetailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
|
||||
.in(FormalSettlementDetail::getFormalSettlementId, ids).orderByAsc(FormalSettlementDetail::getLineNo));
|
||||
List<FormalSettlementDetail> details = formalSettlementDetailMapper.selectList(
|
||||
Wrappers.<FormalSettlementDetail>lambdaQuery()
|
||||
.in(FormalSettlementDetail::getFormalSettlementId, ids)
|
||||
.orderByAsc(FormalSettlementDetail::getLineNo));
|
||||
List<Long> waybillIds = details.stream().map(FormalSettlementDetail::getWaybillId)
|
||||
.filter(Objects::nonNull).distinct().toList();
|
||||
if (!waybillIds.isEmpty()) {
|
||||
Map<Long, Waybill> waybillMap = waybillMapper.selectBatchIds(waybillIds).stream()
|
||||
.collect(Collectors.toMap(Waybill::getId, Function.identity(), (first, duplicate) -> first));
|
||||
details.forEach(detail -> {
|
||||
Waybill waybill = waybillMap.get(detail.getWaybillId());
|
||||
if (waybill == null) return;
|
||||
if (waybill.getStartDate() != null) detail.setActualDepartureTime(waybill.getStartDate().atStartOfDay());
|
||||
if (waybill.getEndDate() != null) detail.setActualCompletionTime(waybill.getEndDate().atStartOfDay());
|
||||
});
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -455,14 +473,21 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
for (InvoiceApplicationSaveRequest.LineRow line : sheet.getLines()) {
|
||||
required(line.getGoodsCategory(), "商品和服务分类");
|
||||
required(line.getGoodsName(), "货物或服务简称");
|
||||
nonNegative(line.getQuantity(), "数量");
|
||||
nonNegative(line.getUnitPriceNoTax(), "不含税单价");
|
||||
BigDecimal amount = nonNegative(line.getAmountWithTax(), "含税金额");
|
||||
BigDecimal quantity = nonNegative(line.getQuantity(), "数量");
|
||||
BigDecimal unitPriceNoTax = nonNegative(line.getUnitPriceNoTax(), "不含税单价")
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal amountNoTax = quantity.multiply(unitPriceNoTax)
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal taxRate = nonNegative(line.getTaxRate(), "税率");
|
||||
if (taxRate.compareTo(BigDecimal.valueOf(100)) > 0) throw new ServiceException("税率必须在0-100之间");
|
||||
line.setTaxAmount(calculateTax(amount, taxRate));
|
||||
BigDecimal taxAmount = calculateTax(amountNoTax, taxRate);
|
||||
BigDecimal totalAmount = amountNoTax.add(taxAmount).setScale(2, RoundingMode.HALF_UP);
|
||||
line.setAmountNoTax(amountNoTax);
|
||||
line.setTaxAmount(taxAmount);
|
||||
line.setTotalAmount(totalAmount);
|
||||
line.setAmountWithTax(totalAmount);
|
||||
line.setRemark(limit(line.getRemark(), 200, "商品行备注"));
|
||||
total = total.add(amount);
|
||||
total = total.add(totalAmount);
|
||||
}
|
||||
}
|
||||
return total.setScale(2, RoundingMode.HALF_UP);
|
||||
@@ -534,7 +559,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
InvoiceApplicationSheet sheet = new InvoiceApplicationSheet();
|
||||
sheet.setInvoiceApplicationId(applicationId);
|
||||
sheet.setSheetNo(sheetNo++);
|
||||
sheet.setInvoiceAmount(sheetRow.getLines().stream().map(InvoiceApplicationSaveRequest.LineRow::getAmountWithTax)
|
||||
sheet.setInvoiceAmount(sheetRow.getLines().stream().map(InvoiceApplicationSaveRequest.LineRow::getTotalAmount)
|
||||
.map(this::money).reduce(BigDecimal.ZERO, BigDecimal::add));
|
||||
sheetMapper.insert(sheet);
|
||||
int lineNo = 1;
|
||||
@@ -730,9 +755,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
}
|
||||
|
||||
private BigDecimal calculateTax(BigDecimal amount, BigDecimal rate) {
|
||||
if (rate.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||
return amount.subtract(amount.divide(BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(100), 8,
|
||||
RoundingMode.HALF_UP)), 8, RoundingMode.HALF_UP)).setScale(2, RoundingMode.HALF_UP);
|
||||
return amount.multiply(rate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal nonNegative(BigDecimal value, String name) {
|
||||
|
||||
+1
@@ -663,6 +663,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
|
||||
.set(Waybill::getEscortName, loadingManage.getEscortName())
|
||||
.set(Waybill::getEscortPhone, loadingManage.getEscortPhone())
|
||||
.set(Waybill::getMileage, loadingManage.getMileage())
|
||||
.set(STATUS_COMPLETED.equals(status), Waybill::getEndDate, LocalDate.now())
|
||||
.set(Waybill::getEstimatedStartTime, toDateTime(loadingManage.getEstimatedStartDate()))
|
||||
.set(Waybill::getEstimatedEndTime, toDateTime(loadingManage.getEstimatedEndDate()))
|
||||
.set(Waybill::getTaskRemark, loadingManage.getTaskRemark())
|
||||
|
||||
+13
-4
@@ -141,6 +141,10 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
||||
masterOrder.setBusinessStatus("closed");
|
||||
boolean updated = updateById(masterOrder);
|
||||
if (updated) {
|
||||
waybillsByMasterNo(masterOrder.getMasterNo()).forEach(waybill -> {
|
||||
waybill.setEndDate(LocalDate.now());
|
||||
waybillService.updateById(waybill);
|
||||
});
|
||||
receivablePayableDetailService.generateForClosedMasterOrder(masterOrder);
|
||||
}
|
||||
return updated;
|
||||
@@ -369,8 +373,7 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
||||
if (Func.isEmpty(string(dispatch, "priceUnit"))) throw new ServiceException("单价单位不能为空");
|
||||
if (!available.containsKey(key)) throw new ServiceException("调度货物必须来自总单配置");
|
||||
LocalDate startDate = date(dispatch, "estimatedStartTime"); LocalDate endDate = date(dispatch, "estimatedEndTime");
|
||||
if (startDate == null || endDate == null) throw new ServiceException("预计发货日期和预计完成日期不能为空");
|
||||
if (startDate.isAfter(endDate)) throw new ServiceException("预计发货日期不能晚于预计完成日期");
|
||||
if (startDate != null && endDate != null && startDate.isAfter(endDate)) throw new ServiceException("预计发货日期不能晚于预计完成日期");
|
||||
if ("运单".equals(string(dispatch, "documentType", "运单"))) {
|
||||
validateCarrier(dispatch, availableCarrierContracts);
|
||||
}
|
||||
@@ -424,10 +427,11 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
||||
|| !Objects.equals(carrierContract.getPartyB(), carrierName))) {
|
||||
throw new ServiceException("所选承运商不属于总单客户合同对应项目的有效承运商合同乙方");
|
||||
}
|
||||
boolean road = string(dispatch, "transportType", "").toLowerCase().contains("road") || string(dispatch, "transportType", "").contains("公路");
|
||||
String transportType = string(dispatch, "transportType", "");
|
||||
boolean road = transportType.toLowerCase().contains("road") || transportType.contains("公路");
|
||||
String mileage = string(dispatch, "mileage");
|
||||
if (!road) {
|
||||
if (Func.isEmpty(string(dispatch, "vehicleNo")) || Func.isEmpty(string(dispatch, "captainName")) || Func.isEmpty(string(dispatch, "driverPhone")) || Func.isEmpty(string(dispatch, "containerNo")) || Func.isEmpty(string(dispatch, "cabinNo")) || (Func.isNotEmpty(mileage) && decimal(dispatch, "mileage").compareTo(BigDecimal.ZERO) <= 0) || ("承运商".equals(carrierType) && Func.isEmpty(string(dispatch, "carrierName")))) {
|
||||
if (Func.isEmpty(string(dispatch, "vehicleNo")) || Func.isEmpty(string(dispatch, "driverPhone")) || (Func.isNotEmpty(mileage) && decimal(dispatch, "mileage").compareTo(BigDecimal.ZERO) <= 0) || ("承运商".equals(carrierType) && Func.isEmpty(string(dispatch, "carrierName")))) {
|
||||
throw new ServiceException("非公路运输的承运信息不完整");
|
||||
}
|
||||
return;
|
||||
@@ -439,6 +443,11 @@ public class MasterOrderServiceImpl extends BaseServiceImpl<MasterOrderMapper, M
|
||||
}
|
||||
if (Func.isEmpty(string(dispatch, "driverName")) || Func.isEmpty(string(dispatch, "driverPhone")) || Func.isEmpty(string(dispatch, "trailerVehicleNo")) || Func.isEmpty(string(dispatch, "escortName")) || Func.isEmpty(string(dispatch, "escortPhone")) || (Func.isNotEmpty(mileage) && decimal(dispatch, "mileage").compareTo(BigDecimal.ZERO) < 0)) throw new ServiceException("自运或网货平台的车辆与人员信息不完整");
|
||||
}
|
||||
private boolean isWaterTransport(String transportType) {
|
||||
String value = transportType == null ? "" : transportType.trim().toLowerCase();
|
||||
return "river".equals(value) || "water".equals(value) || "sl".equals(value)
|
||||
|| value.contains("水路") || value.contains("水运");
|
||||
}
|
||||
private BigDecimal dispatchedQuantity(String masterNo, String segmentNo) {
|
||||
return dispatchedGoods(masterNo, segmentNo).values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
+4
-24
@@ -73,7 +73,6 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/** 付款申请服务实现。 @author Chill */
|
||||
@@ -257,7 +256,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
throw new ServiceException("当前状态不允许提交");
|
||||
}
|
||||
validateNoTailPayment(entity);
|
||||
validateProjectAdvanceContractFile(entity);
|
||||
validateProjectAdvanceContract(entity);
|
||||
validateSelectedBill(entity, false);
|
||||
validateQuota(entity);
|
||||
entity.setApprovalStatus(REVIEWING);
|
||||
@@ -358,33 +357,14 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
}
|
||||
}
|
||||
|
||||
private void validateProjectAdvanceContractFile(PaymentApplication entity) {
|
||||
private void validateProjectAdvanceContract(PaymentApplication entity) {
|
||||
if (!"project_advance".equals(entity.getPaymentType())) return;
|
||||
ContractManage contract = entity.getContractId() == null ? null : contractManageMapper.selectById(entity.getContractId());
|
||||
if (contract == null || Objects.equals(contract.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("项目预付必须选择有效合同");
|
||||
}
|
||||
if (!hasDownloadableFile(contract.getContractFileJson())
|
||||
&& !hasDownloadableFile(entity.getAttachmentsJson(), "contract")) {
|
||||
throw new ServiceException("请上传合同签章文件");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasDownloadableFile(String value) {
|
||||
return hasDownloadableFile(value, null);
|
||||
}
|
||||
|
||||
private boolean hasDownloadableFile(String value, String attachmentType) {
|
||||
if (Func.isEmpty(value)) return false;
|
||||
try {
|
||||
Object parsed = JsonUtil.parse(value, List.class);
|
||||
if (!(parsed instanceof List<?> files)) return false;
|
||||
return files.stream().anyMatch(file -> file instanceof Map<?, ?> fields
|
||||
&& (attachmentType == null || Objects.equals(attachmentType, fields.get("attachmentType")))
|
||||
&& List.of("url", "link", "fileUrl", "downloadUrl", "src", "domain").stream()
|
||||
.anyMatch(key -> Func.isNotEmpty(fields.get(key))));
|
||||
} catch (Exception exception) {
|
||||
return false;
|
||||
if (!"承运商合同".equals(contract.getContractCategory())) {
|
||||
throw new ServiceException("付款申请只能选择承运商合同");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -235,6 +235,7 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
||||
result.put("deptName", contract.getOrganizationName());
|
||||
result.put("partyA", contract.getPartyA());
|
||||
result.put("partyB", contract.getPartyB());
|
||||
result.put("contractCategory", contract.getContractCategory());
|
||||
result.put("fundDemand", project == null ? null : project.getFundDemand());
|
||||
result.put("settlementMode", project == null ? null : project.getSettlementMode());
|
||||
result.put("settlementType", contractSettlementType(contract));
|
||||
@@ -570,10 +571,20 @@ public class PreSettlementServiceImpl extends BaseServiceImpl<PreSettlementMappe
|
||||
if (detail == null || Objects.equals(detail.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("预结算明细不存在");
|
||||
}
|
||||
return detailFeeMapper.selectList(Wrappers.<PreSettlementDetailFee>lambdaQuery()
|
||||
List<PreSettlementDetailFee> fees = detailFeeMapper.selectList(Wrappers.<PreSettlementDetailFee>lambdaQuery()
|
||||
.eq(PreSettlementDetailFee::getPreSettlementDetailId, detailId)
|
||||
.eq(PreSettlementDetailFee::getIsDeleted, 0)
|
||||
.orderByAsc(PreSettlementDetailFee::getCreateTime));
|
||||
List<Long> sourceFeeIds = fees.stream().map(PreSettlementDetailFee::getSourceFeeId)
|
||||
.filter(Objects::nonNull).toList();
|
||||
if (sourceFeeIds.isEmpty()) return fees;
|
||||
Map<Long, ReceivablePayableCargoFee> sourceFeeMap = sourceFeeMapper.selectBatchIds(sourceFeeIds).stream()
|
||||
.collect(Collectors.toMap(ReceivablePayableCargoFee::getId, Function.identity(), (left, right) -> left));
|
||||
fees.forEach(fee -> {
|
||||
ReceivablePayableCargoFee sourceFee = sourceFeeMap.get(fee.getSourceFeeId());
|
||||
if (sourceFee != null) fee.setBillingRulesJson(sourceFee.getBillingRulesJson());
|
||||
});
|
||||
return fees;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -462,7 +462,7 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
|
||||
transportPlan.setArrivalPhone(TransportBusinessSupport.trimToNull(transportPlan.getArrivalPhone()));
|
||||
transportPlan.setGoodsJson(TransportBusinessSupport.trimToNull(transportPlan.getGoodsJson()));
|
||||
transportPlan.setAttachmentsJson(TransportBusinessSupport.trimToNull(transportPlan.getAttachmentsJson()));
|
||||
transportPlan.setDataSource(TransportBusinessSupport.trimToNull(transportPlan.getDataSource()));
|
||||
transportPlan.setDataSource(TransportBusinessSupport.normalizeTransportPlanDataSource(transportPlan.getDataSource()));
|
||||
transportPlan.setBusinessStatus(TransportBusinessSupport.trimToNull(transportPlan.getBusinessStatus()));
|
||||
transportPlan.setDeptName(TransportBusinessSupport.trimToNull(transportPlan.getDeptName()));
|
||||
transportPlan.setRemark(TransportBusinessSupport.trimToNull(transportPlan.getRemark()));
|
||||
|
||||
+15
-2
@@ -106,6 +106,20 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(Waybill waybill) {
|
||||
prepareForSave(waybill);
|
||||
validate(waybill);
|
||||
return saveOrUpdate(waybill);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveDraft(Waybill waybill) {
|
||||
prepareForSave(waybill);
|
||||
waybill.setBusinessStatus(STATUS_DRAFT);
|
||||
return saveOrUpdate(waybill);
|
||||
}
|
||||
|
||||
private void prepareForSave(Waybill waybill) {
|
||||
boolean created = Func.isEmpty(waybill.getId());
|
||||
if (!created) {
|
||||
Waybill oldRecord = loadEditable(waybill.getId(), true);
|
||||
@@ -124,8 +138,6 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
if (created && Func.isEmpty(waybill.getWaybillNo())) {
|
||||
waybill.setWaybillNo(nextCode());
|
||||
}
|
||||
validate(waybill);
|
||||
return saveOrUpdate(waybill);
|
||||
}
|
||||
|
||||
private void fillProjectProcessConfig(Waybill waybill) {
|
||||
@@ -372,6 +384,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
throw new ServiceException("当前状态不允许完成");
|
||||
}
|
||||
waybill.setBusinessStatus("completed");
|
||||
waybill.setEndDate(LocalDate.now());
|
||||
boolean updated = updateById(waybill);
|
||||
if (updated) {
|
||||
receivablePayableDetailService.generateForCompletedWaybills(List.of(waybill.getId()));
|
||||
|
||||
+23
@@ -41,6 +41,10 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public final class TransportBusinessSupport {
|
||||
|
||||
public static final String DATA_SOURCE_BATCH_IMPORT = "批量导入";
|
||||
public static final String DATA_SOURCE_MANUAL = "手工创建";
|
||||
public static final String DATA_SOURCE_EXTERNAL = "外部系统";
|
||||
|
||||
private static final Pattern PHONE_PATTERN = Pattern.compile("^(1\\d{10}|0\\d{2,3}-?\\d{7,8})$");
|
||||
private static final BigDecimal MIN_LONGITUDE = new BigDecimal("-180");
|
||||
private static final BigDecimal MAX_LONGITUDE = new BigDecimal("180");
|
||||
@@ -87,6 +91,25 @@ public final class TransportBusinessSupport {
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化运输计划数据来源,保证列表只出现约定的三种来源。
|
||||
*
|
||||
* @param value 原始数据来源
|
||||
* @return 批量导入、手工创建或外部系统
|
||||
*/
|
||||
public static String normalizeTransportPlanDataSource(String value) {
|
||||
String source = trimToNull(value);
|
||||
if (Func.isEmpty(source)) {
|
||||
return DATA_SOURCE_MANUAL;
|
||||
}
|
||||
return switch (source) {
|
||||
case DATA_SOURCE_BATCH_IMPORT -> DATA_SOURCE_BATCH_IMPORT;
|
||||
case DATA_SOURCE_MANUAL, "手动创建", "手动录入", "手工录入", "手动", "模板生成", "计划调度", "多联总单调度" -> DATA_SOURCE_MANUAL;
|
||||
case DATA_SOURCE_EXTERNAL -> DATA_SOURCE_EXTERNAL;
|
||||
default -> DATA_SOURCE_EXTERNAL;
|
||||
};
|
||||
}
|
||||
|
||||
public static void validateRequired(String value, String message) {
|
||||
if (Func.isEmpty(trimToNull(value))) {
|
||||
throw new ServiceException(message);
|
||||
|
||||
+2
@@ -29,6 +29,7 @@ import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.pojo.entity.TransportPlan;
|
||||
import org.springblade.transport.pojo.vo.TransportPlanVO;
|
||||
import org.springblade.transport.support.TransportBusinessSupport;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -48,6 +49,7 @@ public class TransportPlanWrapper extends BaseEntityWrapper<TransportPlan, Trans
|
||||
TransportPlanVO transportPlanVO = Objects.requireNonNull(BeanUtil.copyProperties(transportPlan, TransportPlanVO.class));
|
||||
transportPlanVO.setCreateUserName(UserCache.getUserRealName(transportPlan.getCreateUser()));
|
||||
transportPlanVO.setUpdateUserName(UserCache.getUserRealName(transportPlan.getUpdateUser()));
|
||||
transportPlanVO.setDataSource(TransportBusinessSupport.normalizeTransportPlanDataSource(transportPlan.getDataSource()));
|
||||
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
|
||||
transportPlanVO.setReadonly(currentDeptId != null && !Objects.equals(transportPlan.getDeptId(), currentDeptId));
|
||||
transportPlanVO.setBusinessStatusName(businessStatusName(transportPlan.getBusinessStatus()));
|
||||
|
||||
@@ -104,10 +104,12 @@ CREATE TABLE IF NOT EXISTS `blade_invoice_application_line` (
|
||||
`goods_name` varchar(100) NOT NULL,
|
||||
`unit` varchar(30) DEFAULT NULL,
|
||||
`quantity` decimal(18,4) DEFAULT NULL,
|
||||
`unit_price_no_tax` decimal(18,4) DEFAULT NULL,
|
||||
`amount_with_tax` decimal(18,2) NOT NULL DEFAULT '0.00',
|
||||
`unit_price_no_tax` decimal(18,2) DEFAULT NULL,
|
||||
`amount_no_tax` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '不含税金额',
|
||||
`amount_with_tax` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '含税金额(兼容历史字段)',
|
||||
`tax_rate` decimal(8,4) NOT NULL DEFAULT '0.0000',
|
||||
`tax_amount` decimal(18,2) NOT NULL DEFAULT '0.00',
|
||||
`total_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '含税合计',
|
||||
`remark` varchar(200) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_invoice_sheet_line` (`invoice_sheet_id`,`line_no`),
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
-- 开票申请商品行补齐不含税金额与含税合计字段。
|
||||
-- 使用 information_schema 判断字段是否存在,重复执行不会报重复列错误。
|
||||
SET @invoice_line_schema = DATABASE();
|
||||
|
||||
SET @invoice_line_sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) > 0,
|
||||
'ALTER TABLE `blade_invoice_application_line` MODIFY COLUMN `unit_price_no_tax` decimal(18,2) DEFAULT NULL',
|
||||
'SELECT 1'
|
||||
)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @invoice_line_schema
|
||||
AND table_name = 'blade_invoice_application_line'
|
||||
AND column_name = 'unit_price_no_tax'
|
||||
AND (numeric_precision <> 18 OR numeric_scale <> 2)
|
||||
);
|
||||
PREPARE invoice_line_stmt FROM @invoice_line_sql;
|
||||
EXECUTE invoice_line_stmt;
|
||||
DEALLOCATE PREPARE invoice_line_stmt;
|
||||
|
||||
SET @invoice_line_sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_invoice_application_line` ADD COLUMN `amount_no_tax` decimal(18,2) NOT NULL DEFAULT ''0.00'' COMMENT ''不含税金额'' AFTER `unit_price_no_tax`',
|
||||
'SELECT 1'
|
||||
)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @invoice_line_schema
|
||||
AND table_name = 'blade_invoice_application_line'
|
||||
AND column_name = 'amount_no_tax'
|
||||
);
|
||||
PREPARE invoice_line_stmt FROM @invoice_line_sql;
|
||||
EXECUTE invoice_line_stmt;
|
||||
DEALLOCATE PREPARE invoice_line_stmt;
|
||||
|
||||
SET @invoice_line_sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_invoice_application_line` ADD COLUMN `total_amount` decimal(18,2) NOT NULL DEFAULT ''0.00'' COMMENT ''含税合计'' AFTER `tax_amount`',
|
||||
'SELECT 1'
|
||||
)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @invoice_line_schema
|
||||
AND table_name = 'blade_invoice_application_line'
|
||||
AND column_name = 'total_amount'
|
||||
);
|
||||
PREPARE invoice_line_stmt FROM @invoice_line_sql;
|
||||
EXECUTE invoice_line_stmt;
|
||||
DEALLOCATE PREPARE invoice_line_stmt;
|
||||
|
||||
UPDATE `blade_invoice_application_line`
|
||||
SET `amount_no_tax` = ROUND(
|
||||
`amount_with_tax` / (1 + `tax_rate` / 100),
|
||||
2
|
||||
)
|
||||
WHERE `amount_no_tax` = 0
|
||||
AND `amount_with_tax` > 0;
|
||||
|
||||
UPDATE `blade_invoice_application_line`
|
||||
SET `total_amount` = ROUND(`amount_no_tax` + `tax_amount`, 2),
|
||||
`amount_with_tax` = ROUND(`amount_no_tax` + `tax_amount`, 2);
|
||||
@@ -196,7 +196,7 @@ CREATE TABLE `blade_transport_plan` (
|
||||
`goods_json` text DEFAULT NULL COMMENT '货物信息',
|
||||
`freight_json` text DEFAULT NULL COMMENT '费用信息',
|
||||
`attachments_json` text DEFAULT NULL COMMENT '附件',
|
||||
`data_source` varchar(100) DEFAULT NULL COMMENT '数据来源',
|
||||
`data_source` varchar(100) DEFAULT '手工创建' COMMENT '数据来源',
|
||||
`business_status` varchar(100) DEFAULT NULL COMMENT '业务状态',
|
||||
`dispatcher_user_id` bigint(20) DEFAULT NULL COMMENT '调度人ID',
|
||||
`dispatcher_user_name` varchar(100) DEFAULT NULL COMMENT '调度人姓名',
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
-- 运输计划数据来源统一为:批量导入、手工创建、外部系统
|
||||
ALTER TABLE `blade_transport_plan`
|
||||
MODIFY COLUMN `data_source` varchar(100) DEFAULT '手工创建' COMMENT '数据来源';
|
||||
|
||||
UPDATE `blade_transport_plan`
|
||||
SET `data_source` = CASE
|
||||
WHEN TRIM(COALESCE(`data_source`, '')) = '批量导入' THEN '批量导入'
|
||||
WHEN TRIM(COALESCE(`data_source`, '')) IN (
|
||||
'手工创建', '手动创建', '手动录入', '手工录入', '手动',
|
||||
'模板生成', '计划调度', '多联总单调度'
|
||||
) THEN '手工创建'
|
||||
WHEN TRIM(COALESCE(`data_source`, '')) = '外部系统' THEN '外部系统'
|
||||
ELSE '外部系统'
|
||||
END
|
||||
WHERE `data_source` IS NULL
|
||||
OR TRIM(`data_source`) = ''
|
||||
OR TRIM(`data_source`) NOT IN ('批量导入', '手工创建', '外部系统');
|
||||
Reference in New Issue
Block a user