1.1.48 分款派发给客户经理逻辑

dev
yinq 1 month ago
parent 5e3febe700
commit b643fb9b70

@ -27,6 +27,7 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentVo;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentBo;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentDispatchBo;
import org.dromara.oa.erp.service.IErpFinAccountInstallmentService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
@ -148,4 +149,15 @@ public class ErpFinAccountInstallmentController extends BaseController {
}
/**
* 稿 ->
*/
@SaCheckPermission("oa/erp:finAccountInstallment:dispatch")
@Log(title = "分款信息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/dispatch")
public R<Void> dispatch(@Validated @RequestBody ErpFinAccountInstallmentDispatchBo bo) {
return toAjax(erpFinAccountInstallmentService.dispatchToManagers(bo));
}
}

@ -26,7 +26,7 @@ public class ErpFinAccountInstallment extends TenantEntity {
/**
* ID
*/
@TableId(value = "account_installment_id", type = IdType.AUTO)
@TableId(value = "account_installment_id", type = IdType.ASSIGN_ID)
private Long accountInstallmentId;
/**
@ -65,14 +65,39 @@ public class ErpFinAccountInstallment extends TenantEntity {
private String remark;
/**
* 0稿 1 2
* flow_statusdraft/waiting/finish/back
*/
private String flowStatus;
/**
* ID
*/
private Long dispatchUserId;
/**
* ID
*/
private Long dispatchDeptId;
/**
*
*/
private Date dispatchDate;
/**
* 0 1 2 installment_status
*/
private String installmentStatus;
/**
*
* ID
*/
private Long accountManageId;
private String accountManagerIds;
/**
* 0 1
*/
@TableLogic
private String delFlag;
}

@ -68,6 +68,11 @@ public class ErpFinAccountInstallmentDetail extends BaseEntity {
*/
private String remark;
/**
* 0 1
*/
@TableLogic
private String delFlag;
@TableField(exist = false)
private String contractCode;

@ -9,6 +9,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
@ -65,15 +66,39 @@ public class ErpFinAccountInstallmentBo extends BaseEntity {
private String remark;
/**
* 0稿 1 2
* flow_status
*/
private String flowStatus;
/**
* ID
*/
private Long dispatchUserId;
/**
* ID
*/
private Long dispatchDeptId;
/**
*
*/
private Date dispatchDate;
/**
* 0 1 2 installment_status
*/
@NotBlank(message = "分款状态0草稿 1已发出 2分款完成不能为空", groups = { AddGroup.class, EditGroup.class })
private String installmentStatus;
/**
*
* ID
*/
private Long accountManageId;
private String accountManagerIds;
/**
* ID accountManagerIds
*/
private List<Long> managerUserIds;
}

@ -0,0 +1,36 @@
package org.dromara.oa.erp.domain.bo;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt;
import java.util.List;
import java.util.Map;
/**
*
*/
@Data
public class ErpFinAccountInstallmentDispatchBo {
@NotEmpty(message = "请选择要派发的回款记录")
private List<Long> accountInstallmentIds;
@NotEmpty(message = "请至少选择一名客户经理")
private List<Long> managerUserIds;
/**
* FKSH
*/
private String flowCode;
/**
* accountManagerIdignore
*/
private Map<String, Object> variables;
/**
* businessId=businessCodebusinessTitle
*/
private List<RemoteFlowInstanceBizExt> flowBizList;
}

@ -13,6 +13,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@ -79,17 +80,53 @@ public class ErpFinAccountInstallmentVo implements Serializable {
private String remark;
/**
* 0稿 1 2
* flow_status
*/
@ExcelProperty(value = "流程状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "flow_status")
private String flowStatus;
/**
* ID
*/
private Long dispatchUserId;
/**
* ID
*/
private Long dispatchDeptId;
/**
*
*/
private Date dispatchDate;
/**
* 0 1 2 installment_status
*/
@ExcelProperty(value = "分款状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=草稿,1=已发出,2=分款完成")
@ExcelDictFormat(readConverterExp = "0=未分款,1=已派发,2=分款完成")
private String installmentStatus;
/**
*
* ID
*/
@ExcelProperty(value = "客户经理")
private Long accountManageId;
private String accountManagerIds;
/**
* ID使
*/
private List<Long> managerUserIds;
/**
*
*/
private String managerNickNames;
/**
*
*/
private Boolean canAllocate;
}

@ -3,6 +3,7 @@ package org.dromara.oa.erp.service;
import org.dromara.oa.erp.domain.ErpFinAccountInstallment;
import org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentVo;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentBo;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentDispatchBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@ -74,4 +75,19 @@ public interface IErpFinAccountInstallmentService {
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 稿
*/
Boolean dispatchToManagers(ErpFinAccountInstallmentDispatchBo bo);
/**
*
*/
void validateAllocatePermission(Long accountInstallmentId);
/**
*
*/
void recalculateInstallmentStatus(Long accountInstallmentId);
}

@ -20,6 +20,7 @@ import org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentDetailVo;
import org.dromara.oa.erp.domain.ErpFinAccountInstallmentDetail;
import org.dromara.oa.erp.mapper.ErpFinAccountInstallmentDetailMapper;
import org.dromara.oa.erp.service.IErpFinAccountInstallmentDetailService;
import org.dromara.oa.erp.service.IErpFinAccountInstallmentService;
import org.dromara.oa.erp.service.IErpProjectPlanService;
import org.springframework.transaction.annotation.Transactional;
@ -44,6 +45,8 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
private final ErpFinAccountInstallmentMapper finAccountInstallmentMapper;
private final IErpFinAccountInstallmentService finAccountInstallmentService;
private final IErpProjectPlanService erpProjectPlanService;
/**
@ -150,6 +153,7 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
.eq(ErpContractPaymentMethod::getContractId, ErpFinAccountInstallmentDetail::getContractId)
.eq(ErpContractPaymentMethod::getPaymentStageId, ErpFinAccountInstallmentDetail::getPaymentStageId)
.eq(ErpContractPaymentMethod::getDelFlag, "0"))
.eq(ErpFinAccountInstallmentDetail::getDelFlag, "0")
.eq(bo.getAccountInstallmentId() != null, ErpFinAccountInstallmentDetail::getAccountInstallmentId, bo.getAccountInstallmentId())
.eq(bo.getProjectId() != null, ErpFinAccountInstallmentDetail::getProjectId, bo.getProjectId())
.eq(StringUtils.isNotBlank(bo.getProjectCode()), ErpFinAccountInstallmentDetail::getProjectCode, bo.getProjectCode())
@ -169,16 +173,16 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(ErpFinAccountInstallmentDetailBo bo) {
finAccountInstallmentService.validateAllocatePermission(bo.getAccountInstallmentId());
ErpFinAccountInstallmentDetail add = MapstructUtils.convert(bo, ErpFinAccountInstallmentDetail.class);
if (StringUtils.isBlank(add.getDelFlag())) {
add.setDelFlag("0");
}
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setInstallmentDetailId(add.getInstallmentDetailId());
}
ErpFinAccountInstallment erpFinAccountInstallment = finAccountInstallmentMapper.selectById(bo.getAccountInstallmentId());
erpFinAccountInstallment.setInstallmentStatus(bo.getInstallmentStatus());
finAccountInstallmentMapper.updateById(erpFinAccountInstallment);
if (flag) {
finAccountInstallmentService.recalculateInstallmentStatus(bo.getAccountInstallmentId());
syncContractCollectionStage(bo.getContractId(), bo.getProjectId(), bo.getPaymentStageId(),
bo.getAccountInstallmentId());
}
@ -194,14 +198,13 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(ErpFinAccountInstallmentDetailBo bo) {
finAccountInstallmentService.validateAllocatePermission(bo.getAccountInstallmentId());
ErpFinAccountInstallmentDetail old = baseMapper.selectById(bo.getInstallmentDetailId());
ErpFinAccountInstallmentDetail update = MapstructUtils.convert(bo, ErpFinAccountInstallmentDetail.class);
validEntityBeforeSave(update);
ErpFinAccountInstallment erpFinAccountInstallment = finAccountInstallmentMapper.selectById(bo.getAccountInstallmentId());
erpFinAccountInstallment.setInstallmentStatus(bo.getInstallmentStatus());
finAccountInstallmentMapper.updateById(erpFinAccountInstallment);
boolean ok = baseMapper.updateById(update) > 0;
if (ok) {
finAccountInstallmentService.recalculateInstallmentStatus(bo.getAccountInstallmentId());
if (old != null) {
syncContractCollectionStage(old.getContractId(), old.getProjectId(), old.getPaymentStageId(),
bo.getAccountInstallmentId());
@ -220,16 +223,12 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
}
/**
*
*
* @param ids
* @param isValid
* @return
* {@link ErpFinAccountInstallmentDetail#delFlag} @TableLogic
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
// 预留业务校验
}
if (ids == null || ids.isEmpty()) {
return true;
@ -261,16 +260,15 @@ public class ErpFinAccountInstallmentDetailServiceImpl implements IErpFinAccount
*/
@Override
public Boolean deleteAccountInstallmentDetail(ErpFinAccountInstallmentDetailBo bo) {
finAccountInstallmentService.validateAllocatePermission(bo.getAccountInstallmentId());
ErpFinAccountInstallmentDetail row = bo.getInstallmentDetailId() == null ? null : baseMapper.selectById(bo.getInstallmentDetailId());
ErpFinAccountInstallment erpFinAccountInstallment = new ErpFinAccountInstallment();
erpFinAccountInstallment.setAccountInstallmentId(bo.getAccountInstallmentId());
erpFinAccountInstallment.setInstallmentStatus(bo.getInstallmentStatus());
finAccountInstallmentMapper.updateById(erpFinAccountInstallment);
boolean ok = baseMapper.deleteById(bo.getInstallmentDetailId()) > 0;
if (ok && row != null) {
syncContractCollectionStage(row.getContractId(), row.getProjectId(), row.getPaymentStageId());
if (ok) {
finAccountInstallmentService.recalculateInstallmentStatus(bo.getAccountInstallmentId());
if (row != null) {
syncContractCollectionStage(row.getContractId(), row.getProjectId(), row.getPaymentStageId());
}
}
return ok;
}

@ -1,31 +1,50 @@
package org.dromara.oa.erp.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.oa.crm.domain.CrmShippingTariff;
import org.dromara.oa.crm.domain.bo.CrmShippingTariffBo;
import org.dromara.system.api.RemoteCodeRuleService;
import org.springframework.stereotype.Service;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentBo;
import org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.oa.erp.domain.ErpFinAccountInstallment;
import org.dromara.oa.erp.domain.ErpFinAccountInstallmentDetail;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentBo;
import org.dromara.oa.erp.domain.bo.ErpFinAccountInstallmentDispatchBo;
import org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentVo;
import org.dromara.oa.erp.mapper.ErpFinAccountInstallmentDetailMapper;
import org.dromara.oa.erp.mapper.ErpFinAccountInstallmentMapper;
import org.dromara.oa.erp.service.IErpFinAccountInstallmentService;
import org.dromara.system.api.RemoteCodeRuleService;
import org.dromara.system.api.RemoteUserService;
import org.dromara.workflow.api.RemoteWorkflowService;
import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt;
import org.dromara.workflow.api.domain.RemoteStartProcess;
import org.dromara.workflow.api.event.ProcessEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Service
@ -33,81 +52,139 @@ import java.util.Collection;
* @author xins
* @date 2026-03-09
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ErpFinAccountInstallmentServiceImpl implements IErpFinAccountInstallmentService {
private static final String ACCOUNT_INSTALLMENT_CODE_RULE = "1031";
private static final String FLOW_CODE_FKSH = "FKSH";
private static final String PERM_DISPATCH = "oa/erp:finAccountInstallment:dispatch";
/** 分款状态字典 installment_status0未分款 1已派发 2分款完成 */
private static final String INSTALLMENT_STATUS_NOT_ALLOCATED = "0";
private static final String INSTALLMENT_STATUS_DISPATCHED = "1";
private static final String INSTALLMENT_STATUS_COMPLETE = "2";
private final ErpFinAccountInstallmentMapper baseMapper;
private final ErpFinAccountInstallmentDetailMapper detailMapper;
@DubboReference(timeout = 30000)
private RemoteCodeRuleService remoteCodeRuleService;
/**
*
*
* @param accountInstallmentId
* @return
*/
@DubboReference
private RemoteUserService remoteUserService;
@DubboReference
private RemoteWorkflowService remoteWorkflowService;
@Override
public ErpFinAccountInstallmentVo queryById(Long accountInstallmentId){
return baseMapper.selectVoById(accountInstallmentId);
public ErpFinAccountInstallmentVo queryById(Long accountInstallmentId) {
ErpFinAccountInstallmentVo vo = baseMapper.selectVoById(accountInstallmentId);
if (vo != null) {
fillManagerInfo(List.of(vo));
}
return vo;
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<ErpFinAccountInstallmentVo> queryPageList(ErpFinAccountInstallmentBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<ErpFinAccountInstallment> lqw = buildQueryWrapper(bo);
Page<ErpFinAccountInstallmentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@Override
public TableDataInfo<ErpFinAccountInstallmentVo> queryPageList(ErpFinAccountInstallmentBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<ErpFinAccountInstallment> lqw = buildQueryWrapper(bo);
Page<ErpFinAccountInstallmentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
fillManagerInfo(result.getRecords());
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<ErpFinAccountInstallmentVo> queryList(ErpFinAccountInstallmentBo bo) {
MPJLambdaWrapper<ErpFinAccountInstallment> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
List<ErpFinAccountInstallmentVo> list = baseMapper.selectVoList(lqw);
fillManagerInfo(list);
return list;
}
private MPJLambdaWrapper<ErpFinAccountInstallment> buildQueryWrapper(ErpFinAccountInstallmentBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<ErpFinAccountInstallment> lqw = JoinWrappers.lambda(ErpFinAccountInstallment.class)
.selectAll(ErpFinAccountInstallment.class)
.eq(StringUtils.isNotBlank(bo.getInstallmentCode()), ErpFinAccountInstallment::getInstallmentCode, bo.getInstallmentCode())
.eq(bo.getCustomerId() != null, ErpFinAccountInstallment::getCustomerId, bo.getCustomerId())
.like(StringUtils.isNotBlank(bo.getCustomerName()), ErpFinAccountInstallment::getCustomerName, bo.getCustomerName())
.eq(StringUtils.isNotBlank(bo.getCurrency()), ErpFinAccountInstallment::getCurrency, bo.getCurrency())
.eq(bo.getPaymentAmount() != null, ErpFinAccountInstallment::getPaymentAmount, bo.getPaymentAmount())
.eq(bo.getPaymentDate() != null, ErpFinAccountInstallment::getPaymentDate, bo.getPaymentDate())
.eq(StringUtils.isNotBlank(bo.getInstallmentStatus()), ErpFinAccountInstallment::getInstallmentStatus, bo.getInstallmentStatus())
.eq(bo.getAccountManageId() != null, ErpFinAccountInstallment::getAccountManageId, bo.getAccountManageId())
.orderByAsc(ErpFinAccountInstallment::getInstallmentStatus)
.orderByDesc(ErpFinAccountInstallment::getCreateTime)
;
.selectAll(ErpFinAccountInstallment.class)
.eq(StringUtils.isNotBlank(bo.getInstallmentCode()), ErpFinAccountInstallment::getInstallmentCode, bo.getInstallmentCode())
.eq(bo.getCustomerId() != null, ErpFinAccountInstallment::getCustomerId, bo.getCustomerId())
.like(StringUtils.isNotBlank(bo.getCustomerName()), ErpFinAccountInstallment::getCustomerName, bo.getCustomerName())
.eq(StringUtils.isNotBlank(bo.getCurrency()), ErpFinAccountInstallment::getCurrency, bo.getCurrency())
.eq(bo.getPaymentAmount() != null, ErpFinAccountInstallment::getPaymentAmount, bo.getPaymentAmount())
.eq(bo.getPaymentDate() != null, ErpFinAccountInstallment::getPaymentDate, bo.getPaymentDate())
.eq(StringUtils.isNotBlank(bo.getFlowStatus()), ErpFinAccountInstallment::getFlowStatus, bo.getFlowStatus())
.eq(StringUtils.isNotBlank(bo.getInstallmentStatus()), ErpFinAccountInstallment::getInstallmentStatus, bo.getInstallmentStatus())
.orderByDesc(ErpFinAccountInstallment::getInstallmentCode)
.orderByDesc(ErpFinAccountInstallment::getCreateTime);
return lqw;
}
/**
*
*
* @param bo
* @return
*/
private void fillManagerInfo(List<ErpFinAccountInstallmentVo> rows) {
if (CollUtil.isEmpty(rows)) {
return;
}
Long currentUserId = LoginHelper.getUserId();
boolean canDispatchAll = LoginHelper.isSuperAdmin() || StpUtil.hasPermission(PERM_DISPATCH);
for (ErpFinAccountInstallmentVo vo : rows) {
List<Long> userIds = parseManagerUserIds(vo.getAccountManagerIds());
vo.setManagerUserIds(userIds);
if (CollUtil.isNotEmpty(userIds)) {
String idsStr = userIds.stream().map(String::valueOf).collect(Collectors.joining(","));
vo.setManagerNickNames(remoteUserService.selectNicknameByIds(idsStr));
}
vo.setCanAllocate(resolveCanAllocate(vo, userIds, currentUserId, canDispatchAll));
}
}
private boolean resolveCanAllocate(ErpFinAccountInstallmentVo vo, List<Long> managerUserIds,
Long currentUserId, boolean canDispatchAll) {
if (!BusinessStatusEnum.WAITING.getStatus().equals(vo.getFlowStatus())) {
return false;
}
if (!canAllocateByInstallmentStatus(vo.getInstallmentStatus())) {
return false;
}
if (canDispatchAll) {
return true;
}
return managerUserIds.contains(currentUserId);
}
private static String joinManagerUserIds(List<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return null;
}
return ids.stream().map(String::valueOf).collect(Collectors.joining(","));
}
private static List<Long> parseManagerUserIds(String idsStr) {
if (StringUtils.isBlank(idsStr)) {
return List.of();
}
return Arrays.stream(idsStr.split(","))
.map(String::trim)
.filter(StringUtils::isNotBlank)
.map(Long::valueOf)
.toList();
}
@Override
public Boolean insertByBo(ErpFinAccountInstallmentBo bo) {
ErpFinAccountInstallment add = MapstructUtils.convert(bo, ErpFinAccountInstallment.class);
if (StringUtils.isBlank(add.getFlowStatus())) {
add.setFlowStatus(BusinessStatusEnum.DRAFT.getStatus());
}
if (StringUtils.isBlank(add.getInstallmentStatus())) {
add.setInstallmentStatus(INSTALLMENT_STATUS_NOT_ALLOCATED);
}
if (StringUtils.isBlank(add.getInstallmentCode())) {
String code = remoteCodeRuleService.selectCodeRuleCode(ACCOUNT_INSTALLMENT_CODE_RULE);
if (StringUtils.isBlank(code)) {
throw new ServiceException("生成回款编号失败");
}
add.setInstallmentCode(code);
}
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
@ -116,18 +193,10 @@ public class ErpFinAccountInstallmentServiceImpl implements IErpFinAccountInstal
return flag;
}
/**
*
*
* @param installmentVolist
* @return
*/
@Override
public String importInstallmentData(List<ErpFinAccountInstallmentVo> installmentVolist) {
try
{
if (StringUtils.isNull(installmentVolist) || installmentVolist.isEmpty())
{
try {
if (StringUtils.isNull(installmentVolist) || installmentVolist.isEmpty()) {
throw new ServiceException("导入数据不能为空!");
}
@ -141,80 +210,293 @@ public class ErpFinAccountInstallmentServiceImpl implements IErpFinAccountInstal
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
for (ErpFinAccountInstallmentVo installmentVo : installmentVolist)
{
try
{
// 检查必填字段
if (StringUtils.isEmpty(installmentVo.getCustomerName()) || installmentVo.getPaymentAmount() == null || installmentVo.getPaymentDate() == null)
{
for (ErpFinAccountInstallmentVo installmentVo : installmentVolist) {
try {
if (StringUtils.isEmpty(installmentVo.getCustomerName())
|| installmentVo.getPaymentAmount() == null
|| installmentVo.getPaymentDate() == null) {
failureNum++;
failureMsg.append("<br/>第" + (failureNum + 1) + "行数据不完整,缺少必填字段");
failureMsg.append("<br/>第").append(failureNum).append("行数据不完整,缺少必填字段");
continue;
}
ErpFinAccountInstallment add = MapstructUtils.convert(installmentVo, ErpFinAccountInstallment.class);
add.setInstallmentCode(code);
add.setFlowStatus(BusinessStatusEnum.DRAFT.getStatus());
add.setInstallmentStatus(INSTALLMENT_STATUS_NOT_ALLOCATED);
add.setDelFlag("0");
baseMapper.insert(add);
successNum++;
successMsg.append("<br/>" + successNum + "、" + installmentVo.getCustomerName() + " 导入成功");
}
catch (Exception e)
{
successMsg.append("<br/>").append(successNum).append("、").append(installmentVo.getCustomerName()).append(" 导入成功");
} catch (Exception e) {
failureNum++;
String msg = "<br/>" + installmentVo.getCustomerName() + " 导入失败:";
failureMsg.append(msg + e.getMessage());
failureMsg.append("<br/>").append(installmentVo.getCustomerName()).append(" 导入失败:").append(e.getMessage());
}
}
if (failureNum > 0)
{
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
}
else
{
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
return successMsg.toString();
}
catch (Exception e)
{
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new ServiceException("导入Excel数据失败:" + e.getMessage());
}
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean updateByBo(ErpFinAccountInstallmentBo bo) {
ErpFinAccountInstallment existing = baseMapper.selectById(bo.getAccountInstallmentId());
if (existing == null) {
throw new ServiceException("回款记录不存在");
}
if (!isFlowEditable(existing.getFlowStatus())) {
throw new ServiceException("仅流程草稿/退回/撤销状态的回款可修改");
}
if (!isInstallmentNotAllocated(existing.getInstallmentStatus())) {
throw new ServiceException("仅未分款的回款可修改");
}
ErpFinAccountInstallment update = MapstructUtils.convert(bo, ErpFinAccountInstallment.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
*
*/
private void validEntityBeforeSave(ErpFinAccountInstallment entity){
//TODO 做一些数据校验,如唯一约束
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public Boolean dispatchToManagers(ErpFinAccountInstallmentDispatchBo bo) {
List<Long> installmentIds = bo.getAccountInstallmentIds().stream().distinct().toList();
List<Long> managerUserIds = bo.getManagerUserIds().stream().distinct().toList();
if (CollUtil.isEmpty(installmentIds) || CollUtil.isEmpty(managerUserIds)) {
throw new ServiceException("请选择回款记录与客户经理");
}
List<ErpFinAccountInstallment> installments = baseMapper.selectBatchIds(installmentIds);
if (installments.size() != installmentIds.size()) {
throw new ServiceException("部分回款记录不存在");
}
for (ErpFinAccountInstallment inst : installments) {
if (!isFlowEditable(inst.getFlowStatus())) {
throw new ServiceException("回款【" + inst.getCustomerName() + "】流程状态不允许派发");
}
if (!isInstallmentNotAllocated(inst.getInstallmentStatus())) {
throw new ServiceException("回款【" + inst.getCustomerName() + "】已派发,无法重复派发");
}
}
Long dispatchUserId = LoginHelper.getUserId();
Long dispatchDeptId = LoginHelper.getDeptId();
Date now = new Date();
String accountManagerIds = joinManagerUserIds(managerUserIds);
Map<Long, ErpFinAccountInstallment> installmentMap = installments.stream()
.collect(Collectors.toMap(ErpFinAccountInstallment::getAccountInstallmentId, i -> i));
for (Long installmentId : installmentIds) {
ErpFinAccountInstallment update = new ErpFinAccountInstallment();
update.setAccountInstallmentId(installmentId);
update.setAccountManagerIds(accountManagerIds);
update.setFlowStatus(BusinessStatusEnum.WAITING.getStatus());
update.setInstallmentStatus(INSTALLMENT_STATUS_DISPATCHED);
update.setDispatchUserId(dispatchUserId);
update.setDispatchDeptId(dispatchDeptId);
update.setDispatchDate(now);
baseMapper.updateById(update);
ErpFinAccountInstallment inst = installmentMap.get(installmentId);
startDispatchFlow(installmentId, inst, bo);
}
return true;
}
/**
*
*
* @param ids
* @param isValid
* @return
*
*/
private void startDispatchFlow(Long installmentId, ErpFinAccountInstallment inst,
ErpFinAccountInstallmentDispatchBo bo) {
Map<String, Object> variables = bo.getVariables() != null
? new HashMap<>(bo.getVariables())
: new HashMap<>();
if (!variables.containsKey("ignore")) {
variables.put("ignore", true);
}
RemoteStartProcess startProcess = new RemoteStartProcess();
startProcess.setBusinessId(installmentId.toString());
startProcess.setFlowCode(StringUtils.isNotBlank(bo.getFlowCode()) ? bo.getFlowCode() : FLOW_CODE_FKSH);
startProcess.setVariables(variables);
RemoteFlowInstanceBizExt bizExt = resolveDispatchBizExt(installmentId, inst, bo);
bizExt.setBusinessId(startProcess.getBusinessId());
startProcess.setBizExt(bizExt);
boolean started = remoteWorkflowService.startCompleteTask(startProcess);
if (!started) {
throw new ServiceException("分款审核流程发起失败");
}
}
private RemoteFlowInstanceBizExt resolveDispatchBizExt(Long installmentId, ErpFinAccountInstallment inst,
ErpFinAccountInstallmentDispatchBo bo) {
String installmentIdStr = installmentId.toString();
if (CollUtil.isNotEmpty(bo.getFlowBizList())) {
for (RemoteFlowInstanceBizExt item : bo.getFlowBizList()) {
if (item != null && installmentIdStr.equals(item.getBusinessId())) {
RemoteFlowInstanceBizExt bizExt = new RemoteFlowInstanceBizExt();
bizExt.setBusinessCode(item.getBusinessCode());
bizExt.setBusinessTitle(item.getBusinessTitle());
return bizExt;
}
}
}
RemoteFlowInstanceBizExt bizExt = new RemoteFlowInstanceBizExt();
if (inst != null) {
bizExt.setBusinessCode(inst.getInstallmentCode());
bizExt.setBusinessTitle("分款审核-" + inst.getCustomerName());
} else {
bizExt.setBusinessTitle("分款审核");
}
return bizExt;
}
/**
*
*/
@EventListener(condition = "#processEvent.flowCode == 'FKSH'")
public void processHandler(ProcessEvent processEvent) {
TenantHelper.dynamic(processEvent.getTenantId(), () -> {
log.info("【分款审核流程监听】flowCode={}, status={}", processEvent.getFlowCode(), processEvent.getStatus());
Long installmentId = Convert.toLong(processEvent.getBusinessId());
ErpFinAccountInstallment inst = baseMapper.selectById(installmentId);
if (inst == null) {
log.error("未找到回款分款记录id={}", installmentId);
return;
}
ErpFinAccountInstallment update = new ErpFinAccountInstallment();
update.setAccountInstallmentId(installmentId);
update.setFlowStatus(processEvent.getStatus());
String status = processEvent.getStatus();
if (BusinessStatusEnum.BACK.getStatus().equals(status)
|| BusinessStatusEnum.CANCEL.getStatus().equals(status)) {
update.setInstallmentStatus(INSTALLMENT_STATUS_NOT_ALLOCATED);
update.setAccountManagerIds(null);
baseMapper.updateById(update);
} else if (BusinessStatusEnum.INVALID.getStatus().equals(status)
|| BusinessStatusEnum.TERMINATION.getStatus().equals(status)) {
update.setInstallmentStatus(INSTALLMENT_STATUS_NOT_ALLOCATED);
baseMapper.updateById(update);
} else {
baseMapper.updateById(update);
}
});
}
@Override
public void validateAllocatePermission(Long accountInstallmentId) {
ErpFinAccountInstallment inst = baseMapper.selectById(accountInstallmentId);
if (inst == null) {
throw new ServiceException("回款记录不存在");
}
if (!BusinessStatusEnum.WAITING.getStatus().equals(inst.getFlowStatus())) {
throw new ServiceException("当前回款流程未处于待处理状态,无法进行分款操作");
}
if (!canAllocateByInstallmentStatus(inst.getInstallmentStatus())) {
throw new ServiceException("当前回款尚未派发或分款已完成,无法进行分款操作");
}
if (LoginHelper.isSuperAdmin() || StpUtil.hasPermission(PERM_DISPATCH)) {
return;
}
Long userId = LoginHelper.getUserId();
if (!parseManagerUserIds(inst.getAccountManagerIds()).contains(userId)) {
throw new ServiceException("您不是该回款的指定客户经理,无法进行分款操作");
}
}
@Override
public void recalculateInstallmentStatus(Long accountInstallmentId) {
ErpFinAccountInstallment inst = baseMapper.selectById(accountInstallmentId);
if (inst == null) {
return;
}
String status = inst.getInstallmentStatus();
if (isInstallmentNotAllocated(status)
|| !BusinessStatusEnum.WAITING.getStatus().equals(inst.getFlowStatus())) {
return;
}
List<ErpFinAccountInstallmentDetail> details = detailMapper.selectList(
Wrappers.lambdaQuery(ErpFinAccountInstallmentDetail.class)
.eq(ErpFinAccountInstallmentDetail::getAccountInstallmentId, accountInstallmentId));
BigDecimal allocated = details.stream()
.map(ErpFinAccountInstallmentDetail::getDetailAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal payment = inst.getPaymentAmount() == null
? BigDecimal.ZERO
: BigDecimal.valueOf(inst.getPaymentAmount());
String newStatus;
if (allocated.compareTo(BigDecimal.ZERO) <= 0) {
newStatus = INSTALLMENT_STATUS_DISPATCHED;
} else if (allocated.compareTo(payment) >= 0) {
newStatus = INSTALLMENT_STATUS_COMPLETE;
} else {
newStatus = INSTALLMENT_STATUS_DISPATCHED;
}
ErpFinAccountInstallment update = new ErpFinAccountInstallment();
update.setAccountInstallmentId(accountInstallmentId);
if (!Objects.equals(status, newStatus)) {
update.setInstallmentStatus(newStatus);
baseMapper.updateById(update);
}
}
/** 流程是否可编辑/派发(同出差申请:草稿、退回、撤销) */
private static boolean isFlowEditable(String flowStatus) {
return BusinessStatusEnum.isDraftOrCancelOrBack(flowStatus);
}
/** 分款状态未分款installment_status = 0 */
private static boolean isInstallmentNotAllocated(String installmentStatus) {
return INSTALLMENT_STATUS_NOT_ALLOCATED.equals(installmentStatus);
}
/** 分款状态已派发可进行明细维护installment_status = 1 */
private static boolean canAllocateByInstallmentStatus(String installmentStatus) {
return INSTALLMENT_STATUS_DISPATCHED.equals(installmentStatus);
}
private void validEntityBeforeSave(ErpFinAccountInstallment entity) {
// 预留业务校验
}
/**
*
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
if (isValid) {
List<ErpFinAccountInstallment> list = baseMapper.selectBatchIds(ids);
for (ErpFinAccountInstallment item : list) {
if (!isFlowEditable(item.getFlowStatus())) {
throw new ServiceException("仅可删除流程草稿/退回/撤销状态的回款记录");
}
if (!isInstallmentNotAllocated(item.getInstallmentStatus())) {
throw new ServiceException("仅可删除未分款的回款记录");
}
}
}
if (CollUtil.isNotEmpty(ids)) {
detailMapper.delete(Wrappers.lambdaQuery(ErpFinAccountInstallmentDetail.class)
.in(ErpFinAccountInstallmentDetail::getAccountInstallmentId, ids));
}
return baseMapper.deleteByIds(ids) > 0;
}

@ -17,6 +17,7 @@
payment_stage_id,
detail_amount,
remark,
del_flag,
create_dept,
create_by,
create_time,
@ -29,7 +30,8 @@
<select id="sumDetailAmountByContractProjectStage" resultType="java.math.BigDecimal">
SELECT COALESCE(SUM(d.detail_amount), 0)
FROM erp_fin_account_installment_detail d
WHERE d.contract_id = #{contractId}
WHERE d.del_flag = '0'
AND d.contract_id = #{contractId}
AND d.project_id = #{projectId}
AND d.payment_stage_id = #{paymentStageId}
</select>

@ -1,14 +1,34 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.erp.mapper.ErpFinAccountInstallmentMapper">
<resultMap type="org.dromara.oa.erp.domain.vo.ErpFinAccountInstallmentVo" id="ErpFinAccountInstallmentResult">
</resultMap>
<select id="selectCustomErpFinAccountInstallmentVoList" resultMap="ErpFinAccountInstallmentResult">
select account_installment_id, tenant_id, installment_code, customer_id, customer_name, currency, payment_amount, payment_date, remark, dispatch_user_id, dispatch_dept_id, dispatch_date, installment_status, account_manage_id, create_dept, create_by, create_time, update_by, update_time from erp_fin_account_installment t
select account_installment_id,
tenant_id,
installment_code,
customer_id,
customer_name,
currency,
payment_amount,
payment_date,
remark,
flow_status,
dispatch_user_id,
dispatch_dept_id,
dispatch_date,
installment_status,
account_manager_ids,
del_flag,
create_dept,
create_by,
create_time,
update_by,
update_time
from erp_fin_account_installment t
${ew.getCustomSqlSegment}
</select>
</mapper>

Loading…
Cancel
Save