package com.kingdee.eas.custom.compensation.osf;
import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.bsf.service.app.IHRMsfService;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.basedata.org.AdminOrgUnitInfo;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.compensation.*;
import com.kingdee.eas.framework.CoreBaseCollection;
import com.kingdee.eas.util.app.DbUtil;
import com.kingdee.jdbc.rowset.IRowSet;
import com.kingdee.shr.base.syssetting.app.osf.OSFAppUtil;
import com.kingdee.shr.compensation.CalSchemeCollection;
import com.kingdee.shr.compensation.CalSchemeFactory;
import com.kingdee.shr.compensation.CalSchemeInfo;
import com.kingdee.shr.compensation.ICalScheme;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 手机押金单据OSF服务
*
* 实现 {@link IHRMsfService} 接口,作为薪酬计算的OSF(Open Service Framework)后处理节点,
* 负责根据薪酬计算结果自动生成、更新或删除员工的手机押金收退单据。
*
*
* 核心业务流程
*
* - 从薪酬计算表(T_HR_SCMPCALTABLE)读取计算完成的手机押金收取/退回金额。
* - 按人员匹配已有押金单据:存在则更新,不存在则新建。
* - 支持反审批(mack="deleteDeposit"):先将对应来源的押金字段清空,若单据全部为空则删除整条记录。
* - 支持跨月补算:当薪酬方案的上次同步标记与实际期间不一致时,先补算上月数据再算本月。
*
*
* 动态字段说明
* s204、s205 为薪酬计算表中的动态字段名,分别对应手机押金收取金额和退回金额。
* 使用动态字段名的原因是不同客户的薪酬方案中这两个字段的列名可能不同。
*
* @see IHRMsfService
* @see HandMacDepositFactory
*/
public class HandMacDepositOSFService implements IHRMsfService {
/**
* 反审批操作标记
* 当 map 中的 mack 参数等于此值时,在生成单据之前会先执行反审批清理逻辑,
* 将对应来源的押金字段置空或删除整个单据。
*/
private static String UN_DEPOSIT = "deleteDeposit";
/**
* 执行押金单据处理(同步方法)
*
* 核心调度逻辑:
*
* - 若未指定薪酬方案(calSchemeID为空),则遍历所有启用状态的方案,逐个处理。
* - 若方案的简单名称标记与实际期间不一致(说明存在跨月未同步),则先补算标记中的历史期间数据。
* - 处理完成后更新方案的简单名称为当前期间,作为下次比对的基准。
* - 若 mack 等于 "deleteDeposit",在每个方案处理前先执行反审批清理。
*
*
* @param ctx BOS上下文
* @param param OSF传入的原始参数Map
* @param calSchemeID 薪酬计算方案ID,为空时遍历所有启用方案
* @param beginDate 手动指定的开始日期,为空则从方案配置或历史标记中读取
* @param endDate 手动指定的结束日期,为空则从方案配置或历史标记中读取
* @param personIds 人员ID列表(逗号分隔),用于限定处理范围
* @param calTimeStr 手动指定的计算次数,为空则从方案配置或历史标记中读取
* @param s204 薪酬计算表中"手机押金收取金额"的动态字段名
* @param s205 薪酬计算表中"手机押金退回金额"的动态字段名
* @param mack 操作标记,{@link #UN_DEPOSIT} 表示反审批
* @return 处理结果:"ok" 表示成功,否则返回错误信息
* @throws BOSException BOS框架异常
*/
protected synchronized String _exceDeposit(Context ctx, Map param, String calSchemeID, String beginDate, String endDate, String personIds, String calTimeStr, String s204, String s205, String mack) throws BOSException {
String result = null;
try {
IHandMacDeposit handMacDeposit = HandMacDepositFactory.getLocalInstance(ctx);
// 未指定方案ID:遍历所有启用状态的薪酬计算方案
if (StringUtils.isBlank(calSchemeID)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 查询所有启用状态的薪酬计算方案
IRowSet rs = DbUtil.executeQuery(ctx, "select * from T_HR_SCalScheme where Fstate=1");
while (rs.next()) {
String desc = rs.getString("FsimpleName");
String fid = rs.getString("fid");
String beginDateStr = sdf.format(rs.getDate("FbeginDate"));
String endDateStr = sdf.format(rs.getDate("FendDate"));
int calTime = rs.getInt("FcalTime");
// 将当前方案的期间信息拼接为标记字符串,用于判断是否需要跨月补算
String desc1 = beginDateStr + "," + endDateStr + "," + calTime;
Map map = new HashMap();
map.put("personIds", personIds);
map.put("calSchemeID", fid);
// 跨月补算检测:FsimpleName 存储了上次处理时的"开始日期,结束日期,计算次数"标记。
// 若标记与实际方案期间不一致,说明中间有月份未同步,需先补算历史期间数据。
if (null != desc && desc.indexOf(",") > 0) {
String[] descArr = desc.split(",");
if (descArr.length < 3 || desc.equals(desc1)) {
// 历史标记格式不完整或与当前方案一致,跳过补算
} else {
// --- 补算历史期间数据 ---
Map map1 = new HashMap();
map1.put("personIds", personIds);
map1.put("calSchemeID", fid);
// 优先使用手动指定的日期/次数,否则使用历史标记中的值
if (beginDate != null) {
map1.put("beginDate", beginDate);
} else {
map1.put("beginDate", descArr[0]);
}
if (endDate != null) {
map1.put("endDate", endDate);
} else {
map1.put("endDate", descArr[1]);
}
if (calTimeStr != null) {
map1.put("calTime", calTimeStr);
} else {
map1.put("calTime", descArr[2]);
}
// 反审批时先清理历史数据
if (null != mack && mack.equals(UN_DEPOSIT)) {
deleteDeposit(ctx, param, (String) map1.get("calSchemeID"), (String) map1.get("beginDate"), (String) map1.get("endDate"), (String) map1.get("calTime"), s204, s205, handMacDeposit);
}
// 生成/更新押金单据
Object obj = saveDeposit(ctx, param, (String) map1.get("calSchemeID"), (String) map1.get("beginDate"), (String) map1.get("endDate"), (String) map1.get("calTime"), s204, s205, handMacDeposit);
}
}
// --- 处理当前期间数据 ---
// 构建当前期间的查询参数:优先使用手动指定的日期/次数,否则使用方案配置的默认值
if (beginDate != null) {
map.put("beginDate", beginDate);
} else {
map.put("beginDate", beginDateStr);
map.put("calTime", String.valueOf(calTime));
}
if (endDate != null) {
map.put("endDate", endDate);
} else {
map.put("endDate", endDateStr);
map.put("calTime", String.valueOf(calTime));
}
if (calTimeStr != null) {
map.put("calTime", calTimeStr);
} else {
map.put("calTime", String.valueOf(calTime));
}
// 反审批时先清理数据
if (null != mack && mack.equals(UN_DEPOSIT)) {
deleteDeposit(ctx, param, (String) map.get("calSchemeID"), (String) map.get("beginDate"), (String) map.get("endDate"), (String) map.get("calTime"), s204, s205, handMacDeposit);
}
// 生成/更新押金单据
Object obj = saveDeposit(ctx, param, (String) map.get("calSchemeID"), (String) map.get("beginDate"), (String) map.get("endDate"), (String) map.get("calTime"), s204, s205, handMacDeposit);
result = (null != obj) ? obj.toString() : null;
// 更新方案的简单名称标记,记录本次处理的期间,供下次比对使用
DbUtil.execute(ctx, "update T_HR_SCalScheme set FsimpleName=? where fid= ? ", new Object[]{desc1, fid});
}
} else {
// 指定了方案ID:直接处理该方案,无需遍历
if (null != mack && mack.equals(UN_DEPOSIT)) {
deleteDeposit(ctx, param, calSchemeID, beginDate, endDate, calTimeStr, s204, s205, handMacDeposit);
}
Object obj = saveDeposit(ctx, param, calSchemeID, beginDate, endDate, calTimeStr, s204, s205, handMacDeposit);
result = (null != obj) ? obj.toString() : null;
}
} catch (Exception e) {
e.printStackTrace();
result = e.getMessage();
}
return result;
}
/**
* OSF服务入口方法
*
* 由薪酬计算引擎在计算完成后回调,从 map 中提取参数并委托给 {@link #_exceDeposit} 执行。
*
*
* @param context BOS上下文
* @param map 参数Map,支持以下键:
*
* - calSchemeID - 薪酬计算方案ID(可选,为空则处理所有启用方案)
* - beginDate - 开始日期(可选)
* - endDate - 结束日期(可选)
* - calTime - 计算次数(可选)
* - personIds - 人员ID列表,逗号分隔(可选)
* - s204 - 手机押金收取金额字段名(必填)
* - s205 - 手机押金退回金额字段名(必填)
* - mack - 操作标记,"deleteDeposit"表示反审批(可选)
*
* @return 处理结果字符串
* @throws EASBizException 业务异常
* @throws BOSException BOS框架异常
*/
@Override
public Object process(Context context, Map map) throws EASBizException, BOSException {
String calSchemeID = (String) map.get("calSchemeID");
String beginDate = (String) map.get("beginDate");
String endDate = (String) map.get("endDate");
String calTime = (String) map.get("calTime");
String personIds = (String) map.get("personIds");
String s204 = (String) map.get("s204");
String s205 = (String) map.get("s205");
String mack = (String) map.get("mack");
return _exceDeposit(context, map, calSchemeID, beginDate, endDate, personIds, calTime, s204, s205, mack);
}
/**
* 审批通过处理:根据薪酬计算结果生成或更新手机押金单据
*
* 核心逻辑:
*
* - 从薪酬计算表查询计算完成的手机押金收退数据(FCalState in 12,20)。
* - 按人员查找已有生效押金单据:
*
* - 有历史单据且来源匹配:直接更新已有单据的押金收退字段。
* - 有历史单据但来源不匹配:旧单据失效,创建新单据(合并旧数据防止丢失)。
* - 无历史单据:直接创建新单据。
*
*
* - 清理多余的生效单据(同一人员只保留一条生效记录)。
*
*
* @param context BOS上下文
* @param map OSF原始参数Map
* @param calSchemeID 薪酬计算方案ID
* @param beginDate 开始日期
* @param endDate 结束日期
* @param calTime 计算次数
* @param s204 手机押金收取金额的动态字段名
* @param s205 手机押金退回金额的动态字段名
* @param handMacDeposit 手机押金单据操作接口
* @return "ok" 表示成功,"err" 表示异常,其他字符串为具体错误信息
*/
private String saveDeposit(Context context, Map map, String calSchemeID, String beginDate, String endDate, String calTime, String s204, String s205, IHandMacDeposit handMacDeposit) {
try {
System.out.println("calSchemeID:" + calSchemeID);
System.out.println("beginDate:" + beginDate);
System.out.println("endDate:" + endDate);
System.out.println("calTime:" + calTime);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 参数兜底:calSchemeID 为空时尝试从 map 中取 calSchemeId 键
if (null == calSchemeID) {
calSchemeID = (String) map.get("calSchemeId");
}
// 必填参数校验
if (null == calSchemeID) {
return "calSchemeID为空";
}
if (null == s204) {
return "s204为空";
}
if (null == s205) {
return "s205为空";
}
String personIds = (String) map.get("personIds");
// 构建查询SQL:从薪酬计算表读取已计算完成的押金收退数据
// FCalState=12 表示审批通过,20 表示已锁定,均为有效计算状态
StringBuilder sql = new StringBuilder();
List