feat:分工会临时报销人功能
This commit is contained in:
@@ -106,6 +106,7 @@ RoleConstant {
|
||||
LEGAL_DOCTOR("法律顾问"),
|
||||
|
||||
UNION_REIMBURSEMENT_MANAGER("分工会报销负责人"),
|
||||
UNION_TEMP_REIMBURSEMENT_APPLICANT("分工会临时报销人"),
|
||||
CLUB_REIMBURSEMENT_MANAGER("协会报销负责人"),
|
||||
SCHOOL_REIMBURSEMENT_MANAGER("校工会报销负责人"),
|
||||
|
||||
|
||||
+91
@@ -23,6 +23,7 @@ import com.budwk.app.zhgh.activity.declarereimbursement.declare.models.ActivityD
|
||||
import com.budwk.app.zhgh.activity.declarereimbursement.vo.CommonPageParam;
|
||||
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.models.ActivityReimbursementInfo;
|
||||
import com.budwk.app.zhgh.activity.declarereimbursement.reimbursement.service.ActivityReimbursementService;
|
||||
import com.budwk.app.zhgh.dayofficework.outlay.activityBudget.models.ActivityBudget;
|
||||
import com.budwk.app.zhgh.dayofficework.outlay.outlayReimburse.service.OutlayReimburseApplyService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
@@ -94,6 +95,10 @@ public class ActivityReimbursementApplyController {
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "保存申请,申请人: ${args[0].username}")
|
||||
public Result save(@Param("data") ActivityReimbursementInfo activityReimbursementInfo) {
|
||||
Result scopeResult = validateTemporaryUnionReimbursement(activityReimbursementInfo);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
activityReimbursementInfo.setUserId(SecurityUtil.getUserId());
|
||||
activityReimbursementInfo.setLoginName(SecurityUtil.getUserLoginname());
|
||||
activityReimbursementInfo.setUserName(SecurityUtil.getUserUsername());
|
||||
@@ -123,6 +128,10 @@ public class ActivityReimbursementApplyController {
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "提交申请,申请人: ${args[0].username}")
|
||||
public Result submit(@Param("data") ActivityReimbursementInfo activityReimbursementInfo) {
|
||||
Result scopeResult = validateTemporaryUnionReimbursement(activityReimbursementInfo);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
activityReimbursementInfo.setUserId(SecurityUtil.getUserId());
|
||||
activityReimbursementInfo.setLoginName(SecurityUtil.getUserLoginname());
|
||||
activityReimbursementInfo.setUserName(SecurityUtil.getUserUsername());
|
||||
@@ -170,6 +179,10 @@ public class ActivityReimbursementApplyController {
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
@SLog(tag = "活动申报", msg = "重新提交申请,申请人: ${args[0].username}")
|
||||
public Result submitAgain(@Param("data") ActivityReimbursementInfo activityReimbursementInfo, @Param("taskId") Long taskId) {
|
||||
Result scopeResult = validateTemporaryUnionReimbursement(activityReimbursementInfo);
|
||||
if (scopeResult != null) {
|
||||
return scopeResult;
|
||||
}
|
||||
if (StrUtil.isBlank(activityReimbursementInfo.getDeclareId())) {
|
||||
activityReimbursementInfo.setDeclareId(activityReimbursementInfo.getDeclareId());
|
||||
}
|
||||
@@ -196,13 +209,78 @@ public class ActivityReimbursementApplyController {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 临时报销人按本分工会校验申请及实际扣款预算,防止篡改身份、经费类型或预算ID绕过限制。
|
||||
*/
|
||||
private Result validateTemporaryUnionReimbursement(ActivityReimbursementInfo info) {
|
||||
if (!AuthUtil.hasRole(RoleConstant.UNION_TEMP_REIMBURSEMENT_APPLICANT.name())) {
|
||||
return null;
|
||||
}
|
||||
if (info == null || !Objects.equals(info.getReimbursementIdentity(), "union")
|
||||
|| !Objects.equals(info.getOutlayManageSource(), "ACTIVITY_BUDGET_TYPE_TWO")
|
||||
|| StrUtil.isNotBlank(info.getClubId())) {
|
||||
return Result.error("分工会临时报销人仅可使用分工会身份申请分工会经费报销");
|
||||
}
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
if (StrUtil.isBlank(unionId)) {
|
||||
return Result.error("当前用户未关联分工会,无法申请报销");
|
||||
}
|
||||
if (StrUtil.isNotBlank(info.getId())) {
|
||||
ActivityReimbursementInfo savedInfo = dao.fetch(ActivityReimbursementInfo.class, info.getId());
|
||||
if (savedInfo == null || !Objects.equals(savedInfo.getUserId(), SecurityUtil.getUserId())
|
||||
|| !Objects.equals(savedInfo.getUnionId(), unionId)
|
||||
|| !Objects.equals(savedInfo.getReimbursementIdentity(), "union")
|
||||
|| !Objects.equals(savedInfo.getOutlayManageSource(), "ACTIVITY_BUDGET_TYPE_TWO")) {
|
||||
return Result.error("仅可修改本人所在分工会的分工会经费报销申请");
|
||||
}
|
||||
}
|
||||
if (StrUtil.isBlank(info.getDeductionBudgetId())) {
|
||||
return Result.error("请选择本分工会的扣款预算");
|
||||
}
|
||||
ActivityBudget budget = dao.fetch(ActivityBudget.class, info.getDeductionBudgetId());
|
||||
if (budget == null || !Objects.equals(budget.getOutlayManageSource(), "ACTIVITY_BUDGET_TYPE_TWO")
|
||||
|| !Objects.equals(budget.getUnionId(), unionId)) {
|
||||
return Result.error("扣款预算必须属于本分工会的分工会经费");
|
||||
}
|
||||
if (Objects.equals(info.getActivityReimbursementMode(), "activity")) {
|
||||
if (StrUtil.isBlank(info.getDeclareId())) {
|
||||
return Result.error("请选择本分工会的申报活动");
|
||||
}
|
||||
// 申报通过活动类型区分工会,实际扣款经费类型及归属已由上方预算校验约束。
|
||||
ActivityDeclareInfo declareInfo = dao.fetch(ActivityDeclareInfo.class, info.getDeclareId());
|
||||
if (declareInfo == null || !Objects.equals(declareInfo.getUnionId(), unionId)
|
||||
|| !Objects.equals(declareInfo.getActivityType(), "BRANCH_UNION")) {
|
||||
return Result.error("仅可报销本分工会使用分工会经费的活动");
|
||||
}
|
||||
} else if (Objects.equals(info.getActivityReimbursementMode(), "daily")) {
|
||||
info.setDeclareId(null);
|
||||
} else {
|
||||
return Result.error("请选择报销模式");
|
||||
}
|
||||
// 分工会归属以登录用户为准,供后续审批流程使用。
|
||||
info.setUnionId(unionId);
|
||||
info.setActivityType("BRANCH_UNION");
|
||||
return null;
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("获取当前用户活动报销")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
public Result getActivityReimbursementByUser(String id, String outlayManageSource, String clubId) {
|
||||
String activityType = "";
|
||||
boolean temporaryUnionApplicant = AuthUtil.hasRole(RoleConstant.UNION_TEMP_REIMBURSEMENT_APPLICANT.name());
|
||||
if (temporaryUnionApplicant && (!Objects.equals(outlayManageSource, "ACTIVITY_BUDGET_TYPE_TWO")
|
||||
|| StrUtil.isBlank(SecurityUtil.getUnionId()))) {
|
||||
return Result.error("分工会临时报销人仅可申请本分工会经费报销");
|
||||
}
|
||||
if (StrUtil.isNotBlank(id)) {
|
||||
ActivityReimbursementInfo reimbursementInfo = dao.fetch(ActivityReimbursementInfo.class, id);
|
||||
if (temporaryUnionApplicant && (reimbursementInfo == null
|
||||
|| !Objects.equals(reimbursementInfo.getUserId(), SecurityUtil.getUserId())
|
||||
|| !Objects.equals(reimbursementInfo.getUnionId(), SecurityUtil.getUnionId())
|
||||
|| !Objects.equals(reimbursementInfo.getOutlayManageSource(), "ACTIVITY_BUDGET_TYPE_TWO"))) {
|
||||
return Result.error("无权访问该报销申请");
|
||||
}
|
||||
if (Objects.equals(reimbursementInfo.getActivityReimbursementMode(), "daily")) {
|
||||
return Result.success();
|
||||
}
|
||||
@@ -324,7 +402,20 @@ public class ActivityReimbursementApplyController {
|
||||
@ApiOperation("获取申报的记录")
|
||||
@SaCheckPermission(value = {"activityReimbursement.apply", "h5.activityReimbursement.apply"}, mode = SaMode.OR)
|
||||
public Result getBudgetByYear(String outlayManageSource, String clubId) {
|
||||
boolean temporaryUnionApplicant = AuthUtil.hasRole(RoleConstant.UNION_TEMP_REIMBURSEMENT_APPLICANT.name());
|
||||
if (temporaryUnionApplicant && (!Objects.equals(outlayManageSource, "ACTIVITY_BUDGET_TYPE_TWO")
|
||||
|| StrUtil.isBlank(SecurityUtil.getUnionId()))) {
|
||||
return Result.error("分工会临时报销人仅可申请本分工会经费报销");
|
||||
}
|
||||
List<NutMap> budgetMoney = activityReimbursementService.getBudgetMoney(outlayManageSource, clubId);
|
||||
if (temporaryUnionApplicant) {
|
||||
// 兼有系统管理员角色时也必须限制为本分工会预算,不改变原预算金额展示规则。
|
||||
List<String> unionBudgetIds = dao.query(ActivityBudget.class,
|
||||
Cnd.where("unionId", "=", SecurityUtil.getUnionId())
|
||||
.and("outlayManageSource", "=", "ACTIVITY_BUDGET_TYPE_TWO"))
|
||||
.stream().map(ActivityBudget::getId).toList();
|
||||
budgetMoney = budgetMoney.stream().filter(v -> unionBudgetIds.contains(v.getString("id"))).toList();
|
||||
}
|
||||
return Result.success(budgetMoney);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
-- 分工会临时报销人:复用现有报销菜单;经费范围由申请页面及后端按角色编码共同限制。
|
||||
-- 适用于已配置费用报销菜单的 MySQL 数据库,可重复执行,不关联具体用户。
|
||||
START TRANSACTION;
|
||||
|
||||
INSERT INTO sys_role
|
||||
(id, name, code, aliasName, disabled, unitid, note, sort, createdBy, createdAt, updatedBy, updatedAt, delFlag)
|
||||
SELECT REPLACE(UUID(), '-', ''), '分工会临时报销人', 'UNION_TEMP_REIMBURSEMENT_APPLICANT',
|
||||
'分工会临时报销人', 0, '', '仅可使用分工会身份申请本分工会经费报销', 0,
|
||||
'', FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000),
|
||||
'', FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000), 0
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role WHERE code = 'UNION_TEMP_REIMBURSEMENT_APPLICANT'
|
||||
);
|
||||
|
||||
-- 只授权报销申请、我的报销及其导航父级,不复制原报销角色的审批等权限。
|
||||
-- activityDeclare 用于报销选取活动后读取申报详情,不授权活动申报、预算申报或审核子菜单。
|
||||
-- 同时关联已有的手机端报销菜单;通过路径前缀补齐导航父级,不授权同级菜单。
|
||||
INSERT INTO sys_role_menu (roleId, menuId)
|
||||
SELECT DISTINCT role.id, menu.id
|
||||
FROM sys_role role
|
||||
JOIN sys_menu entry ON entry.permission IN (
|
||||
'activityReimbursement.apply',
|
||||
'activityReimbursement.mine',
|
||||
'h5.activityReimbursement.apply',
|
||||
'h5.activityReimbursement.mine',
|
||||
'activityDeclare'
|
||||
)
|
||||
JOIN sys_menu menu ON menu.id = entry.id OR (
|
||||
menu.type = 'menu'
|
||||
AND menu.path IS NOT NULL
|
||||
AND menu.path <> ''
|
||||
AND entry.path LIKE CONCAT(menu.path, '%')
|
||||
)
|
||||
WHERE role.code = 'UNION_TEMP_REIMBURSEMENT_APPLICANT'
|
||||
AND entry.disabled = 0
|
||||
AND menu.disabled = 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu assigned
|
||||
WHERE assigned.roleId = role.id AND assigned.menuId = menu.id
|
||||
);
|
||||
|
||||
-- 工会管理的角色下拉框及人员列表均读取 BRANCH_UNION_ROLES 字典,角色表记录不会自动成为选项。
|
||||
-- 沿用字典每级四位的路径规则,将新角色追加到已有子项之后,并按父级与角色编码避免重复插入。
|
||||
INSERT INTO sys_dict
|
||||
(id, parentId, path, name, remark, code, disabled, location, hasChildren,
|
||||
createdBy, createdAt, updatedBy, updatedAt, delFlag)
|
||||
SELECT REPLACE(UUID(), '-', ''), parent.id,
|
||||
CONCAT(parent.path, LPAD(COALESCE(siblings.maxSuffix, 0) + 1, 4, '0')),
|
||||
'分工会临时报销人', '仅可申请本分工会经费报销', 'UNION_TEMP_REIMBURSEMENT_APPLICANT',
|
||||
0, COALESCE(siblings.maxLocation, 0) + 1, 0,
|
||||
'', FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000),
|
||||
'', FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000), 0
|
||||
FROM sys_dict parent
|
||||
LEFT JOIN (
|
||||
SELECT parentId, MAX(CAST(RIGHT(path, 4) AS UNSIGNED)) AS maxSuffix,
|
||||
MAX(location) AS maxLocation
|
||||
FROM sys_dict
|
||||
GROUP BY parentId
|
||||
) siblings ON siblings.parentId = parent.id
|
||||
WHERE parent.code = 'BRANCH_UNION_ROLES'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_dict existing
|
||||
WHERE existing.parentId = parent.id
|
||||
AND existing.code = 'UNION_TEMP_REIMBURSEMENT_APPLICANT'
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
+26
@@ -380,6 +380,9 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isTemporaryUnionApplicant() {
|
||||
return this.$auth.hasRole('UNION_TEMP_REIMBURSEMENT_APPLICANT')
|
||||
},
|
||||
hasUnionRole() {
|
||||
return this.$auth.hasPermission('activityReimbursement.apply.unionHas')
|
||||
},
|
||||
@@ -565,6 +568,10 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
|
||||
this.formData.id = this.bizId ? this.bizId : null
|
||||
if (this.isTemporaryUnionApplicant()) {
|
||||
// 申报记录的ID不能作为报销主键,单独保留关联活动以供后端校验。
|
||||
this.$set(this.formData, 'declareId', id)
|
||||
}
|
||||
|
||||
if (data.planStartTime && data.planEndTime) {
|
||||
this.formData.planDate = [data.planStartTime, data.planEndTime]
|
||||
@@ -602,6 +609,15 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
initBudgetType() {
|
||||
this.$businessTool.getDictOptions("ACTIVITY_BUDGET_TYPE").then(resp => {
|
||||
// 临时报销人即使兼有其他角色,也只能使用分工会经费。
|
||||
if (this.isTemporaryUnionApplicant()) {
|
||||
this.budgetTypeOption = resp.filter(v => v.code === "ACTIVITY_BUDGET_TYPE_TWO")
|
||||
if (!this.bizId) {
|
||||
this.$set(this.formData, 'outlayManageSource', 'ACTIVITY_BUDGET_TYPE_TWO')
|
||||
this.outlayManageSourceChange('ACTIVITY_BUDGET_TYPE_TWO')
|
||||
}
|
||||
return
|
||||
}
|
||||
this.budgetTypeOption = resp
|
||||
const budgetTypeCloseOption = []
|
||||
const typeOneOption = resp.find(v => v.code === "ACTIVITY_BUDGET_TYPE_ONE")
|
||||
@@ -636,6 +652,16 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
},
|
||||
initReimbursementIdentity() {
|
||||
if (this.isTemporaryUnionApplicant()) {
|
||||
this.reimbursementIdentityOption = [{name: "分工会", code: "union"}]
|
||||
if (!this.bizId) {
|
||||
this.$set(this.formData, 'reimbursementIdentity', 'union')
|
||||
if (this.$store.state.user.union) {
|
||||
this.reimbursementIdentityChange('union')
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (this.$auth.hasPermission('activityReimbursement.apply.schoolHas')) {
|
||||
this.reimbursementIdentityOption.push({name: "校工会", code: "school"})
|
||||
}
|
||||
|
||||
+24
@@ -276,6 +276,9 @@ layout("/layouts/platform_h5.html"){
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isTemporaryUnionApplicant() {
|
||||
return this.$auth.hasRole('UNION_TEMP_REIMBURSEMENT_APPLICANT')
|
||||
},
|
||||
hasUnionRole() {
|
||||
return this.$auth.hasPermission('activityReimbursement.apply.unionHas')
|
||||
},
|
||||
@@ -786,6 +789,16 @@ layout("/layouts/platform_h5.html"){
|
||||
},
|
||||
initBudgetType() {
|
||||
return this.$businessTool.getDictOptions('ACTIVITY_BUDGET_TYPE').then((resp) => {
|
||||
// 与电脑端保持一致,临时报销人的经费范围不叠加其他角色权限。
|
||||
if (this.isTemporaryUnionApplicant()) {
|
||||
this.budgetTypeOption = resp.filter((item) => item.code === 'ACTIVITY_BUDGET_TYPE_TWO')
|
||||
if (!this.bizId) {
|
||||
this.$set(this.formData, 'outlayManageSource', 'ACTIVITY_BUDGET_TYPE_TWO')
|
||||
this.$set(this.formData, 'outlayManageSourceName', '分工会经费')
|
||||
this.outlayManageSourceChange('ACTIVITY_BUDGET_TYPE_TWO')
|
||||
}
|
||||
return
|
||||
}
|
||||
const result = []
|
||||
const typeOne = resp.find((item) => item.code === 'ACTIVITY_BUDGET_TYPE_ONE')
|
||||
if (typeOne) {
|
||||
@@ -815,6 +828,17 @@ layout("/layouts/platform_h5.html"){
|
||||
})
|
||||
},
|
||||
initReimbursementIdentity() {
|
||||
if (this.isTemporaryUnionApplicant()) {
|
||||
this.reimbursementIdentityOption = [{name: '分工会', code: 'union'}]
|
||||
if (!this.bizId) {
|
||||
this.$set(this.formData, 'reimbursementIdentity', 'union')
|
||||
this.$set(this.formData, 'reimbursementIdentityName', '分工会')
|
||||
if (this.$store.state.user.union) {
|
||||
this.reimbursementIdentityChange('union')
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (this.hasSchoolLevelRole()) {
|
||||
this.reimbursementIdentityOption.push({name: '校工会', code: 'school'})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user