This commit is contained in:
2026-04-21 10:18:59 +08:00
parent 821bf08141
commit d09dc43f76
9 changed files with 714 additions and 15 deletions
@@ -22,6 +22,7 @@ import com.budwk.app.zhgh.club.service.SysClubService;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.service.ActivityBudgetService;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.template.ActivityBudgetTemp;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.vo.ActivityBudgetQuotaInfoVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -91,6 +92,10 @@ public class ActivityBudgetApplyController {
@Aop(TransAop.READ_COMMITTED)
@SLog( tag = "年度预算申报-预算申报", msg = "重新提交申请")
public Result submitAgain(@Param("data") ActivityBudget activityBudget, @Param("taskId") Long taskId) {
Result quotaValidateResult = activityBudgetService.validateQuota(activityBudget);
if (quotaValidateResult.getCode() != 0) {
return quotaValidateResult;
}
activityBudgetService.insertOrUpdate(activityBudget);
Dict dict = Dict.create();
@@ -100,6 +105,30 @@ public class ActivityBudgetApplyController {
return Result.success();
}
/**
* 查询预算额度占用情况。
* 参数说明:
* 1. outlayManageSource:预算类型,当前仅分工会预算需要查询额度。
* 2. unionId:分工会ID。
* 3. applyDate:申报时间,用来确定预算所属年度。
* 返回值字段:
* 1. totalQuota:本年度总额度。
* 2. applyingQuota:申请中额度。
* 3. approvedQuota:已通过额度。
* 4. occupiedQuota:已占用额度。
* 5. remainQuota:剩余额度。
*
* @param activityBudget 预算申报参数
* @return 当前预算主体的额度统计
*/
@At
@ApiOperation("查询年度预算额度信息")
@SaCheckPermission("activity.budget.apply")
public Result getQuotaInfo(@Param("data") ActivityBudget activityBudget) {
ActivityBudgetQuotaInfoVo quotaInfoVo = activityBudgetService.getQuotaInfo(activityBudget);
return Result.success(quotaInfoVo);
}
@At
@SaCheckPermission("activity.budget.apply")
@@ -4,6 +4,7 @@ import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.vo.ActivityBudgetQuotaInfoVo;
import org.nutz.dao.Cnd;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.util.NutMap;
@@ -22,6 +23,41 @@ public interface ActivityBudgetService extends BaseService<ActivityBudget> {
*/
Result submit(ActivityBudget activityBudget);
/**
* 校验预算申报额度。
* 参数说明:
* 1. activityBudget.outlayManageSource:预算类型,分工会/协会/校工会。
* 2. activityBudget.unionId:分工会预算时传对应分工会ID。
* 4. activityBudget.declareTotalBudgetMoney:本次申报金额。
* 5. activityBudget.applyDate:申报时间,用于判定所属年度;为空时按当前年份处理。
* 返回值:
* Result.success 表示可提交;当前仅分工会预算会触发年度额度校验;
* Result.error 表示超额或年度额度未配置,message 会直接返回给前端提示。
*
* @param activityBudget 预算申报数据
* @return 校验结果
*/
Result validateQuota(ActivityBudget activityBudget);
/**
* 查询预算额度占用信息。
* 参数说明:
* 1. activityBudget.outlayManageSource:预算类型;当前仅分工会预算需要返回额度占用信息。
* 2. activityBudget.unionId:分工会预算时用于定位对应年度额度记录。
* 3. activityBudget.applyDate:用于计算预算所属年度;为空时按当前年份处理。
* 返回值字段:
* 1. year:预算所属年度。
* 2. totalQuota:本年度总额度。
* 3. applyingQuota:申请中额度,除已通过和已拒绝外的流程金额总和。
* 4. approvedQuota:已通过额度。
* 5. occupiedQuota:当前已占用额度,除已拒绝外全部计入。
* 6. remainQuota:当前剩余额度。
*
* @param activityBudget 预算申报数据
* @return 预算额度统计信息
*/
ActivityBudgetQuotaInfoVo getQuotaInfo(ActivityBudget activityBudget);
NutMap findOne(String id);
@@ -4,7 +4,9 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.Audit;
@@ -16,6 +18,7 @@ import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum;
import com.budwk.app.sys.models.Sys_dict;
import com.budwk.app.sys.services.SysDictService;
@@ -26,6 +29,8 @@ import com.budwk.app.zhgh.club.service.SysClubService;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudgetDetails;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.service.ActivityBudgetService;
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.vo.ActivityBudgetQuotaInfoVo;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.union.model.OutlayManageUnion;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
@@ -37,6 +42,7 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@@ -140,6 +146,10 @@ public class ActivityBudgetServiceImpl extends BaseServiceImpl<ActivityBudget> i
}
}
}
Result quotaValidateResult = validateQuota(activityBudget);
if (quotaValidateResult != null && quotaValidateResult.getCode() != 0) {
return quotaValidateResult;
}
//删除预算详情表
if (StrUtil.isNotBlank(activityBudget.getId()) ) {
dao().clear(ActivityBudgetDetails.class, Cnd.where("budgetId", "=", activityBudget.getId()));
@@ -227,6 +237,80 @@ public class ActivityBudgetServiceImpl extends BaseServiceImpl<ActivityBudget> i
return Result.success();
}
@Override
public Result validateQuota(ActivityBudget activityBudget) {
if (ObjectUtil.isEmpty(activityBudget) || StrUtil.isBlank(activityBudget.getOutlayManageSource())) {
return Result.error("预算类型不能为空");
}
if (!"ACTIVITY_BUDGET_TYPE_TWO".equals(activityBudget.getOutlayManageSource())) {
// 当前业务仅分工会预算保留年度额度校验,校工会和协会预算直接跳过。
return Result.success();
}
if (ObjectUtil.isEmpty(activityBudget.getDeclareTotalBudgetMoney())) {
return Result.error("预算金额不能为空");
}
ActivityBudgetQuotaInfoVo quotaInfo = getQuotaInfo(activityBudget);
if (!quotaInfo.getSelectedTarget()) {
return Result.error(quotaInfo.getMessage());
}
if (ObjectUtil.isEmpty(quotaInfo.getTotalQuota())) {
return Result.error(quotaInfo.getMessage());
}
BigDecimal remainQuota = quotaInfo.getRemainQuota();
BigDecimal declareTotalBudgetMoney = activityBudget.getDeclareTotalBudgetMoney();
if (remainQuota.compareTo(declareTotalBudgetMoney) < 0) {
return Result.error("本年度预算额度不足,剩余额度:" + remainQuota
+ "元,申请中额度:" + quotaInfo.getApplyingQuota()
+ "元,已通过额度:" + quotaInfo.getApprovedQuota() + "");
}
return Result.success();
}
@Override
public ActivityBudgetQuotaInfoVo getQuotaInfo(ActivityBudget activityBudget) {
if (ObjectUtil.isEmpty(activityBudget)) {
activityBudget = new ActivityBudget();
}
String outlayManageSource = activityBudget.getOutlayManageSource();
Integer year = getBudgetYear(activityBudget);
ActivityBudgetQuotaInfoVo quotaInfo = new ActivityBudgetQuotaInfoVo();
quotaInfo.setYear(year);
quotaInfo.setNeedLimit("ACTIVITY_BUDGET_TYPE_TWO".equals(outlayManageSource));
quotaInfo.setSelectedTarget(true);
quotaInfo.setTotalQuota(null);
quotaInfo.setApplyingQuota(BigDecimal.ZERO);
quotaInfo.setApprovedQuota(BigDecimal.ZERO);
quotaInfo.setOccupiedQuota(BigDecimal.ZERO);
quotaInfo.setRemainQuota(BigDecimal.ZERO);
if (!"ACTIVITY_BUDGET_TYPE_TWO".equals(outlayManageSource)) {
return quotaInfo;
}
BigDecimal totalQuota = getCurrentYearTotalQuota(activityBudget, year, quotaInfo);
if (!quotaInfo.getSelectedTarget()) {
return quotaInfo;
}
if (ObjectUtil.isEmpty(totalQuota)) {
quotaInfo.setMessage("该年度未配置预算额度,请先维护年度预算");
return quotaInfo;
}
NutMap quotaUseInfo = getQuotaUseInfo(activityBudget, year);
BigDecimal occupiedQuota = getMapBigDecimal(quotaUseInfo, "occupiedQuota");
BigDecimal approvedQuota = getMapBigDecimal(quotaUseInfo, "approvedQuota");
BigDecimal applyingQuota = getMapBigDecimal(quotaUseInfo, "applyingQuota");
quotaInfo.setTotalQuota(totalQuota);
quotaInfo.setOccupiedQuota(occupiedQuota);
quotaInfo.setApprovedQuota(approvedQuota);
quotaInfo.setApplyingQuota(applyingQuota);
quotaInfo.setRemainQuota(totalQuota.subtract(occupiedQuota));
return quotaInfo;
}
@Override
public NutMap findOne(String id) {
Sql sql = Sqls.create("""
@@ -277,4 +361,108 @@ public class ActivityBudgetServiceImpl extends BaseServiceImpl<ActivityBudget> i
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, mapList);
CommonDownloadUtil.download("预算申报汇总表.xlsx", workbook, response);
}
/**
* 解析预算所属年度,页面没有单独年度字段时默认取申报时间中的年份。
*
* @param activityBudget 预算申报数据
* @return 预算所属年度
*/
private Integer getBudgetYear(ActivityBudget activityBudget) {
if (ObjectUtil.isEmpty(activityBudget) || StrUtil.isBlank(activityBudget.getApplyDate())) {
return DateUtil.thisYear();
}
try {
return DateUtil.parse(activityBudget.getApplyDate()).year();
} catch (Exception e) {
return DateUtil.thisYear();
}
}
/**
* 查询当前预算类型在所属年度的总额度配置。
*
* @param activityBudget 预算申报数据
* @param year 预算所属年度
* @param quotaInfo 返回前端的额度信息对象,用于补充缺失主体的提示
* @return 年度总额度;未配置时返回 null
*/
private BigDecimal getCurrentYearTotalQuota(ActivityBudget activityBudget, Integer year, ActivityBudgetQuotaInfoVo quotaInfo) {
String outlayManageSource = activityBudget.getOutlayManageSource();
if ("ACTIVITY_BUDGET_TYPE_TWO".equals(outlayManageSource)) {
String unionId = StrUtil.blankToDefault(activityBudget.getUnionId(), SecurityUtil.getUnionId());
if (StrUtil.isBlank(unionId)) {
quotaInfo.setSelectedTarget(false);
quotaInfo.setMessage("请选择分工会");
return null;
}
OutlayManageUnion outlayManageUnion = dao().fetch(OutlayManageUnion.class,
Cnd.where("year", "=", year).and("unionId", "=", unionId));
return ObjectUtil.isEmpty(outlayManageUnion) ? null : outlayManageUnion.getTotalQuota();
}
return null;
}
/**
* 统计当前主体在本年度已经占用的预算。
* 统计规则:
* 1. 只统计已进入流程的申请;
* 2. 已拒绝不计入额度;
* 3. 其他状态全部计入占用额度;
* 4. 申请中额度 = 非已通过、非已拒绝的金额合计。
*
* @param activityBudget 预算申报数据
* @param year 预算所属年度
* @return occupiedQuota/applyingQuota/approvedQuota 统计结果
*/
private NutMap getQuotaUseInfo(ActivityBudget activityBudget, Integer year) {
Sql sql = Sqls.create("""
SELECT
IFNULL(SUM(ab.declareTotalBudgetMoney), 0) occupiedQuota,
IFNULL(SUM(CASE WHEN ins.state = @finishedState THEN ab.declareTotalBudgetMoney ELSE 0 END), 0) approvedQuota,
IFNULL(SUM(CASE WHEN ins.state != @finishedState AND ins.state != @rejectState THEN ab.declareTotalBudgetMoney ELSE 0 END), 0) applyingQuota
FROM
activity_budget ab
LEFT JOIN wf_process_instance ins ON ins.businessNo = ab.id
$condition
""");
sql.setParam("finishedState", ProcessInstanceStateEnum.FINISHED.getCode());
sql.setParam("rejectState", ProcessInstanceStateEnum.REJECT.getCode());
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(ab.applyDate)", "=", year);
cnd.and("ab.outlayManageSource", "=", activityBudget.getOutlayManageSource());
cnd.and("ins.id", "IS NOT", null);
cnd.and("ins.state", "!=", ProcessInstanceStateEnum.REJECT.getCode());
if (StrUtil.isNotBlank(activityBudget.getId())) {
// 重新提交或编辑当前记录时,先排除本条数据,避免把旧记录金额重复计入占用额度。
cnd.and("ab.id", "!=", activityBudget.getId());
}
if ("ACTIVITY_BUDGET_TYPE_TWO".equals(activityBudget.getOutlayManageSource())) {
cnd.and("ab.unionId", "=", StrUtil.blankToDefault(activityBudget.getUnionId(), SecurityUtil.getUnionId()));
} else if ("ACTIVITY_BUDGET_TYPE_THREE".equals(activityBudget.getOutlayManageSource())) {
cnd.and("ab.clubId", "=", activityBudget.getClubId());
}
sql.setCondition(cnd);
sql.setCallback(Sqls.callback.map());
execute(sql);
return (NutMap) sql.getResult();
}
/**
* 统一把数据库聚合结果转成 BigDecimal,避免前后端展示时出现 null。
*
* @param map 聚合结果
* @param key 字段名
* @return 对应金额,默认 0
*/
private BigDecimal getMapBigDecimal(NutMap map, String key) {
Object value = ObjectUtil.isEmpty(map) ? null : map.get(key);
if (ObjectUtil.isEmpty(value)) {
return BigDecimal.ZERO;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
return new BigDecimal(String.valueOf(value));
}
}
@@ -0,0 +1,59 @@
package com.budwk.app.zhgh.dayofficework.outlay.activityBudget.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author zhf
* @date 2026/4/20
* @description 预算申报额度信息
*/
@Data
public class ActivityBudgetQuotaInfoVo {
/**
* 预算所属年度。
*/
private Integer year;
/**
* 当前预算类型是否需要做额度限制。
*/
private Boolean needLimit = false;
/**
* 是否已经选择了有效的预算主体。
* 分工会预算需要分工会ID,协会预算需要协会ID。
*/
private Boolean selectedTarget = true;
/**
* 本年度总额度;未配置时为 null。
*/
private BigDecimal totalQuota;
/**
* 当前申请中额度。
*/
private BigDecimal applyingQuota = BigDecimal.ZERO;
/**
* 当前已通过额度。
*/
private BigDecimal approvedQuota = BigDecimal.ZERO;
/**
* 当前已占用额度,除已拒绝外全部计入。
*/
private BigDecimal occupiedQuota = BigDecimal.ZERO;
/**
* 当前剩余额度。
*/
private BigDecimal remainQuota = BigDecimal.ZERO;
/**
* 当前额度提示信息。
*/
private String message;
}
@@ -16,12 +16,15 @@ import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.club.model.SysClub;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.service.OutlayManageClubService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
@@ -49,6 +52,8 @@ public class OutlayManageClubController {
@Inject
private SysRoleService sysRoleService;
@Inject
private OutlayManageClubService outlayManageClubService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/outlay/outlayManage/club/manage/index.html")
@@ -89,6 +94,45 @@ public class OutlayManageClubController {
return Result.success();
}
/**
* 查询某个年度已经分配预算的协会ID。
* 参数说明:
* 1. year:预算年度。
* 返回值说明:
* 1. 返回当前年度已存在预算分配记录的协会ID列表。
*
* @param year 预算年度
* @return 已分配预算的协会ID列表
*/
@At
@ApiOperation("查询某个年度已分配预算的协会ID")
@SaCheckPermission("outlay.outlayManage.club.manage")
public Result getAllocatedClubIds(Integer year) {
return Result.success(outlayManageClubService.getAllocatedClubIds(year));
}
/**
* 手动新增协会预算分配。
* 参数说明:
* 1. outlayManageClub.year:预算年度。
* 2. outlayManageClub.clubId:协会ID。
* 3. outlayManageClub.totalQuota:分配总额度。
* 返回值说明:
* 1. 返回新增成功结果,失败时返回业务提示信息。
*
* @param outlayManageClub 新增分配数据
* @return 新增结果
*/
@At
@ApiOperation("手动新增协会预算分配")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("outlay.outlayManage.club.manage")
@SLog(tag = "协会预算分配", msg = "手动新增了协会预算分配,年度:${args[0].year},协会:${args[0].clubId},总额度:${args[0].totalQuota}")
public Result addAllocation(OutlayManageClub outlayManageClub) {
outlayManageClubService.addAllocation(outlayManageClub);
return Result.success();
}
@At
@ApiOperation("查看是否分配")
@@ -0,0 +1,42 @@
package com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
import java.util.List;
/**
* @author zhf
* @date 2026/4/20
* @description 协会预算分配公共方法
*/
public interface OutlayManageClubService extends BaseService<OutlayManageClub> {
/**
* 查询某个年度已经分配过预算的协会ID列表。
* 参数说明:
* 1. year:预算年度,查询该年度已存在的协会预算记录。
* 返回值说明:
* 1. 返回当前年度在 outlay_manage_club 表中已经存在的 clubId 集合。
* 2. 前端可基于该结果过滤“审核通过但未分配预算”的协会下拉。
*
* @param year 预算年度
* @return 已分配预算的协会ID列表
*/
List<String> getAllocatedClubIds(Integer year);
/**
* 手动新增协会预算分配。
* 参数说明:
* 1. outlayManageClub.year:预算年度。
* 2. outlayManageClub.clubId:协会ID。
* 3. outlayManageClub.totalQuota:本次分配的总额度。
* 返回值说明:
* 1. 返回已补全协会名称、协会编码后的预算分配实体。
* 2. 如果年度、协会、总额度为空,或该年度该协会已存在预算记录,则抛出业务异常。
*
* @param outlayManageClub 手动新增的预算分配数据
* @return 新增后的预算分配实体
*/
OutlayManageClub addAllocation(OutlayManageClub outlayManageClub);
}
@@ -0,0 +1,77 @@
package com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.club.model.SysClub;
import com.budwk.app.zhgh.club.service.SysClubService;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.model.OutlayManageClub;
import com.budwk.app.zhgh.dayofficework.outlay.outlayManage.club.service.OutlayManageClubService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.util.List;
/**
* @author zhf
* @date 2026/4/20
* @description 协会预算分配公共方法实现
*/
@IocBean(args = {"refer:dao"})
public class OutlayManageClubServiceImpl extends BaseServiceImpl<OutlayManageClub> implements OutlayManageClubService {
public OutlayManageClubServiceImpl(Dao dao) {
super(dao);
}
@Inject
private SysClubService sysClubService;
@Override
public List<String> getAllocatedClubIds(Integer year) {
if (ObjectUtil.isEmpty(year)) {
return List.of();
}
List<OutlayManageClub> outlayManageClubList = query(Cnd.where("year", "=", year));
return outlayManageClubList.stream()
.map(OutlayManageClub::getClubId)
.filter(StrUtil::isNotBlank)
.toList();
}
@Override
public OutlayManageClub addAllocation(OutlayManageClub outlayManageClub) {
if (ObjectUtil.isEmpty(outlayManageClub) || ObjectUtil.isEmpty(outlayManageClub.getYear())) {
throw new BaseException("预算年度不能为空");
}
if (StrUtil.isBlank(outlayManageClub.getClubId())) {
throw new BaseException("协会不能为空");
}
if (ObjectUtil.isEmpty(outlayManageClub.getTotalQuota())) {
throw new BaseException("总额度不能为空");
}
if (outlayManageClub.getTotalQuota().compareTo(BigDecimal.ZERO) < 0) {
throw new BaseException("总额度不能小于0");
}
int count = count(Cnd.where("year", "=", outlayManageClub.getYear()).and("clubId", "=", outlayManageClub.getClubId()));
if (count > 0) {
throw new BaseException("该年度该协会已分配预算,请勿重复新增");
}
SysClub sysClub = sysClubService.fetch(outlayManageClub.getClubId());
if (ObjectUtil.isEmpty(sysClub)) {
throw new BaseException("未找到对应协会信息");
}
outlayManageClub.setClubName(sysClub.getClubName());
outlayManageClub.setClubCode(sysClub.getClubCode());
if (ObjectUtil.isEmpty(outlayManageClub.getUsedQuota())) {
outlayManageClub.setUsedQuota(BigDecimal.ZERO);
}
return insert(outlayManageClub);
}
}
@@ -90,6 +90,17 @@ layout("/layouts/platform.html"){
v-model="formData.declareTotalBudgetMoney"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item v-if="quotaInfo.needLimit" label="年度额度提示" :span="2">
<div v-if="quotaInfo.selectedTarget && quotaInfo.totalQuota !== null">
<div>本年度总额度:{{quotaInfo.totalQuota}}元,申请中额度:{{quotaInfo.applyingQuota}}元,已通过额度:{{quotaInfo.approvedQuota}}元,当前剩余额度:{{quotaInfo.remainQuota}}元</div>
<div v-if="isQuotaExceeded()" style="color: #F56C6C; margin-top: 6px;">
当前申报金额已超出剩余额度,请先调整预算金额。
</div>
</div>
<div v-else style="color: #E6A23C;">
{{quotaInfo.message}}
</div>
</el-descriptions-item>
<el-descriptions-item label="预算条目" span="2">
<el-form-item
prop="budgetReimburseType" label="预算条目"
@@ -186,6 +197,16 @@ layout("/layouts/platform.html"){
formData: {
budgetDetails: []
},
quotaInfo: {
needLimit: false,
selectedTarget: true,
totalQuota: null,
applyingQuota: 0,
approvedQuota: 0,
occupiedQuota: 0,
remainQuota: 0,
message: ""
},
dialogVisible: false,
editType: "apply"
}
@@ -200,57 +221,65 @@ layout("/layouts/platform.html"){
return resp.data
}
},
init() {
async init() {
if (this.bizId) {
this.findOne(this.bizId).then(data => {
this.formData = data
})
const data = await this.findOne(this.bizId)
this.$set(this, "formData", data)
} else {
this.formData = {
this.$set(this, "formData", {
isSchoolBudget: false,
isRepeatReimburse: true,
budgetDetails: [],
applyDate: this.$moment().format('YYYY-MM-DD'),
userName: this.$store.state.user.username,
mobile: this.$store.state.user.mobile,
}
})
}
},
clubChange(val) {
const club = this.clubOption.find(c => c.id === val)
this.formData.helpUnitName = club.clubName
this.$set(this.formData, "helpUnitName", club ? club.clubName : "")
this.refreshQuotaInfo()
},
unionChange(id) {
if (id) {
const union = this.unionList.find(c => c.id === id)
this.$set(this.formData, "helpUnitName", union.unionname)
this.$set(this.formData, "helpUnitName", union ? (union.name || union.unionname) : "")
} else {
this.$set(this.formData, "helpUnitName", '')
}
this.refreshQuotaInfo()
},
budgetTypeCodeChange(val) {
if (val === "ACTIVITY_BUDGET_TYPE_ONE") {
this.$set(this.formData, "helpUnitName", "校工会")
this.$set(this.formData, "unionId", "")
this.$set(this.formData, "clubId", "")
} else if (val === "ACTIVITY_BUDGET_TYPE_TWO") {
this.$set(this.formData, "helpUnitName", this.$store.state.user.union.name)
this.$set(this.formData, "unionId", this.$store.state.user.union.id)
this.$set(this.formData, "clubId", "")
} else if (["ACTIVITY_BUDGET_TYPE_THREE"].includes(val)) {
this.$set(this.formData, "helpUnitName", '')
this.$set(this.formData, "clubId", '')
this.$set(this.formData, "unionId", "")
} else {
this.$set(this.formData, "helpUnitName", "校工会")
this.$set(this.formData, "unionId", "")
this.$set(this.formData, "clubId", "")
}
if (val) {
const budgetType = this.budgetTypeOption.find(b => b.code === val)
this.$set(this.formData, "budgetTypeId", budgetType.code)
}
this.refreshQuotaInfo()
},
async getActivityBudgetType() {
const data = await this.$businessTool.getDictOptions("ACTIVITY_BUDGET_TYPE")
let budgetTypeOption = []
if (this.$auth.hasRoleOr(['SYSADMIN'])) {
this.budgetTypeOption = data
this.$set(this, "budgetTypeOption", data)
} else {
if (this.$auth.hasRoleOr(['SCHOOL_UNION_ADMIN', 'SCHOOL_UNION_CHAIRMAN', 'SCHOOL_UNION_VICE_CHAIRMAN'])) {
data.map(v => {
@@ -273,7 +302,7 @@ layout("/layouts/platform.html"){
}
})
}
this.budgetTypeOption = budgetTypeOption
this.$set(this, "budgetTypeOption", budgetTypeOption)
if (this.budgetTypeOption.length === 1){
this.$set(this.formData, "outlayManageSource", this.budgetTypeOption[0].code)
this.budgetTypeCodeChange(this.budgetTypeOption[0].code)
@@ -283,10 +312,81 @@ layout("/layouts/platform.html"){
getSchoolBudget() {
this.$axios.post("/platform/activity/budget/apply/getSchoolBudget").then((res) => {
if (res.code === 0) {
this.activityList = res.data
this.$set(this, "activityList", res.data)
}
})
},
clearQuotaInfo() {
this.$set(this, "quotaInfo", {
needLimit: false,
selectedTarget: true,
totalQuota: null,
applyingQuota: 0,
approvedQuota: 0,
occupiedQuota: 0,
remainQuota: 0,
message: ""
})
},
refreshQuotaInfo() {
const outlayManageSource = this.formData.outlayManageSource
if (!outlayManageSource || outlayManageSource !== "ACTIVITY_BUDGET_TYPE_TWO") {
this.clearQuotaInfo()
return
}
if (!this.formData.unionId) {
this.$set(this, "quotaInfo", {
needLimit: true,
selectedTarget: false,
totalQuota: null,
applyingQuota: 0,
approvedQuota: 0,
occupiedQuota: 0,
remainQuota: 0,
message: "请选择分工会"
})
return
}
this.$axios.post("/platform/activity/budget/apply/getQuotaInfo", {
data: JSON.stringify({
id: this.formData.id,
applyDate: this.formData.applyDate,
outlayManageSource: this.formData.outlayManageSource,
unionId: this.formData.unionId,
clubId: this.formData.clubId
})
}).then((res) => {
if (res.code === 0) {
this.$set(this, "quotaInfo", {
needLimit: res.data.needLimit,
selectedTarget: res.data.selectedTarget,
totalQuota: res.data.totalQuota,
applyingQuota: res.data.applyingQuota,
approvedQuota: res.data.approvedQuota,
occupiedQuota: res.data.occupiedQuota,
remainQuota: res.data.remainQuota,
message: res.data.message || ""
})
} else {
this.$set(this, "quotaInfo", {
needLimit: true,
selectedTarget: false,
totalQuota: null,
applyingQuota: 0,
approvedQuota: 0,
occupiedQuota: 0,
remainQuota: 0,
message: res.message || "预算额度查询失败"
})
}
})
},
isQuotaExceeded() {
if (!this.quotaInfo.needLimit || !this.quotaInfo.selectedTarget || this.quotaInfo.totalQuota === null) {
return false
}
return Number(this.formData.declareTotalBudgetMoney || 0) > Number(this.quotaInfo.remainQuota || 0)
},
// 保存
onSave() {
this.$confirm("您确定保存吗?", "提示", {
@@ -316,6 +416,8 @@ layout("/layouts/platform.html"){
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/activity/budget/applyList')
} else {
this.$message.error(res.message)
}
})
})
@@ -333,6 +435,8 @@ layout("/layouts/platform.html"){
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/activity/budget/applyList')
} else {
this.$message.error(res.message)
}
})
})
@@ -340,11 +444,12 @@ layout("/layouts/platform.html"){
},
async created() {
this.init()
await this.init()
await this.getActivityBudgetType()
await this.getSchoolBudget()
this.unionList = await this.$businessTool.listUnion(this.$store.state.user.union.id)
this.clubOption = await this.$businessTool.listCLubByRole()
this.$set(this, "unionList", await this.$businessTool.listUnion(this.$store.state.user.union.id))
this.$set(this, "clubOption", await this.$businessTool.listCLubByRole())
this.refreshQuotaInfo()
}
})
</script>
@@ -24,6 +24,9 @@ layout("/layouts/platform.html"){
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="预算分配">
<el-button @click="openAddDialog" size="small" type="primary">
新增分配
</el-button>
<template v-if="pageForm.year===$moment().format('YYYY')">
<el-button @click="issuedOutlay" size="small" type="primary" v-if="!isAllocation">年度分配
</el-button>
@@ -83,6 +86,40 @@ layout("/layouts/platform.html"){
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="新增分配" :visible.sync="addDialogVisible" width="520px">
<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="90px">
<el-form-item label="年度" prop="year">
<el-date-picker
@change="addYearChange"
:picker-options="addYearPickerOptions"
placeholder="选择年度"
style="width: 100%" type="year"
v-model="addForm.year"
:clearable="false"
value-format="yyyy">
</el-date-picker>
</el-form-item>
<el-form-item label="协会" prop="clubId">
<el-select v-model="addForm.clubId" filterable clearable placeholder="请选择协会" style="width: 100%;">
<el-option v-for="item in addClubOption"
:key="item.id"
:label="item.clubName"
:value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="总额度" prop="totalQuota">
<el-input-number v-model="addForm.totalQuota"
:min="0"
:precision="2"
placeholder="请输入总额度"
style="width: 100%"></el-input-number>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="closeAddDialog">取消</el-button>
<el-button type="primary" @click="submitAddAllocation">确定</el-button>
</div>
</el-dialog>
</template>
</guava>
</div>
@@ -102,11 +139,70 @@ layout("/layouts/platform.html"){
pageForm: {
year: this.$moment().format("YYYY")
},
isAllocation: false
isAllocation: false,
addDialogVisible: false,
addClubOption: [],
addForm: {
year: this.$moment().format("YYYY"),
clubId: "",
totalQuota: undefined
},
addFormRules: {
year: [{required: true, message: '必填', trigger: ['change', 'blur']}],
clubId: [{required: true, message: '必填', trigger: ['change', 'blur']}],
totalQuota: [{required: true, message: '必填', trigger: ['change', 'blur']}]
},
addYearPickerOptions: {
disabledDate(time) {
const currentYear = Number(moment().format("YYYY"))
return time.getFullYear() < currentYear
}
}
}
},
components: {},
methods: {
openAddDialog() {
this.$set(this, "addDialogVisible", true)
this.$set(this, "addForm", {
year: this.pageForm.year,
clubId: "",
totalQuota: undefined
})
this.$nextTick(() => {
if (this.$refs.addFormRef) {
this.$refs.addFormRef.clearValidate()
}
})
this.loadAvailableClubOption()
},
closeAddDialog() {
this.$set(this, "addDialogVisible", false)
},
addYearChange() {
this.$set(this.addForm, "clubId", "")
this.loadAvailableClubOption()
},
loadAvailableClubOption() {
if (!this.addForm.year) {
this.$set(this, "addClubOption", [])
return
}
Promise.all([
this.$businessTool.listCLubByRole(),
this.$axios.post("/platform/outlay/outlayManage/clubManage/getAllocatedClubIds", {
year: this.addForm.year
})
]).then((res) => {
const allClubOption = res[0] || []
const allocatedClubIds = res[1].code === 0 ? res[1].data : []
const availableClubOption = allClubOption.filter(item => !allocatedClubIds.includes(item.id))
this.$set(this, "addClubOption", availableClubOption)
if (availableClubOption.length === 0) {
this.$message.warning("当前年度没有可新增分配的协会");
}
})
},
doSubmit(row) {
this.$confirm('您确定要提交此条记录吗?', '提示', {
confirmButtonText: '确定',
@@ -125,6 +221,29 @@ layout("/layouts/platform.html"){
})
})
},
submitAddAllocation() {
this.$refs.addFormRef.validate((valid) => {
if (valid) {
this.$confirm('您确定要新增这条分配记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$axios.post("/platform/outlay/outlayManage/clubManage/addAllocation", this.addForm)
.then((resp) => {
if (resp.code === 0) {
this.$message.success("新增成功");
this.closeAddDialog()
this.doSearch()
this.getIsAllocation()
} else {
this.$message.error(resp.message)
}
})
})
}
})
},
resetOutlay() {
this.$confirm('您确定要重置今年的记录吗?', '提示', {
confirmButtonText: '确定',