package com.kingdee.eas.custom.compensation.startingsalary.backgroundtransaction;

import com.kingdee.bos.dao.IObjectPK;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.bos.metadata.entity.SelectorItemCollection;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.bos.util.EASResource;
import com.kingdee.eas.base.permission.UserInfo;
import com.kingdee.eas.basedata.assistant.CurrencyInfo;
import com.kingdee.eas.basedata.org.AdminOrgUnitInfo;
import com.kingdee.eas.basedata.org.HROrgUnitInfo;
import com.kingdee.eas.basedata.org.PositionInfo;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.framework.CoreBaseInfo;
import com.kingdee.eas.hr.base.ApproveTypeEnum;
import com.kingdee.eas.hr.base.HRBillStateEnum;
import com.kingdee.eas.hr.elecontract.util.EmpElectronicContractUtilsEx;
import com.kingdee.eas.hr.elecontract.util.EmpElectronicOtherContractUtilsEx;
import com.kingdee.jdbc.rowset.IRowSet;
import com.kingdee.shr.base.syssetting.api.bean.BatchMessageTipsBody;
import com.kingdee.shr.base.syssetting.api.bean.BatchMessageTipsHeader;
import com.kingdee.shr.base.syssetting.context.SHRContext;
import com.kingdee.shr.base.syssetting.exception.SHRWebException;
import com.kingdee.shr.base.syssetting.exception.ShrWebBizException;
import com.kingdee.shr.cmpdesign.*;
import com.kingdee.shr.cmpdesign.fix.FixAdjustSalaryExecute;
import com.kingdee.shr.cmpdesign.fix.FixAdjustSalaryExecuteCommon;
import com.kingdee.shr.compensation.*;
import com.kingdee.shr.compensation.exception.ExceptionHandle;
import com.kingdee.shr.compensation.util.CmpSQLUtil;
import com.kingdee.shr.compensation.util.CmpStrUtil;
import com.kingdee.shr.compensation.util.FunUtils;
import com.kingdee.util.StringUtils;
import org.apache.log4j.Logger;

import javax.ejb.*;
import java.math.BigDecimal;
import java.rmi.RemoteException;
import com.kingdee.bos.*;
import com.kingdee.bos.util.BOSObjectType;
import com.kingdee.bos.metadata.IMetaDataPK;
import com.kingdee.bos.metadata.rule.RuleExecutor;
import com.kingdee.bos.metadata.MetaDataPK;
//import com.kingdee.bos.metadata.entity.EntityViewInfo;
import com.kingdee.bos.framework.ejb.AbstractEntityControllerBean;
import com.kingdee.bos.framework.ejb.AbstractBizControllerBean;
//import com.kingdee.bos.dao.IObjectPK;
import com.kingdee.bos.dao.IObjectValue;
import com.kingdee.bos.dao.IObjectCollection;
import com.kingdee.bos.service.ServiceContext;
import com.kingdee.bos.service.IServiceContext;

import java.lang.String;
import java.util.*;

/**
 * 薪资工具Facade实现类
 * <p>
 * 自定义薪资相关后台事务工具服务，扩展批量薪资调整单生效、电子合同批量发起签署能力
 * 适配入职起薪场景后台调用，支持跳过审批直接生效批量薪资调整单，并同步生成固定薪资调整记录
 * </p>
 * @author 自定义开发
 */
public class SalaryUtilsFacadeControllerBean extends AbstractSalaryUtilsFacadeControllerBean
{
    private static Logger logger =
            Logger.getLogger("com.kingdee.eas.custom.compensation.startingsalary.backgroundtransaction.SalaryUtilsFacadeControllerBean");

    /**
     * 批量发起其他电子合同签署（重写父类方法，增加批量执行结果收集）
     * <p>
     * 支持传入多个合同ID逗号分隔，循环逐个发起签署，收集每条执行成功/失败结果，组装批量消息头返回
     * </p>
     * @param ctx BOS上下文
     * @param otherContractId 多个其他电子合同主键ID，多个ID使用英文逗号分隔
     * @throws BOSException BOS框架异常
     */
    @Override
    public void sendBatchEmployeeOtherSign(Context ctx, String otherContractId) throws BOSException {
        super.sendBatchEmployeeOtherSign(ctx, otherContractId);
        // 批量消息结果头部对象，汇总所有单据执行情况
        BatchMessageTipsHeader batchMessageTipsHeader = new BatchMessageTipsHeader();
        // 拆分多个合同ID数组
        String[] ids = otherContractId.split(",");
        int sucess = 0;
        int failure = 0;
        // 统计map：保存成功数、失败数
        HashMap<String, Integer> hashMap = new HashMap();
        hashMap.put("success", sucess);
        hashMap.put("failure", failure);
        // 是否批量模式标识，传入电子合同工具类
        Boolean batchAgain = true;

        // 循环逐个发起电子合同签署
        for(String id : ids) {
            BatchMessageTipsBody body = new BatchMessageTipsBody();
            body.setId(id);

            try {
                // 发起单个其他电子合同签署
                EmpElectronicOtherContractUtilsEx.batchEmployeeOtherSign(ctx, id, (String)null, body, batchAgain);
                // 更新成功/失败统计
                EmpElectronicOtherContractUtilsEx.ProcessingSucceededOrFailed(body, hashMap);
            } catch (EASBizException e) {
                logger.error(e.getMessage(), e);
                body.setMuitTipsState(Boolean.FALSE);
                body.setMuitTipsMessage(e.getMessage());
                EmpElectronicContractUtilsEx.ProcessingSucceededOrFailed(body, hashMap);
            } catch (BOSException e) {
                logger.error(e.getMessage(), e);
                body.setMuitTipsState(Boolean.FALSE);
                body.setMuitTipsMessage(e.getMessage());
                EmpElectronicContractUtilsEx.ProcessingSucceededOrFailed(body, hashMap);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                body.setMuitTipsState(Boolean.FALSE);
                body.setMuitTipsMessage(e.getMessage());
                EmpElectronicContractUtilsEx.ProcessingSucceededOrFailed(body, hashMap);
            }

            // 将单条执行结果加入批量结果集
            batchMessageTipsHeader.addResult(body);
        }

        // 回填批量执行汇总信息
        batchMessageTipsHeader.setBillId(otherContractId);
        batchMessageTipsHeader.setFailureCount((Integer)hashMap.get("failure"));
        batchMessageTipsHeader.setSuccessCount((Integer)hashMap.get("success"));
        System.out.println("执行完成:" + batchMessageTipsHeader.toString());
    }

    /**
     * 根据批量薪资调整单主键，提交并生效薪资调整单（对外入口方法）
     * <p>
     * 后台事务调用入口，通过单据ID加载单据模型，调用内部submitEffect完成保存、生效、同步固定薪资记录
     * </p>
     * @param ctx BOS上下文
     * @param batchAdjustId 批量薪资调整单主键ID
     * @throws BOSException BOS框架异常
     */
    public void submitEffectBatchAdjustBill(Context ctx, String batchAdjustId) throws BOSException {
        try {
            SelectorItemCollection selector = new SelectorItemCollection();
            selector.add("*");
            selector.add("entry.*");
            // 根据主键加载批量薪资调整单主表+明细
            BatchAdjustBillInfo batchAdjustBillInfo = BatchAdjustBillFactory.getLocalInstance(ctx)
                    .getBatchAdjustBillInfo(new ObjectUuidPK(batchAdjustId),selector);
            this.submitEffect(ctx, batchAdjustBillInfo);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 提交并生效薪资调整单核心逻辑
     * <p>
     * 业务逻辑说明：
     * 1. 设置审批类型为直接审批，跳过工作流审批；
     * 2. 执行保存前校验、金额精度格式化；
     * 3. 保存薪资调整单；
     * 4. 将调整明细同步生成固定薪资调整记录；
     * 5. 更新单据状态为【已审核】，二次持久化单据；
     * </p>
     * @param ctx   上下文对象
     * @param model 薪资调整单数据模型（需传入BatchAdjustBillInfo实例）
     * @return IObjectPK 生效后的单据主键ID
     * @throws BOSException     BOS框架异常
     * @throws EASBizException  业务逻辑异常
     * @throws SHRWebException  SHR薪资模块异常
     */
    public IObjectPK submitEffect(Context ctx, CoreBaseInfo model) throws BOSException, EASBizException, SHRWebException {
        BatchAdjustBillInfo bill = (BatchAdjustBillInfo) model;
        bill.setApproveType(ApproveTypeEnum.DIRECT); // 审批类型：直接审批（无需工作流流转）
        bill.setBillState(HRBillStateEnum.SAVED); // 初始状态：已保存
        this.beforeSave(model, ctx); // 保存前预处理（数据校验+金额精度格式化）

        // 保存单据，获取主键
        IObjectPK savePK = BatchAdjustBillFactory.getLocalInstance(ctx).save(model);
        bill.setId(BOSUuid.read(savePK.toString())); // 将生成的主键绑定到单据实体

        // 同步至固定薪资调整记录（核心：调整单数据落地为实际生效薪资数据）
        this.submitToFixAdjustSalary(ctx, bill);
        bill.setBillState(HRBillStateEnum.AUDITED); // 更新单据状态为：已审核
        savePK = BatchAdjustBillFactory.getLocalInstance(ctx).save(model); // 再次保存更新单据状态

        return savePK;
    }

    /**
     * 薪资调整单保存前预处理
     * <p>
     * 执行顺序：先数据合法性校验，再遍历明细，按薪资项配置小数位数对金额四舍五入，防止金额精度溢出
     * </p>
     * @param model 薪资调整单数据模型
     * @param ctx   BOS上下文
     * @throws SHRWebException 预处理失败时抛出业务异常
     */
    protected void beforeSave(CoreBaseInfo model, Context ctx) throws SHRWebException {
        this.verify(model, ctx); // 先执行数据合法性校验
        BatchAdjustBillInfo batchInfo = (BatchAdjustBillInfo) model;
        BatchAdjustBillEntryCollection entryColl = batchInfo.getEntry();
        CmpItemInfo itemInfo = null; // 薪资项元数据（读取配置小数位数）
        BigDecimal money = null;     // 格式化后金额
        String cmpItemId = "";       // 薪资项主键ID

        try {
            if (!entryColl.isEmpty()) {
                // 遍历所有调整明细，统一金额精度
                for (int i = 0; i < entryColl.size(); ++i) {
                    BatchAdjustBillEntryInfo batchEntryInfo = entryColl.get(i);
                    // 转换金额为BigDecimal，规避浮点精度丢失
                    money = FunUtils.toBigDecimal(batchEntryInfo.getMoney());
                    // 获取薪资项ID，查询薪资项配置信息
                    cmpItemId = batchEntryInfo.getCmpItem().getId().toString();
                    itemInfo = CmpItemFactory.getLocalInstance(ctx)
                            .getCmpItemInfo("select id, dataDot, name where id = '" + cmpItemId + "'");
                    int dataDot = itemInfo.getDataDot().getValue(); // 薪资项配置保留小数位数
                    // 金额四舍五入至配置小数位，回写明细
                    batchEntryInfo.setMoney(money.setScale(dataDot, BigDecimal.ROUND_HALF_UP));
                }
            }
        } catch (Exception e) {
            ExceptionHandle.handleException(e); // 调用薪资统一异常处理工具
            e.printStackTrace();
            throw new RuntimeException("保存前预处理失败：" + e.getMessage());
        }
    }

    /**
     * 薪资调整单保存前数据校验
     * <p>
     * 校验场景区分：新增单据 / 编辑已有单据
     * 校验内容：
     * 1. 员工在职状态校验，禁止对离职人员调薪；
     * 2. 任职薪酬关联关系有效性校验；
     * 3. 新增/变更人员条目对应的薪酬任职关系是否存在；
     * 收集所有错误信息，全部校验完成后统一抛出异常
     * </p>
     * @param model 薪资调整单数据模型
     * @param ctx   BOS上下文
     * @return String 校验结果：null表示校验通过；当前实现不依赖返回值，校验失败直接抛异常
     * @throws SHRWebException 通用薪资web异常
     */
    private String verify(CoreBaseInfo model, Context ctx) throws SHRWebException {
        BatchAdjustBillInfo billInfo = (BatchAdjustBillInfo) model;
        List<String> entryIdList = new ArrayList<>(); // 条目ID列表：关联关系未变更的明细，用于批量数据库校验
        List<String> personIdList = new ArrayList<>(); // 人员ID列表（预留筛选条件）
        Map<String, BatchAdjustBillEntryInfo> relationMap = new HashMap<>(); // key：薪酬任职关系ID；value：明细对象，存放新增/人员变更条目
        StringBuffer errorMsg = new StringBuffer(); // 全局错误信息收集缓冲区
        Map<String, String> newAdjEmpMap = new HashMap<>(); // key：明细ID；value：薪酬任职关系ID，当前界面最新数据

        BatchAdjustBillEntryCollection entryColl = billInfo.getEntry();

        // 1. 构建界面最新明细：明细ID → 薪酬任职关系ID映射
        for (int i = 0; i < entryColl.size(); ++i) {
            BatchAdjustBillEntryInfo entryInfo = entryColl.get(i);
            if (null != entryInfo.getId()) {
                newAdjEmpMap.put(entryInfo.getId().toString(), entryInfo.getAdjEmpORelation().getId().toString());
            }
        }

        // 获取当前会话多语言编码，用于异常信息国际化展示
        String localeCode = com.kingdee.shr.base.syssetting.util.LocaleUtils.getShortCode(SHRContext.getInstance().getContext());
        BatchAdjustBillEntryInfo entryInfo;

        // 单据已有主键=编辑单据场景，需要对比数据库原始旧数据
        if (null != billInfo.getId()) {
            // 2. 获取页面当前携带明细集合
            BatchAdjustBillEntryCollection existingEntryColl = billInfo.getEntry();
            Map<String, String> oldPersonMap = new HashMap<>(); // 数据库旧数据：明细ID → 人员ID
            Map<String, String> oldAdjEmpMap = new HashMap<>(); // 数据库旧数据：明细ID → 薪酬任职关系ID

            try {
                // 查询数据库持久化的原始明细数据
                entryColl = BatchAdjustBillEntryFactory.getLocalInstance(ctx)
                        .getBatchAdjustBillEntryCollection("select id, person.id, adjEmpORelation.id where bill.id = '" + billInfo.getId() + "'");
            } catch (BOSException e) {
                e.printStackTrace();
                throw new RuntimeException("查询已有调整条目失败", e);
            }

            // 组装旧数据映射关系
            for (int i = 0; i < entryColl.size(); ++i) {
                oldPersonMap.put(entryColl.get(i).getId().toString(), entryColl.get(i).getPerson().getId().toString());
                oldAdjEmpMap.put(entryColl.get(i).getId().toString(), entryColl.get(i).getAdjEmpORelation().getId().toString());
            }

            // 3. 区分明细类型：新增条目 / 人员变更条目 / 关系不变条目，分类存放用于后续校验
            int i;
            for (i = 0; i < existingEntryColl.size(); ++i) {
                entryInfo = existingEntryColl.get(i);
                String personId = entryInfo.getPerson().getId().toString();
                String entryId = (entryInfo.getId() == null) ? null : entryInfo.getId().toString();

                if (null == entryId) {
                    // 场景：新增明细条目，放入关联关系校验map
                    relationMap.put(entryInfo.getAdjEmpORelation().getId().toString(), entryInfo);
                } else if (oldPersonMap.containsKey(entryId) && !personId.equals(oldPersonMap.get(entryId))) {
                    // 场景：原有明细更换人员，放入关联关系校验map
                    relationMap.put(entryInfo.getAdjEmpORelation().getId().toString(), entryInfo);
                } else if (oldAdjEmpMap.get(entryId).equals(newAdjEmpMap.get(entryId))) {
                    // 场景：人员、任职关系均未变更，放入批量校验列表
                    entryIdList.add(entryInfo.getId().toString());
                }
            }

            // ==========校验1：校验在职人员状态，过滤离职人员==========
            if (entryIdList.size() > 0) {
                StringBuffer querySql = new StringBuffer();
                querySql.append(" select p.FName_" + localeCode + " personName, pp.FName_" + localeCode + " positionName, en.FEmpState state");
                querySql.append(" from T_BD_Person p ");
                querySql.append(" inner join T_HR_SBatchAdjustBillEntry en on p.fid = en.FPersonId ");
                querySql.append(" inner join T_ORG_Position pp on pp.fid = en.FPositionId ");
                querySql.append(" where en.fid in (" + CmpStrUtil.convertListToString(entryIdList) + ")");
                querySql.append(" and en.FEmpState in (1,2) "); // 1:在职, 2:离职

                try {
                    IRowSet rowSet = CmpSQLUtil.executeQuery(ctx, querySql.toString());
                    while (rowSet.next()) {
                        // 拼接人员状态异常提示信息
                        errorMsg.append(EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label3", SHRContext.getInstance().getContext().getLocale())
                                + rowSet.getString("personName") + "," + rowSet.getString("positionName")
                                + EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label4")
                                + (1 == rowSet.getInt("state") ? EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label5") : EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label6")));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new SHRWebException(errorMsg.toString());
                }
            }

            // ==========校验2：校验明细关联薪酬任职关系是否存在==========
            if (entryIdList.size() > 0) {
                StringBuffer querySql = new StringBuffer();
                querySql.append(" select person.FNUMBER personNumber,person.Fname_l2 personName,adjOR.FID adjORID ");
                querySql.append(" from T_HR_SBatchAdjustBillEntry entry ");
                querySql.append(" INNER JOIN T_BD_Person person ON ENTRY.FPERSONID = PERSON.FID ");
                querySql.append(" LEFT OUTER JOIN T_HR_SAdjustEmpORelation adjOR on entry.FADJEMPORELATIONID = adjOR.FID ");
                querySql.append(" where  entry.fid in (").append(CmpStrUtil.convertListToString(entryIdList)).append(")");
                if (personIdList.size() > 0) {
                    querySql.append(" and  person.fid in (").append(CmpStrUtil.convertListToString(personIdList)).append(")");
                }

                try {
                    IRowSet rowSet = CmpSQLUtil.executeQuery(ctx, querySql.toString());
                    String personName = "";
                    String personNumber = "";
                    String adjORId = "";

                    while (rowSet.next()) {
                        personName = rowSet.getString("personName");
                        personNumber = rowSet.getString("personNumber");
                        adjORId = rowSet.getString("adjORID");
                        if (StringUtils.isEmpty(adjORId)) {
                            // 薪酬任职关系为空，追加错误信息
                            errorMsg.append(EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label3", SHRContext.getInstance().getContext().getLocale())
                                    + personName + "(" + personNumber + ")"
                                    + EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label26", SHRContext.getInstance().getContext().getLocale()));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ShrWebBizException(errorMsg.toString());
                }
            }
        }

        // ==========校验3：新增/人员变更条目对应的薪酬任职关系有效性校验==========
        if (relationMap.size() > 0) {
            StringBuffer querySql = new StringBuffer();
            querySql.append(" select fid from T_HR_SAdjustEmpORelation where fid in (" + CmpStrUtil.converMapToString(relationMap) + ")");

            try {
                IRowSet rowSet = CmpSQLUtil.executeQuery(ctx, querySql.toString());
                while (rowSet.next()) {
                    // 数据库能查询到则代表关系有效，移除map不再校验
                    if (relationMap.containsKey(rowSet.getString(1))) {
                        relationMap.remove(rowSet.getString(1));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new SHRWebException(querySql.toString());
            }

            // map剩余数据：数据库不存在的无效任职关系，拼接错误信息
            Iterator<Map.Entry<String, BatchAdjustBillEntryInfo>> iterator = relationMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, BatchAdjustBillEntryInfo> entry = iterator.next();
                entryInfo = entry.getValue();
                errorMsg.append(EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label3", SHRContext.getInstance().getContext().getLocale())
                        + entryInfo.getPerson().getName() + "," + entryInfo.getPosition().getName()
                        + EASResource.getString("com.kingdee.shr.cmpdesign.resource.CmpdesignMultiLanResource", "label7", SHRContext.getInstance().getContext().getLocale()));
            }
        }

        // 存在错误信息，抛出业务异常
        if (errorMsg.length() > 0) {
            throw new ShrWebBizException(errorMsg.toString());
        } else {
            return null; // 全部校验通过
        }
    }

    /**
     * 将批量薪资调整单明细同步生成【固定薪资调整记录】
     * <p>
     * 业务作用：批量薪资调整单只是前端操作单据，FixAdjustSalaryInfo是系统生效薪资底层存储对象；
     * 单据审核后必须生成该记录，薪资核算模块才能读取最新薪资标准
     * 逻辑流程：
     * 1. 查询当前调整单所有明细；
     * 2. 批量查询人员历史薪资生效日期；
     * 3. 循环明细组装FixAdjustSalaryInfo对象并保存；
     * 4. 执行UPDATE同步薪酬任职关系中的FUseDefault标识
     * </p>
     * @param ctx  BOS上下文对象
     * @param bill 已持久化完成的批量薪资调整单主表实体
     * @throws BOSException    BOS框架异常
     * @throws EASBizException EAS业务异常
     */
    private void submitToFixAdjustSalary(Context ctx, BatchAdjustBillInfo bill)
            throws BOSException, EASBizException {
        // 获取固定薪资调整执行工具实现类
        FixAdjustSalaryExecute execute = new FixAdjustSalaryExecuteCommon();
        FixAdjustSalaryInfo fixInfo = null;

        // 1. 查询当前调整单全部明细数据
        BatchAdjustBillEntryCollection entryColl = BatchAdjustBillEntryFactory.getLocalInstance(ctx)
                .getBatchAdjustBillEntryCollection(" where bill.id = '" + bill.getId().toString() + "'");

        // 初始化默认截止日期：2199-12-31，无截止日期时使用该极大值
        Calendar calendar = Calendar.getInstance();
        calendar.set(2199, 11, 31, 0, 0, 0);

        // 2. 拼接所有人员ID，用于批量查询人员历史生效日期
        StringBuilder personIdStr = new StringBuilder();
        int i = 0;
        for (int size = entryColl.size(); i < size; ++i) {
            personIdStr.append("'").append(entryColl.get(i).getPerson().getId().toString()).append("',");
        }
        String personIds = personIdStr.substring(0, personIdStr.lastIndexOf(",")); // 移除末尾多余逗号

        // 3. 查询人员薪酬任职生效记录，获取历史生效日期
        String oql = "where person.id in (" + personIds + ") and hrOrgUnit = '" + bill.getHrOrgUnit().getId().toString() + "' ";
        AdjustPersonRecordCollection recColl = AdjustPersonRecordFactory.getLocalInstance(ctx).getAdjustPersonRecordCollection(oql);

        // 缓存人员ID与历史生效日期映射，避免循环重复查询数据库
        Map<String, Date> personEffectDateMap = new HashMap<>();
        i = 0;
        for (i = 0; i < recColl.size(); ++i) {
            personEffectDateMap.put(recColl.get(i).getPerson().getId().toString(), recColl.get(i).getLeffectDate());
        }

        // 4. 循环明细，逐条构建并保存固定薪资调整记录
        i = 0;
        for (int size = entryColl.size(); i < size; ++i) {
            fixInfo = new FixAdjustSalaryInfo();
            BatchAdjustBillEntryInfo entryInfo = entryColl.get(i);

            // 固定薪资调整记录核心字段赋值
            fixInfo.setAdjEmpORelation(entryInfo.getAdjEmpORelation()); // 薪酬任职关系
            fixInfo.setAdminOrgUnit(entryInfo.getAdminOrgUnit()); // 行政组织
            fixInfo.setPosition(entryInfo.getPosition()); // 职位
            fixInfo.setAdjHrOrgUnit(bill.getHrOrgUnit()); // HR组织（单据头）
            fixInfo.setAdjustSalaryCause(entryInfo.getAdjustCause()); // 调薪原因
            fixInfo.setCmpItem(entryInfo.getCmpItem()); // 薪资项
            fixInfo.setEffectDay(entryInfo.getEffectDate()); // 调整生效日期（明细上配置日期）
            fixInfo.setLeffectDay(personEffectDateMap.get(entryInfo.getPerson().getId().toString())); // 长期生效日期（默认继承历史）
            fixInfo.setStandard(entryInfo.getCmpStandard()); // 薪酬标准
            fixInfo.setStdLevel(entryInfo.getCmpLevel()); // 薪酬标准等级
            fixInfo.setStdPoint(entryInfo.getStdPoint()); // 标准分值
            fixInfo.setStdScope(entryInfo.getStdScope()); // 标准区间
            fixInfo.setFloatRate(entryInfo.getFloatRate()); // 固浮比例
            fixInfo.setMoney(entryInfo.getMoney()); // 调整金额
            fixInfo.setPerson(entryInfo.getPerson()); // 人员
            fixInfo.setRemark(entryInfo.getRemark()); // 备注
            fixInfo.setBillId(entryInfo.getBill().getId().toString()); // 关联来源批量薪资调整单ID
            fixInfo.setOldFixAdjustSalary(entryInfo.getLastInfo()); // 上一条薪资调整记录
            fixInfo.setPayHrOrgUnit(entryInfo.getPayHrOrgUnit()); // 发薪HR组织
            fixInfo.setCurrency(entryInfo.getCurrency()); // 币别

            // 长期生效日期优先级：明细手动配置日期 > 自动继承历史生效日期
            if (null != entryInfo.getLeffectDate() && entryInfo.getLeffectDate().compareTo(personEffectDateMap.get(entryInfo.getPerson().getId().toString())) < 0) {
                //fixInfo.setLeffectSource(LeffectSourceEnum.MANUAL); // 来源：手动配置 方正微版本无此字段，注释保留备查
                fixInfo.setLeffectDay(entryInfo.getLeffectDate());
            } else if (null == entryInfo.getLeffectDate()) {
                //fixInfo.setLeffectSource(LeffectSourceEnum.AUTO); // 来源：自动继承 方正微版本无此字段，注释保留备查
                fixInfo.setLeffectDay(personEffectDateMap.get(entryInfo.getPerson().getId().toString()));
            }

            // 持久化固定薪资调整记录
            execute.save(ctx, fixInfo);
        }

        // 5. 更新固定薪资调整记录的FUseDefault标识，同步薪酬任职关系配置
        StringBuilder sql = new StringBuilder();
        sql.append(" update T_HR_SFixAdjustSalary tt set FUseDefault = (");
        sql.append(" select FUseDefault ");
        sql.append(" from T_HR_SAdjustEmpORelation t0 ");
        sql.append(" where t0.Fid = tt.FAdjEmpORelationID) ");
        sql.append(" where tt.FBillId = '" + bill.getId().toString() + "'");
        CmpSQLUtil.execute(ctx, sql.toString());
    }
}
