调整 合同、运力、基础配置、客商

This commit is contained in:
2026-09-14 23:43:00 +08:00
parent 70e44da789
commit 087d89b31a
33 changed files with 881 additions and 93 deletions
@@ -47,6 +47,7 @@ import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.excel.AnnualInspectionRecordImporter;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import org.springblade.transport.service.IAnnualInspectionRecordService;
import org.springblade.transport.wrapper.AnnualInspectionRecordWrapper;
@@ -58,6 +59,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -91,6 +93,7 @@ public class AnnualInspectionRecordController extends BladeController {
@ApiOperationSupport(order = 2)
@Operation(summary = "分页", description = "传入annualInspectionRecord")
public R<IPage<AnnualInspectionRecordVO>> list(AnnualInspectionRecordVO annualInspectionRecord, Query query) {
fillExpiryDate(annualInspectionRecord);
IPage<AnnualInspectionRecordVO> pages = annualInspectionRecordService.selectAnnualInspectionRecordPage(Condition.getPage(normalizeQuery(query)), annualInspectionRecord);
return R.data(pages);
}
@@ -109,8 +112,17 @@ public class AnnualInspectionRecordController extends BladeController {
return R.status(annualInspectionRecordService.deleteLogic(Func.toLongList(ids)));
}
@PostMapping("/import-annual-inspection-record")
@GetMapping("/expiry-stat")
@ApiOperationSupport(order = 5)
@Operation(summary = "有效期统计", description = "传入annualInspectionRecord")
public R<AnnualInspectionRecordExpiryStatVO> expiryStat(AnnualInspectionRecordVO annualInspectionRecord) {
fillExpiryDate(annualInspectionRecord);
annualInspectionRecord.setExpireStatus(null);
return R.data(annualInspectionRecordService.expiryStat(annualInspectionRecord));
}
@PostMapping("/import-annual-inspection-record")
@ApiOperationSupport(order = 6)
@Operation(summary = "导入年检记录", description = "传入excel")
public R importAnnualInspectionRecord(MultipartFile file, HttpServletResponse response) {
List<AnnualInspectionRecordExcel> failureList = annualInspectionRecordService.importAnnualInspectionRecord(ExcelUtil.read(file, AnnualInspectionRecordExcel.class));
@@ -122,17 +134,18 @@ public class AnnualInspectionRecordController extends BladeController {
}
@GetMapping("/export-annual-inspection-record")
@ApiOperationSupport(order = 6)
@ApiOperationSupport(order = 7)
@Operation(summary = "导出年检记录")
public void exportAnnualInspectionRecord(AnnualInspectionRecordVO annualInspectionRecord,
@RequestParam(required = false) String ids,
HttpServletResponse response) {
fillExpiryDate(annualInspectionRecord);
List<AnnualInspectionRecordExportExcel> list = annualInspectionRecordService.exportAnnualInspectionRecord(buildExportQuery(annualInspectionRecord, ids));
ExcelUtil.export(response, "年检记录" + DateUtil.time(), "年检记录表", list, AnnualInspectionRecordExportExcel.class);
}
@GetMapping("/export-template")
@ApiOperationSupport(order = 7)
@ApiOperationSupport(order = 8)
@Operation(summary = "导出模板")
public void exportTemplate(HttpServletResponse response) {
List<AnnualInspectionRecordExcel> list = new ArrayList<>();
@@ -155,6 +168,15 @@ public class AnnualInspectionRecordController extends BladeController {
return query;
}
private void fillExpiryDate(AnnualInspectionRecordVO annualInspectionRecord) {
if (annualInspectionRecord.getToday() == null) {
annualInspectionRecord.setToday(LocalDate.now());
}
if (annualInspectionRecord.getWarningDate() == null) {
annualInspectionRecord.setWarningDate(annualInspectionRecord.getToday().plusDays(30));
}
}
private LambdaQueryWrapper<AnnualInspectionRecord> buildExportQuery(AnnualInspectionRecordVO annualInspectionRecord, String ids) {
LambdaQueryWrapper<AnnualInspectionRecord> queryWrapper = Wrappers.<AnnualInspectionRecord>lambdaQuery()
.eq(AnnualInspectionRecord::getIsDeleted, 0)
@@ -183,6 +205,15 @@ public class AnnualInspectionRecordController extends BladeController {
if (Func.isNotEmpty(annualInspectionRecord.getCreateTimeEnd())) {
queryWrapper.le(AnnualInspectionRecord::getCreateTime, annualInspectionRecord.getCreateTimeEnd());
}
if ("within30".equals(annualInspectionRecord.getExpireStatus())) {
queryWrapper.isNotNull(AnnualInspectionRecord::getValidUntilDate)
.ge(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getToday())
.le(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getWarningDate());
}
if ("expired".equals(annualInspectionRecord.getExpireStatus())) {
queryWrapper.isNotNull(AnnualInspectionRecord::getValidUntilDate)
.lt(AnnualInspectionRecord::getValidUntilDate, annualInspectionRecord.getToday());
}
return queryWrapper;
}
@@ -27,7 +27,9 @@ package org.springblade.transport.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import java.util.List;
@@ -46,6 +48,14 @@ public interface AnnualInspectionRecordMapper extends BaseMapper<AnnualInspectio
* @param annualInspectionRecord 查询参数
* @return 年检记录分页
*/
List<AnnualInspectionRecordVO> selectAnnualInspectionRecordPage(IPage<AnnualInspectionRecordVO> page, AnnualInspectionRecordVO annualInspectionRecord);
List<AnnualInspectionRecordVO> selectAnnualInspectionRecordPage(IPage<AnnualInspectionRecordVO> page, @Param("annualInspectionRecord") AnnualInspectionRecordVO annualInspectionRecord);
/**
* 有效期统计
*
* @param annualInspectionRecord 查询参数
* @return 统计结果
*/
AnnualInspectionRecordExpiryStatVO selectExpiryStat(@Param("annualInspectionRecord") AnnualInspectionRecordVO annualInspectionRecord);
}
@@ -26,6 +26,49 @@
<result column="remark" property="remark"/>
</resultMap>
<sql id="ExpiryWithin30Condition">
valid_until_date IS NOT NULL
AND valid_until_date &gt;= #{annualInspectionRecord.today}
AND valid_until_date &lt;= #{annualInspectionRecord.warningDate}
</sql>
<sql id="ExpiryExpiredCondition">
valid_until_date IS NOT NULL
AND valid_until_date &lt; #{annualInspectionRecord.today}
</sql>
<sql id="QueryCondition">
is_deleted = 0
<if test="annualInspectionRecord.createDept != null">
AND create_dept = #{annualInspectionRecord.createDept}
</if>
<if test="annualInspectionRecord.vehicleType != null and annualInspectionRecord.vehicleType != ''">
AND vehicle_type = #{annualInspectionRecord.vehicleType}
</if>
<if test="annualInspectionRecord.vehicleNo != null and annualInspectionRecord.vehicleNo != ''">
<bind name="vehicleNoLike" value="'%' + annualInspectionRecord.vehicleNo + '%'"/>
AND vehicle_no LIKE #{vehicleNoLike}
</if>
<if test="annualInspectionRecord.inspectionAssessmentDateStart != null">
AND inspection_assessment_date &gt;= #{annualInspectionRecord.inspectionAssessmentDateStart}
</if>
<if test="annualInspectionRecord.inspectionAssessmentDateEnd != null">
AND inspection_assessment_date &lt;= #{annualInspectionRecord.inspectionAssessmentDateEnd}
</if>
<if test="annualInspectionRecord.createTimeStart != null">
AND create_time &gt;= #{annualInspectionRecord.createTimeStart}
</if>
<if test="annualInspectionRecord.createTimeEnd != null">
AND create_time &lt;= #{annualInspectionRecord.createTimeEnd}
</if>
<if test="annualInspectionRecord.expireStatus != null and annualInspectionRecord.expireStatus == 'within30'">
AND <include refid="ExpiryWithin30Condition"/>
</if>
<if test="annualInspectionRecord.expireStatus != null and annualInspectionRecord.expireStatus == 'expired'">
AND <include refid="ExpiryExpiredCondition"/>
</if>
</sql>
<select id="selectAnnualInspectionRecordPage" resultMap="annualInspectionRecordResultMap">
SELECT
id,
@@ -52,30 +95,19 @@
FROM
blade_annual_inspection_record
WHERE
is_deleted = 0
<if test="annualInspectionRecord.createDept != null">
AND create_dept = #{annualInspectionRecord.createDept}
</if>
<if test="annualInspectionRecord.vehicleType != null and annualInspectionRecord.vehicleType != ''">
AND vehicle_type = #{annualInspectionRecord.vehicleType}
</if>
<if test="annualInspectionRecord.vehicleNo != null and annualInspectionRecord.vehicleNo != ''">
<bind name="vehicleNoLike" value="'%' + annualInspectionRecord.vehicleNo + '%'"/>
AND vehicle_no LIKE #{vehicleNoLike}
</if>
<if test="annualInspectionRecord.inspectionAssessmentDateStart != null">
AND inspection_assessment_date &gt;= #{annualInspectionRecord.inspectionAssessmentDateStart}
</if>
<if test="annualInspectionRecord.inspectionAssessmentDateEnd != null">
AND inspection_assessment_date &lt;= #{annualInspectionRecord.inspectionAssessmentDateEnd}
</if>
<if test="annualInspectionRecord.createTimeStart != null">
AND create_time &gt;= #{annualInspectionRecord.createTimeStart}
</if>
<if test="annualInspectionRecord.createTimeEnd != null">
AND create_time &lt;= #{annualInspectionRecord.createTimeEnd}
</if>
<include refid="QueryCondition"/>
ORDER BY create_time DESC
</select>
<select id="selectExpiryStat" resultType="org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO">
SELECT
COUNT(1) AS total,
SUM(CASE WHEN <include refid="ExpiryWithin30Condition"/> THEN 1 ELSE 0 END) AS within30,
SUM(CASE WHEN <include refid="ExpiryExpiredCondition"/> THEN 1 ELSE 0 END) AS expired
FROM
blade_annual_inspection_record
WHERE
<include refid="QueryCondition"/>
</select>
</mapper>
@@ -16,6 +16,7 @@
<result column="short_name" property="shortName"/>
<result column="full_name" property="fullName"/>
<result column="customer_nature" property="customerNature"/>
<result column="guangxi_top100" property="guangxiTop100"/>
<result column="unified_credit_code" property="unifiedCreditCode"/>
<result column="customer_type" property="customerType"/>
<result column="project_name" property="projectName"/>
@@ -29,6 +30,7 @@
<result column="dept_name" property="deptName"/>
<result column="invoice_tax_rate" property="invoiceTaxRate"/>
<result column="business_scope" property="businessScope"/>
<result column="network_freight_platform" property="networkFreightPlatform"/>
<result column="business_term_type" property="businessTermType"/>
<result column="business_end_date" property="businessEndDate"/>
<result column="registered_capital" property="registeredCapital"/>
@@ -61,6 +63,7 @@
short_name,
full_name,
customer_nature,
guangxi_top100,
unified_credit_code,
customer_type,
project_name,
@@ -74,6 +77,7 @@
dept_name,
invoice_tax_rate,
business_scope,
network_freight_platform,
business_term_type,
business_end_date,
registered_capital,
@@ -0,0 +1,21 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.transport.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
/**
* 客商发票联系信息 Mapper 接口
*
* @author Chill
*/
public interface CustomerInvoiceContactMapper extends BaseMapper<CustomerInvoiceContact> {
}
@@ -31,6 +31,7 @@ import org.springblade.core.mp.base.BaseService;
import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import java.util.List;
@@ -44,6 +45,8 @@ public interface IAnnualInspectionRecordService extends BaseService<AnnualInspec
IPage<AnnualInspectionRecordVO> selectAnnualInspectionRecordPage(IPage<AnnualInspectionRecordVO> page, AnnualInspectionRecordVO annualInspectionRecord);
AnnualInspectionRecordExpiryStatVO expiryStat(AnnualInspectionRecordVO annualInspectionRecord);
boolean submit(AnnualInspectionRecord annualInspectionRecord);
List<AnnualInspectionRecordExcel> importAnnualInspectionRecord(List<AnnualInspectionRecordExcel> data);
@@ -68,6 +68,7 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
/**
* 新增或修改客商档案
* <p>仅当 {@code customer.recordChange = true}(前端点「提交」)时写入变更记录;「保存」不落变更记录。</p>
*
* @param customer 客商档案
* @return 是否成功
@@ -36,13 +36,13 @@ import org.springblade.transport.excel.AnnualInspectionRecordExcel;
import org.springblade.transport.excel.AnnualInspectionRecordExportExcel;
import org.springblade.transport.mapper.AnnualInspectionRecordMapper;
import org.springblade.transport.pojo.entity.AnnualInspectionRecord;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordExpiryStatVO;
import org.springblade.transport.pojo.vo.AnnualInspectionRecordVO;
import org.springblade.transport.service.IAnnualInspectionRecordService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -74,6 +74,24 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl<AnnualIns
return page.setRecords(records);
}
@Override
public AnnualInspectionRecordExpiryStatVO expiryStat(AnnualInspectionRecordVO annualInspectionRecord) {
AnnualInspectionRecordExpiryStatVO stat = baseMapper.selectExpiryStat(annualInspectionRecord);
if (stat == null) {
stat = new AnnualInspectionRecordExpiryStatVO();
}
if (stat.getTotal() == null) {
stat.setTotal(0L);
}
if (stat.getWithin30() == null) {
stat.setWithin30(0L);
}
if (stat.getExpired() == null) {
stat.setExpired(0L);
}
return stat;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean submit(AnnualInspectionRecord annualInspectionRecord) {
@@ -131,6 +149,7 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl<AnnualIns
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, SHIP.equals(annualInspectionRecord.getVehicleType()) && Func.isEmpty(annualInspectionRecord.getShipInspectionType()), "船舶检验类型不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(annualInspectionRecord.getPassengerTypeLevel()) && !PASSENGER_TYPE_LEVELS.contains(annualInspectionRecord.getPassengerTypeLevel()), "客车类型及等级不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(annualInspectionRecord.getValidUntilDate()), "有效期截止日不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(annualInspectionRecord.getInspectionAssessmentDate()), "检测评定日期不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(annualInspectionRecord.getValidUntilDate()) && Func.isNotEmpty(annualInspectionRecord.getInspectionAssessmentDate()) && !annualInspectionRecord.getValidUntilDate().isAfter(annualInspectionRecord.getInspectionAssessmentDate()), "有效期截止日应大于检测评定日期");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(annualInspectionRecord.getFee()), "费用不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addLengthValidationError(validationErrors, annualInspectionRecord.getVehicleNo(), VEHICLE_NO_MAX_LENGTH, "车牌号/船号不能超过30字");
@@ -170,9 +189,6 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl<AnnualIns
annualInspectionRecord.setAssessmentUnit(trimToNull(annualInspectionRecord.getAssessmentUnit()));
annualInspectionRecord.setAttachments(trimToNull(annualInspectionRecord.getAttachments()));
annualInspectionRecord.setRemark(trimToNull(annualInspectionRecord.getRemark()));
if (annualInspectionRecord.getInspectionAssessmentDate() == null) {
annualInspectionRecord.setInspectionAssessmentDate(LocalDate.now());
}
if (VEHICLE.equals(annualInspectionRecord.getVehicleType())) {
annualInspectionRecord.setShipInspectionType(null);
} else {
@@ -203,6 +219,9 @@ public class AnnualInspectionRecordServiceImpl extends BaseServiceImpl<AnnualIns
if (Func.isEmpty(annualInspectionRecord.getValidUntilDate())) {
throw new ServiceException("有效期截止日不能为空");
}
if (Func.isEmpty(annualInspectionRecord.getInspectionAssessmentDate())) {
throw new ServiceException("检测评定日期不能为空");
}
if (!annualInspectionRecord.getValidUntilDate().isAfter(annualInspectionRecord.getInspectionAssessmentDate())) {
throw new ServiceException("有效期截止日应大于检测评定日期");
}
@@ -121,6 +121,16 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
@Override
@Transactional(rollbackFor = Exception.class)
public boolean submit(ContractManage contractManage) {
// 变更驳回后从编辑页提交,应重新进入变更审批,不能走临时合同提交流程。
if (Func.isNotEmpty(contractManage.getId())) {
ContractManage existing = loadExists(contractManage.getId());
if (Objects.equals(existing.getApprovalStatus(), STATUS_CHANGE_REJECTED)) {
if (Func.isEmpty(TransportBusinessSupport.trimToNull(contractManage.getChangeReason()))) {
contractManage.setChangeReason(resolveResubmitChangeReason(existing));
}
return submitChange(contractManage);
}
}
prepare(contractManage);
validateDraft(contractManage);
validateTemporary(contractManage);
@@ -262,6 +272,9 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
}
TransportBusinessSupport.validateRequired(request.getSettlementCurrency(), "请选择结算币种");
String changeReason = TransportBusinessSupport.trimToNull(request.getChangeReason());
if (Func.isEmpty(changeReason) && Objects.equals(source.getApprovalStatus(), STATUS_CHANGE_REJECTED)) {
changeReason = resolveResubmitChangeReason(source);
}
TransportBusinessSupport.validateRequired(changeReason, "请输入变更原因");
TransportBusinessSupport.validateLength(changeReason, 500, "变更原因不能超过500字");
ContractManage candidate = new ContractManage();
@@ -848,7 +861,8 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
private ContractManage loadEditable(Long id) {
ContractManage contractManage = loadExists(id);
if (!List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED).contains(contractManage.getApprovalStatus())) {
if (!List.of(STATUS_DRAFT, STATUS_WITHDRAWN, STATUS_REJECTED, STATUS_CHANGE_REJECTED)
.contains(contractManage.getApprovalStatus())) {
throw new ServiceException("当前状态不允许编辑");
}
if (!canOperateInCurrentScope(contractManage)) {
@@ -857,6 +871,21 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
return contractManage;
}
/**
* 变更驳回后重新提交时,优先沿用最近一次变更原因。
*/
private String resolveResubmitChangeReason(ContractManage contractManage) {
List<Map<String, Object>> records = parseChangeRecords(contractManage.getChangeRecordJson());
for (int index = records.size() - 1; index >= 0; index--) {
String changeReason = TransportBusinessSupport.trimToNull(
records.get(index).get("changeReason") == null ? null : String.valueOf(records.get(index).get("changeReason")));
if (Func.isNotEmpty(changeReason)) {
return changeReason;
}
}
return "重新提交变更";
}
private boolean canResubmitTemporary(ContractManage contractManage) {
String stage = contractManage.getContractStage();
String status = contractManage.getApprovalStatus();
@@ -46,6 +46,7 @@ import org.springblade.transport.mapper.CustomerChangeRecordMapper;
import org.springblade.transport.mapper.CustomerContactMapper;
import org.springblade.transport.mapper.CustomerCreditScoreDetailMapper;
import org.springblade.transport.mapper.CustomerCreditScoreMapper;
import org.springblade.transport.mapper.CustomerInvoiceContactMapper;
import org.springblade.transport.mapper.CustomerInvoiceInfoMapper;
import org.springblade.transport.mapper.CustomerReceiptAccountMapper;
import org.springblade.transport.mapper.UserCustomerScopeMapper;
@@ -59,6 +60,7 @@ import org.springblade.transport.pojo.entity.CustomerChangeRecord;
import org.springblade.transport.pojo.entity.CustomerContact;
import org.springblade.transport.pojo.entity.CustomerCreditScore;
import org.springblade.transport.pojo.entity.CustomerCreditScoreDetail;
import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
import org.springblade.transport.pojo.entity.CustomerInvoiceInfo;
import org.springblade.transport.pojo.entity.CustomerReceiptAccount;
import org.springblade.transport.pojo.vo.CustomerArchiveVO;
@@ -67,6 +69,7 @@ import org.springblade.transport.pojo.vo.CustomerContactVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreDetailVO;
import org.springblade.transport.pojo.vo.CustomerCreditScoreVO;
import org.springblade.transport.pojo.vo.CreditRatingStandardVO;
import org.springblade.transport.pojo.vo.CustomerInvoiceContactVO;
import org.springblade.transport.pojo.vo.CustomerInvoiceInfoVO;
import org.springblade.transport.pojo.vo.CustomerReceiptAccountVO;
import org.springblade.transport.service.ICustomerArchiveService;
@@ -108,6 +111,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
private final CustomerContactMapper contactMapper;
private final CustomerReceiptAccountMapper receiptAccountMapper;
private final CustomerInvoiceInfoMapper invoiceInfoMapper;
private final CustomerInvoiceContactMapper invoiceContactMapper;
private final CustomerCreditScoreMapper creditScoreMapper;
private final CustomerCreditScoreDetailMapper creditScoreDetailMapper;
private final CustomerChangeRecordMapper changeRecordMapper;
@@ -173,7 +177,8 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
if (created && result) {
grantNewCustomerToIncludedUsers(entity);
}
if (result && !created) {
// 仅「提交」写入变更记录;「保存」不落变更记录
if (result && !created && Boolean.TRUE.equals(customer.getRecordChange())) {
addChangeRecord(entity.getId(), "修改客商档案", before, entity, beforeDetail, customer);
}
return result;
@@ -292,6 +297,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
customer.setShortName(trimToNull(customer.getShortName()));
customer.setFullName(trimToEmpty(customer.getFullName()));
customer.setCustomerNature(trimToEmpty(customer.getCustomerNature()));
customer.setGuangxiTop100(normalizeYesNoFlag(customer.getGuangxiTop100()));
customer.setUnifiedCreditCode(trimToEmpty(customer.getUnifiedCreditCode()));
customer.setCustomerType(trimToEmpty(customer.getCustomerType()));
customer.setBusinessScope(trimToNull(customer.getBusinessScope()));
@@ -428,7 +434,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
deleteDetail(List.of(customerId), false);
insertContacts(customerId, customer.getContacts());
insertReceiptAccounts(customerId, customer.getReceiptAccounts());
insertInvoices(customerId, customer.getInvoices());
insertInvoices(customerId, customer);
if (Func.isNotEmpty(customer.getScores())) {
deleteScores(List.of(customerId));
insertScores(customerId, customer.getScores());
@@ -461,27 +467,111 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
}
}
private void insertInvoices(Long customerId, List<CustomerInvoiceInfoVO> invoices) {
private void insertInvoices(Long customerId, CustomerArchiveVO customer) {
List<CustomerInvoiceInfoVO> invoices = customer.getInvoices();
if (Func.isEmpty(invoices)) {
return;
}
boolean internal = isInternalCustomer(customer);
for (CustomerInvoiceInfoVO invoiceVO : invoices) {
if (Func.isEmpty(invoiceVO.getInvoiceTitle()) && Func.isEmpty(invoiceVO.getTaxNo())) {
continue;
}
if (Func.isEmpty(invoiceVO.getInvoiceType())) {
throw new ServiceException("发票类型不能为空");
if (Func.isEmpty(invoiceVO.getInvoiceTitle())) {
throw new ServiceException("企业全称不能为空");
}
if (!List.of("增值税专用发票", "普通发票").contains(invoiceVO.getInvoiceType())) {
throw new ServiceException("发票类型不合法");
if (Func.isEmpty(invoiceVO.getTaxNo())) {
throw new ServiceException("纳税人识别号不能为空");
}
if (Func.isEmpty(invoiceVO.getBankName())) {
throw new ServiceException("开户行名称不能为空");
}
if (Func.isEmpty(invoiceVO.getBankAccount())) {
throw new ServiceException("银行账号不能为空");
}
if (Func.isEmpty(invoiceVO.getRegisteredRegionName()) && Func.isEmpty(invoiceVO.getRegisteredAddress())) {
throw new ServiceException("注册地址不能为空");
}
if (Func.isEmpty(invoiceVO.getRegisteredDetailAddress())) {
throw new ServiceException("详细地址不能为空");
}
invoiceVO.setRegisteredAddress(buildAddress(invoiceVO.getRegisteredRegionName(), invoiceVO.getRegisteredDetailAddress(), invoiceVO.getRegisteredAddress()));
invoiceVO.setReceiverAddress(buildAddress(invoiceVO.getReceiverRegionName(), invoiceVO.getReceiverDetailAddress(), invoiceVO.getReceiverAddress()));
CustomerInvoiceInfo invoice = Objects.requireNonNull(BeanUtil.copyProperties(invoiceVO, CustomerInvoiceInfo.class));
invoice.setId(IdWorker.getId());
invoice.setCustomerId(customerId);
invoice.setStatus(STATUS_ENABLED);
invoiceInfoMapper.insert(invoice);
insertInvoiceContacts(invoice.getId(), invoiceVO.getContacts(), internal, customer);
}
}
private void insertInvoiceContacts(Long invoiceId, List<CustomerInvoiceContactVO> contacts, boolean internal,
CustomerArchiveVO customer) {
if (Func.isEmpty(contacts)) {
return;
}
for (CustomerInvoiceContactVO contactVO : contacts) {
if (isBlankInvoiceContact(contactVO)) {
continue;
}
if (Func.isEmpty(contactVO.getContactName())) {
throw new ServiceException("发票联系人不能为空");
}
if (Func.isEmpty(contactVO.getContactPhone())) {
throw new ServiceException("发票联系电话不能为空");
}
validateInvoiceContactEmail(contactVO.getEmail());
if (Func.isNotEmpty(contactVO.getRemark()) && contactVO.getRemark().length() > 200) {
throw new ServiceException("发票联系信息备注最多200个字");
}
if (internal && Func.isEmpty(contactVO.getDeptIds()) && Func.isEmpty(contactVO.getDeptNames())) {
throw new ServiceException("内部组织客商必须选择发票联系信息所属部门");
}
if (!internal) {
if (Func.isEmpty(contactVO.getDeptIds())) {
contactVO.setDeptIds(customer.getDeptIds());
}
if (Func.isEmpty(contactVO.getDeptNames())) {
contactVO.setDeptNames(Func.isNotEmpty(customer.getFullName()) ? customer.getFullName() : customer.getDeptName());
}
}
CustomerInvoiceContact contact = Objects.requireNonNull(BeanUtil.copyProperties(contactVO, CustomerInvoiceContact.class));
contact.setId(IdWorker.getId());
contact.setInvoiceId(invoiceId);
contact.setStatus(STATUS_ENABLED);
invoiceContactMapper.insert(contact);
}
}
private boolean isBlankInvoiceContact(CustomerInvoiceContactVO contactVO) {
return Func.isEmpty(contactVO.getContactName())
&& Func.isEmpty(contactVO.getContactPhone())
&& Func.isEmpty(contactVO.getEmail())
&& Func.isEmpty(contactVO.getDeptIds())
&& Func.isEmpty(contactVO.getDeptNames())
&& Func.isEmpty(contactVO.getRemark());
}
private void validateInvoiceContactEmail(String email) {
if (Func.isEmpty(email)) {
return;
}
if (email.length() > 100) {
throw new ServiceException("发票联系邮箱不能超过100个字");
}
if (!email.matches("^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$")) {
throw new ServiceException("发票联系邮箱格式不正确");
}
}
private boolean isInternalCustomer(CustomerArchiveVO customer) {
String customerKind = customer.getCustomerKind();
if (Func.isNotEmpty(customerKind)) {
return "internal".equals(customerKind) || "内部组织".equals(customerKind);
}
return "internal".equals(customer.getCustomerNature()) || "内部组织".equals(customer.getCustomerNature());
}
private void insertScores(Long customerId, List<CustomerCreditScoreVO> scores) {
for (CustomerCreditScoreVO scoreVO : scores) {
if (Func.isEmpty(scoreVO.getQuantificationId()) && Func.isEmpty(scoreVO.getScoreDate())) {
@@ -529,6 +619,15 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
private void deleteDetail(List<Long> customerIds, boolean deleteScores) {
contactMapper.update(null, Wrappers.<CustomerContact>lambdaUpdate().in(CustomerContact::getCustomerId, customerIds).set(CustomerContact::getIsDeleted, 1));
receiptAccountMapper.update(null, Wrappers.<CustomerReceiptAccount>lambdaUpdate().in(CustomerReceiptAccount::getCustomerId, customerIds).set(CustomerReceiptAccount::getIsDeleted, 1));
List<CustomerInvoiceInfo> invoices = invoiceInfoMapper.selectList(Wrappers.<CustomerInvoiceInfo>lambdaQuery()
.in(CustomerInvoiceInfo::getCustomerId, customerIds)
.eq(CustomerInvoiceInfo::getIsDeleted, 0));
if (Func.isNotEmpty(invoices)) {
List<Long> invoiceIds = invoices.stream().map(CustomerInvoiceInfo::getId).toList();
invoiceContactMapper.update(null, Wrappers.<CustomerInvoiceContact>lambdaUpdate()
.in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
.set(CustomerInvoiceContact::getIsDeleted, 1));
}
invoiceInfoMapper.update(null, Wrappers.<CustomerInvoiceInfo>lambdaUpdate().in(CustomerInvoiceInfo::getCustomerId, customerIds).set(CustomerInvoiceInfo::getIsDeleted, 1));
if (deleteScores) {
deleteScores(customerIds);
@@ -565,12 +664,27 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
}
private List<CustomerInvoiceInfoVO> loadInvoices(Long customerId) {
return invoiceInfoMapper.selectList(Wrappers.<CustomerInvoiceInfo>lambdaQuery()
.eq(CustomerInvoiceInfo::getCustomerId, customerId)
.eq(CustomerInvoiceInfo::getIsDeleted, 0)
.orderByDesc(CustomerInvoiceInfo::getIsDefault)
.orderByAsc(CustomerInvoiceInfo::getCreateTime))
.stream().map(invoice -> Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class))).toList();
List<CustomerInvoiceInfo> invoices = invoiceInfoMapper.selectList(Wrappers.<CustomerInvoiceInfo>lambdaQuery()
.eq(CustomerInvoiceInfo::getCustomerId, customerId)
.eq(CustomerInvoiceInfo::getIsDeleted, 0)
.orderByDesc(CustomerInvoiceInfo::getIsDefault)
.orderByAsc(CustomerInvoiceInfo::getCreateTime));
if (Func.isEmpty(invoices)) {
return new ArrayList<>();
}
List<Long> invoiceIds = invoices.stream().map(CustomerInvoiceInfo::getId).toList();
Map<Long, List<CustomerInvoiceContactVO>> contactMap = invoiceContactMapper.selectList(Wrappers.<CustomerInvoiceContact>lambdaQuery()
.in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
.eq(CustomerInvoiceContact::getIsDeleted, 0)
.orderByAsc(CustomerInvoiceContact::getCreateTime))
.stream()
.map(contact -> Objects.requireNonNull(BeanUtil.copyProperties(contact, CustomerInvoiceContactVO.class)))
.collect(Collectors.groupingBy(CustomerInvoiceContactVO::getInvoiceId));
return invoices.stream().map(invoice -> {
CustomerInvoiceInfoVO invoiceVO = Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class));
invoiceVO.setContacts(contactMap.getOrDefault(invoice.getId(), new ArrayList<>()));
return invoiceVO;
}).toList();
}
private List<CustomerCreditScoreVO> loadScores(Long customerId) {
@@ -621,6 +735,16 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
return value != null && value.compareTo(BigDecimal.ONE.negate()) == 0 ? null : value;
}
private Integer normalizeYesNoFlag(Integer value) {
if (value == null || Objects.equals(value, -1)) {
return null;
}
if (value == 0 || value == 1) {
return value;
}
throw new ServiceException("是否广西百强只能选择是或否");
}
private Object parseScoreAttachments(String value) {
if (Func.isEmpty(value)) {
return new ArrayList<>();
@@ -928,6 +1052,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
snapshot.put("客商简称", customer.getShortName());
snapshot.put("客商全称", customer.getFullName());
snapshot.put("客商性质", customer.getCustomerNature());
snapshot.put("是否广西百强", customer.getGuangxiTop100());
snapshot.put("统一社会信用代码", customer.getUnifiedCreditCode());
snapshot.put("客商类型", customer.getCustomerType());
snapshot.put("所属项目", customer.getProjectName());
@@ -55,6 +55,7 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl<InsuranceOc
private static final int NAME_MAX_LENGTH = 100;
private static final int MAPPING_VALUE_MAX_LENGTH = 100;
private static final List<String> VEHICLE_TYPES = List.of("车辆", "船舶");
private static final List<String> INSURANCE_FIELD_KEYS = List.of(
"保险类型", "保单号", "开始日期", "结束日期", "保额", "保费", "发票号", "开票日期", "备注"
);
@@ -64,6 +65,7 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl<InsuranceOc
LambdaQueryWrapper<InsuranceOcrTemplate> queryWrapper = Wrappers.<InsuranceOcrTemplate>lambdaQuery()
.eq(InsuranceOcrTemplate::getIsDeleted, 0)
.like(StringUtil.isNotBlank(insuranceOcrTemplate.getName()), InsuranceOcrTemplate::getName, insuranceOcrTemplate.getName())
.eq(StringUtil.isNotBlank(insuranceOcrTemplate.getVehicleType()), InsuranceOcrTemplate::getVehicleType, insuranceOcrTemplate.getVehicleType())
.orderByDesc(InsuranceOcrTemplate::getCreateTime);
return InsuranceOcrTemplateWrapper.build().pageVO(page(page, queryWrapper));
}
@@ -79,6 +81,7 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl<InsuranceOc
insuranceOcrTemplate.setTenantId(oldTemplate.getTenantId());
}
insuranceOcrTemplate.setName(trimToNull(insuranceOcrTemplate.getName()));
insuranceOcrTemplate.setVehicleType(trimToNull(insuranceOcrTemplate.getVehicleType()));
insuranceOcrTemplate.setMappingConfig(trimToNull(insuranceOcrTemplate.getMappingConfig()));
if (created && insuranceOcrTemplate.getStatus() == null) {
insuranceOcrTemplate.setStatus(1);
@@ -94,6 +97,12 @@ public class InsuranceOcrTemplateServiceImpl extends BaseServiceImpl<InsuranceOc
if (insuranceOcrTemplate.getName().length() > NAME_MAX_LENGTH) {
throw new ServiceException("模板名称不能超过100个字符");
}
if (StringUtil.isBlank(insuranceOcrTemplate.getVehicleType())) {
throw new ServiceException("请选择车船类型");
}
if (!VEHICLE_TYPES.contains(insuranceOcrTemplate.getVehicleType())) {
throw new ServiceException("车船类型仅支持车辆或船舶");
}
Long nameCount = count(Wrappers.<InsuranceOcrTemplate>lambdaQuery()
.eq(InsuranceOcrTemplate::getIsDeleted, 0)
.eq(InsuranceOcrTemplate::getName, insuranceOcrTemplate.getName())
@@ -85,6 +85,7 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl<InsuranceRecordM
private static final Set<String> INSURANCE_TYPES = Set.of("交强险", "商业险", "承运人责任险", "货运险", "船舶险");
private static final Set<String> SUPPORT_FILE_TYPES = Set.of("jpg", "jpeg", "png", "bmp");
private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4})[-/.年](\\d{1,2})[-/.月](\\d{1,2})日?");
private static final Pattern POLICY_NO_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");
private final IBaiduOcrService baiduOcrService;
private final IInsuranceOcrTemplateService insuranceOcrTemplateService;
@@ -157,6 +158,11 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl<InsuranceRecordM
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(insuranceRecord.getInsuranceType()), "保险类型不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(insuranceRecord.getInsuranceType()) && !INSURANCE_TYPES.contains(insuranceRecord.getInsuranceType()), "保险类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(insuranceRecord.getPolicyNo()), "保单号不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(
validationErrors,
Func.isNotEmpty(insuranceRecord.getPolicyNo()) && !POLICY_NO_PATTERN.matcher(insuranceRecord.getPolicyNo()).matches(),
"保单号只能输入数字、字母"
);
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(insuranceRecord.getStartDate()), "开始日期不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(insuranceRecord.getEndDate()), "结束日期不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(insuranceRecord.getStartDate()) && Func.isNotEmpty(insuranceRecord.getEndDate()) && insuranceRecord.getEndDate().isBefore(insuranceRecord.getStartDate()), "结束日期不能早于开始日期");
@@ -348,6 +354,9 @@ public class InsuranceRecordServiceImpl extends BaseServiceImpl<InsuranceRecordM
if (Func.isEmpty(insuranceRecord.getPolicyNo())) {
throw new ServiceException("保单号不能为空");
}
if (!POLICY_NO_PATTERN.matcher(insuranceRecord.getPolicyNo()).matches()) {
throw new ServiceException("保单号只能输入数字、字母");
}
if (Func.isEmpty(insuranceRecord.getStartDate())) {
throw new ServiceException("开始日期不能为空");
}
@@ -37,6 +37,7 @@ import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.transport.mapper.CustomerArchiveMapper;
import org.springblade.transport.mapper.CustomerContactMapper;
import org.springblade.transport.mapper.CustomerInvoiceContactMapper;
import org.springblade.transport.mapper.CustomerInvoiceInfoMapper;
import org.springblade.transport.mapper.ContractManageMapper;
import org.springblade.transport.mapper.FormalSettlementDetailMapper;
@@ -52,6 +53,7 @@ import org.springblade.transport.pojo.dto.InvoiceApplicationSaveRequest;
import org.springblade.transport.pojo.dto.InvoiceApplicationStatusRequest;
import org.springblade.transport.pojo.entity.CustomerArchive;
import org.springblade.transport.pojo.entity.CustomerContact;
import org.springblade.transport.pojo.entity.CustomerInvoiceContact;
import org.springblade.transport.pojo.entity.CustomerInvoiceInfo;
import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.entity.FormalSettlement;
@@ -63,6 +65,8 @@ 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.CustomerInvoiceContactVO;
import org.springblade.transport.pojo.vo.CustomerInvoiceInfoVO;
import org.springblade.transport.pojo.vo.InvoiceApplicationSheetVO;
import org.springblade.transport.pojo.vo.InvoiceApplicationVO;
import org.springblade.transport.service.IInvoiceApplicationService;
@@ -75,6 +79,7 @@ import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -111,6 +116,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
private final ContractManageMapper contractManageMapper;
private final CustomerArchiveMapper customerArchiveMapper;
private final CustomerInvoiceInfoMapper customerInvoiceInfoMapper;
private final CustomerInvoiceContactMapper customerInvoiceContactMapper;
private final CustomerContactMapper customerContactMapper;
@Override
@@ -267,7 +273,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
String customerName = "receivable".equals(first.getSettlementType())
? first.getPayerName() : first.getPayeeName();
CustomerArchive customer = findCustomer(customerName);
List<CustomerInvoiceInfo> invoiceInfos = customer == null ? List.of() : activeInvoiceInfos(customer.getId());
List<CustomerInvoiceInfoVO> invoiceInfos = customer == null ? List.of() : activeInvoiceInfoVOs(customer.getId());
Map<String, Object> result = new LinkedHashMap<>();
result.put("issuerName", first.getPayeeName());
result.put("receiverName", first.getPayerName());
@@ -321,8 +327,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
}
CustomerArchive customer = findCustomer(first.getPayerName());
if (customer == null) throw new ServiceException("未找到正式结算单受票方对应的客商档案");
List<CustomerInvoiceInfo> invoiceInfos = activeInvoiceInfos(customer.getId());
CustomerInvoiceInfo invoiceInfo = invoiceInfos.stream()
List<CustomerInvoiceInfoVO> invoiceInfos = activeInvoiceInfoVOs(customer.getId());
CustomerInvoiceInfoVO invoiceInfo = invoiceInfos.stream()
.filter(item -> Objects.equals(item.getId(), request.getReceiverInvoiceInfoId()))
.findFirst().orElseThrow(() -> new ServiceException("请选择受票方有效的开票信息"));
String departmentEmails = normalizeDepartmentEmails(request.getDepartmentEmails(), invoiceInfos);
@@ -655,8 +661,32 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
.orderByAsc(CustomerInvoiceInfo::getCreateTime));
}
private List<String> invoiceInfoEmails(List<CustomerInvoiceInfo> invoiceInfos) {
return invoiceInfos.stream().map(CustomerInvoiceInfo::getEmail).filter(Func::isNotEmpty)
private List<CustomerInvoiceInfoVO> activeInvoiceInfoVOs(Long customerId) {
List<CustomerInvoiceInfo> invoiceInfos = activeInvoiceInfos(customerId);
if (Func.isEmpty(invoiceInfos)) {
return List.of();
}
List<Long> invoiceIds = invoiceInfos.stream().map(CustomerInvoiceInfo::getId).toList();
Map<Long, List<CustomerInvoiceContactVO>> contactMap = customerInvoiceContactMapper.selectList(
Wrappers.<CustomerInvoiceContact>lambdaQuery()
.in(CustomerInvoiceContact::getInvoiceId, invoiceIds)
.eq(CustomerInvoiceContact::getIsDeleted, 0)
.orderByAsc(CustomerInvoiceContact::getCreateTime))
.stream()
.map(contact -> Objects.requireNonNull(BeanUtil.copyProperties(contact, CustomerInvoiceContactVO.class)))
.collect(Collectors.groupingBy(CustomerInvoiceContactVO::getInvoiceId));
return invoiceInfos.stream().map(invoice -> {
CustomerInvoiceInfoVO invoiceVO = Objects.requireNonNull(BeanUtil.copyProperties(invoice, CustomerInvoiceInfoVO.class));
invoiceVO.setContacts(contactMap.getOrDefault(invoice.getId(), new ArrayList<>()));
return invoiceVO;
}).toList();
}
private List<String> invoiceInfoEmails(List<CustomerInvoiceInfoVO> invoiceInfos) {
return invoiceInfos.stream()
.flatMap(invoice -> (invoice.getContacts() == null ? List.<CustomerInvoiceContactVO>of() : invoice.getContacts()).stream())
.map(CustomerInvoiceContactVO::getEmail)
.filter(Func::isNotEmpty)
.flatMap(value -> splitEmails(value).stream())
.collect(Collectors.toMap(email -> email.toLowerCase(Locale.ROOT), Function.identity(),
(first, duplicate) -> first, LinkedHashMap::new)).values().stream().toList();
@@ -708,7 +738,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
.likeRight(InvoiceApplication::getApplicationNo, prefix)) + 1);
}
private String normalizeDepartmentEmails(String value, List<CustomerInvoiceInfo> invoiceInfos) {
private String normalizeDepartmentEmails(String value, List<CustomerInvoiceInfoVO> invoiceInfos) {
Map<String, String> configuredEmails = invoiceInfoEmails(invoiceInfos).stream()
.collect(Collectors.toMap(email -> email.toLowerCase(Locale.ROOT), Function.identity(),
(first, duplicate) -> first, LinkedHashMap::new));
@@ -12,7 +12,9 @@ import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.DictBizCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.DictBiz;
import org.springblade.transport.excel.OtherExpenseRecordExcel;
import org.springblade.transport.excel.OtherExpenseRecordExportExcel;
import org.springblade.transport.mapper.OtherExpenseRecordMapper;
@@ -28,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 其他费用记录 服务实现类
@@ -43,7 +46,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl<OtherExpenseR
private static final int MONEY_SCALE = 2;
private static final String VEHICLE = "车辆";
private static final String SHIP = "船舶";
private static final Set<String> EXPENSE_TYPES = Set.of("过路费", "停车费", "维修费", "保险费", "年检费", "装卸费", "其他");
private static final String EXPENSE_TYPE_DICT_CODE = "other_fee_category";
@Override
public IPage<OtherExpenseRecordVO> selectOtherExpenseRecordPage(IPage<OtherExpenseRecordVO> page, OtherExpenseRecordVO otherExpenseRecord) {
@@ -103,7 +106,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl<OtherExpenseR
List<String> validationErrors = new ArrayList<>();
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getExpenseDate()), "费用日期不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getExpenseType()), "费用类型不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getExpenseType()) && !EXPENSE_TYPES.contains(otherExpenseRecord.getExpenseType()), "费用类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getExpenseType()) && !loadExpenseTypeValues().contains(otherExpenseRecord.getExpenseType()), "费用类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getVehicleType()), "车船类型不能为空");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isNotEmpty(otherExpenseRecord.getVehicleType()) && !VEHICLE.equals(otherExpenseRecord.getVehicleType()) && !SHIP.equals(otherExpenseRecord.getVehicleType()), "车船类型不正确");
org.springblade.common.excel.ImportFailureExcelUtil.addValidationError(validationErrors, Func.isEmpty(otherExpenseRecord.getVehicleNo()), "车牌号/船号不能为空");
@@ -152,7 +155,7 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl<OtherExpenseR
if (Func.isEmpty(otherExpenseRecord.getExpenseType())) {
throw new ServiceException("费用类型不能为空");
}
if (!EXPENSE_TYPES.contains(otherExpenseRecord.getExpenseType())) {
if (!loadExpenseTypeValues().contains(otherExpenseRecord.getExpenseType())) {
throw new ServiceException("费用类型不正确");
}
if (Func.isEmpty(otherExpenseRecord.getVehicleType())) {
@@ -217,4 +220,17 @@ public class OtherExpenseRecordServiceImpl extends BaseServiceImpl<OtherExpenseR
return trimValue.isEmpty() ? null : trimValue;
}
private Set<String> loadExpenseTypeValues() {
List<DictBiz> expenseTypes = DictBizCache.getList(EXPENSE_TYPE_DICT_CODE);
if (Func.isEmpty(expenseTypes)) {
return Set.of();
}
return expenseTypes.stream()
.map(DictBiz::getDictValue)
.filter(Objects::nonNull)
.map(String::trim)
.filter(Func::isNotEmpty)
.collect(Collectors.toSet());
}
}
@@ -1565,11 +1565,11 @@ public class ReceivablePayableDetailServiceImpl
private BigDecimal calculateRule(Map<String, Object> rule, Waybill waybill) {
String element = stringValue(rule, "billingElement", ""); String type = stringValue(rule, "billingType", "");
BigDecimal base = measure(rule, waybill); BigDecimal unit = decimal(rule.get("unitPrice"));
BigDecimal measuredBase = measure(rule, waybill); BigDecimal unit = decimal(rule.get("unitPrice"));
List<Map<String, Object>> ranges = ranges(rule);
boolean intervalUnitPrice = "区间单价".equals(type);
boolean intervalFlatPrice = type.contains("区间") && type.contains("一口价");
if (!intervalUnitPrice && !intervalFlatPrice) base = applyMinimum(base, rule, waybill);
BigDecimal base = (!intervalUnitPrice && !intervalFlatPrice) ? applyMinimum(measuredBase, rule, waybill) : measuredBase;
if ("固定一口价".equals(type)) return unit.setScale(2, RoundingMode.HALF_UP);
if (ranges.isEmpty() || "固定单价".equals(type)) return base.multiply(unit).setScale(2, RoundingMode.HALF_UP);
if (intervalFlatPrice) return range(ranges, base).map(r -> decimal(r.get("unitPrice")).signum() == 0 ? unit : decimal(r.get("unitPrice"))).orElse(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);