/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package jp.mosp.platform.bean.workflow.impl;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import jp.mosp.framework.base.MospException;
import jp.mosp.framework.base.MospParams;
import jp.mosp.framework.utils.MospUtility;
import jp.mosp.platform.base.PlatformBean;
import jp.mosp.platform.bean.human.ConcurrentReferenceBeanInterface;
import jp.mosp.platform.bean.human.HumanReferenceBeanInterface;
import jp.mosp.platform.bean.human.HumanSearchBeanInterface;
import jp.mosp.platform.bean.system.PositionReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.ApprovalRouteReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.ApprovalRouteUnitReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.ApprovalUnitReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.RouteApplicationReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.SubApproverReferenceBeanInterface;
import jp.mosp.platform.bean.workflow.WorkflowIntegrateBeanInterface;
import jp.mosp.platform.bean.workflow.WorkflowReferenceBeanInterface;
import jp.mosp.platform.comparator.human.HumanPositionGradeComparator;
import jp.mosp.platform.constant.PlatformConst;
import jp.mosp.platform.constant.PlatformMessageConst;
import jp.mosp.platform.dto.human.ConcurrentDtoInterface;
import jp.mosp.platform.dto.human.HumanDtoInterface;
import jp.mosp.platform.dto.system.PositionDtoInterface;
import jp.mosp.platform.dto.workflow.ApprovalRouteDtoInterface;
import jp.mosp.platform.dto.workflow.ApprovalRouteUnitDtoInterface;
import jp.mosp.platform.dto.workflow.ApprovalUnitDtoInterface;
import jp.mosp.platform.dto.workflow.RouteApplicationDtoInterface;
import jp.mosp.platform.dto.workflow.SubApproverDtoInterface;
import jp.mosp.platform.dto.workflow.WorkflowDtoInterface;
import jp.mosp.time.constant.TimeConst;

/**
 * ワークフロー統括クラス。
 */
public class WorkflowIntegrateBean extends PlatformBean implements WorkflowIntegrateBeanInterface {
	
	/**
	 * ワークフロー参照クラス。
	 */
	protected WorkflowReferenceBeanInterface			workflowReference;
	
	/**
	 * 承認ルート適用参照クラス。
	 */
	protected RouteApplicationReferenceBeanInterface	applicationReference;
	
	/**
	 * 承認ルート参照クラス。
	 */
	protected ApprovalRouteReferenceBeanInterface		routeReference;
	
	/**
	 * 承認ルートユニット情報参照クラス。
	 */
	protected ApprovalRouteUnitReferenceBeanInterface	routeUnitReference;
	
	/**
	 * 承認ユニット参照クラス。
	 */
	protected ApprovalUnitReferenceBeanInterface		unitReference;
	
	/**
	 * 人事マスタ検索クラス。
	 */
	protected HumanSearchBeanInterface					humanSearch;
	
	/**
	 * 人事マスタ参照クラス。
	 */
	protected HumanReferenceBeanInterface				humanReference;
	
	/**
	 * 職位マスタ参照クラス。
	 */
	protected PositionReferenceBeanInterface			positionReference;
	
	/**
	 * 代理承認者テーブル参照クラス。
	 */
	protected SubApproverReferenceBeanInterface			subApproverReference;
	

	/**
	 * {@link PlatformBean#PlatformBean()}を実行する。<br>
	 */
	public WorkflowIntegrateBean() {
		super();
	}
	
	/**
	 * {@link PlatformBean#PlatformBean(MospParams, Connection)}を実行する。<br>
	 * @param mospParams MosP処理情報
	 * @param connection DBコネクション
	 */
	public WorkflowIntegrateBean(MospParams mospParams, Connection connection) {
		super(mospParams, connection);
	}
	
	@Override
	public void initBean() throws MospException {
		routeReference = (ApprovalRouteReferenceBeanInterface)createBean(ApprovalRouteReferenceBeanInterface.class);
		applicationReference = (RouteApplicationReferenceBeanInterface)createBean(RouteApplicationReferenceBeanInterface.class);
		routeUnitReference = (ApprovalRouteUnitReferenceBeanInterface)createBean(ApprovalRouteUnitReferenceBeanInterface.class);
		unitReference = (ApprovalUnitReferenceBeanInterface)createBean(ApprovalUnitReferenceBeanInterface.class);
		humanSearch = (HumanSearchBeanInterface)createBean(HumanSearchBeanInterface.class);
		humanReference = (HumanReferenceBeanInterface)createBean(HumanReferenceBeanInterface.class);
		positionReference = (PositionReferenceBeanInterface)createBean(PositionReferenceBeanInterface.class);
		workflowReference = (WorkflowReferenceBeanInterface)createBean(WorkflowReferenceBeanInterface.class);
		subApproverReference = (SubApproverReferenceBeanInterface)createBean(SubApproverReferenceBeanInterface.class);
	}
	
	@Override
	public WorkflowDtoInterface getLatestWorkflowInfo(long workflow) throws MospException {
		return workflowReference.getLatestWorkflowInfo(workflow);
	}
	
	@Override
	public List<List<String[]>> getRouteApproverList(String personalId, Date targetDate, int workflowType)
			throws MospException {
		// ルート承認者リスト準備
		List<List<String[]>> routeApproverList = new ArrayList<List<String[]>>();
		// ルート適用情報取得及び確認
		RouteApplicationDtoInterface routeApplicationDto = findForPerson(personalId, targetDate, workflowType);
		if (routeApplicationDto == null) {
			return routeApproverList;
		}
		// ルートコード取得
		String routeCode = routeApplicationDto.getRouteCode();
		// 自己承認確認
		if (routeCode.equals(PlatformConst.APPROVAL_ROUTE_SELF)) {
			// 承認者リスト(自己承認)作成(自己承認の場合の承認階層は1)
			routeApproverList.add(getSelfApproverList());
			return routeApproverList;
		}
		// ルート情報取得及び確認
		ApprovalRouteDtoInterface routeDto = routeReference.getApprovalRouteInfo(routeCode, targetDate);
		if (routeDto == null) {
			return routeApproverList;
		}
		// 承認ルートユニット取得(承認階層順)
		List<ApprovalRouteUnitDtoInterface> unitList = routeUnitReference.getApprovalRouteUnitList(routeCode,
				targetDate);
		// 職位等級比較クラス準備
		HumanPositionGradeComparator comparator = getPositionGradeComparator(targetDate);
		// ユニット毎に承認者を設定
		for (ApprovalRouteUnitDtoInterface routeUnitDto : unitList) {
			// ユニット承認者リスト取得
			List<HumanDtoInterface> unitApproverList = getUnitApproverList(routeUnitDto.getUnitCode(), targetDate);
			// 職位等級でソート
			Collections.sort(unitApproverList, comparator);
			// ルート承認者リストに追加
			routeApproverList.add(getApproverList(unitApproverList, personalId, targetDate, workflowType));
		}
		return routeApproverList;
	}
	
	/**
	 * 職位比較クラスを取得する。<br>
	 * @param targetDate 対象年月日
	 * @return 職位比較クラス
	 * @throws MospException インスタンスの取得、SQLの作成及び実行に失敗した場合
	 */
	protected HumanPositionGradeComparator getPositionGradeComparator(Date targetDate) throws MospException {
		// 職位マスタマップ取得(比較用のため操作範囲指定は無し)
		Map<String, PositionDtoInterface> positionMap = positionReference.getPositionMap(targetDate, null);
		// 職位優劣取得
		boolean hasLowGradeAdvantage = positionReference.hasLowGradeAdvantage();
		// 職位等級比較クラス準備
		return new HumanPositionGradeComparator(positionMap, hasLowGradeAdvantage);
	}
	
	@Override
	public List<ApprovalRouteUnitDtoInterface> getListForApprover(String personalId, Date targetDate)
			throws MospException {
		Map<Long, ApprovalUnitDtoInterface> unitMap = new HashMap<Long, ApprovalUnitDtoInterface>();
		// 承認者個人ID
		List<ApprovalUnitDtoInterface> unitList = unitReference.findForApproverPersonalId(personalId, targetDate);
		for (ApprovalUnitDtoInterface dto : unitList) {
			unitMap.put(dto.getPfmApprovalUnitId(), dto);
		}
		// 承認者所属コード、承認者職位コード
		HumanDtoInterface humanDto = getHumanInfo(personalId, targetDate);
		// 人事情報確認
		if (humanDto == null) {
			// 人事情報が存在しない場合
			return new ArrayList<ApprovalRouteUnitDtoInterface>();
		}
		unitList = unitReference.findForApproverSection(humanDto.getSectionCode(), humanDto.getPositionCode(),
				targetDate);
		for (ApprovalUnitDtoInterface dto : unitList) {
			unitMap.put(dto.getPfmApprovalUnitId(), dto);
		}
		// 兼務
		ConcurrentReferenceBeanInterface concurrentReference;
		concurrentReference = (ConcurrentReferenceBeanInterface)createBean(ConcurrentReferenceBeanInterface.class);
		List<ConcurrentDtoInterface> concurrentList = concurrentReference.getConcurrentList(personalId, targetDate);
		for (ConcurrentDtoInterface concurrentDto : concurrentList) {
			unitList = unitReference.findForApproverSection(concurrentDto.getSectionCode(), concurrentDto
				.getPositionCode(), targetDate);
			for (ApprovalUnitDtoInterface dto : unitList) {
				unitMap.put(dto.getPfmApprovalUnitId(), dto);
			}
		}
		// MapからListへ変換。
		unitList.clear();
		Set<Entry<Long, ApprovalUnitDtoInterface>> unitSet = unitMap.entrySet();
		for (Entry<Long, ApprovalUnitDtoInterface> entry : unitSet) {
			unitList.add(entry.getValue());
		}
		Map<Long, ApprovalRouteUnitDtoInterface> map = new HashMap<Long, ApprovalRouteUnitDtoInterface>();
		for (ApprovalUnitDtoInterface unitDto : unitList) {
			List<ApprovalRouteUnitDtoInterface> list = routeUnitReference.findForApprovalUnit(unitDto.getUnitCode(),
					targetDate);
			for (ApprovalRouteUnitDtoInterface dto : list) {
				map.put(dto.getPfaApprovalRouteUnitId(), dto);
			}
		}
		// MapからListへ変換。
		List<ApprovalRouteUnitDtoInterface> list = new ArrayList<ApprovalRouteUnitDtoInterface>();
		Set<Entry<Long, ApprovalRouteUnitDtoInterface>> set = map.entrySet();
		for (Entry<Long, ApprovalRouteUnitDtoInterface> entry : set) {
			list.add(entry.getValue());
		}
		return list;
	}
	
	@Override
	public RouteApplicationDtoInterface findForPerson(String personalId, Date targetDate, int workflowType)
			throws MospException {
		return applicationReference.findForPerson(personalId, targetDate, workflowType);
	}
	
	@Override
	public String getWorkflowStatus(String status, int stage) {
		// ワークフロー状態確認
		if (PlatformConst.CODE_STATUS_DRAFT.equals(status)) {
			// 下書
			return getNameDraft();
		}
		if (PlatformConst.CODE_STATUS_APPLY.equals(status)) {
			// 未承認
			return getNameNotApproved();
		}
		if (PlatformConst.CODE_STATUS_CANCEL.equals(status)) {
			// 承解除
			return getNameRelease();
		}
		if (PlatformConst.CODE_STATUS_WITHDRAWN.equals(status)) {
			// 取下
			return getNameWithdraw();
		}
		if (PlatformConst.CODE_STATUS_COMPLETE.equals(status)) {
			// 承認済
			return getNameFinish();
		}
		if (PlatformConst.CODE_STATUS_APPROVED.equals(status)) {
			// n次済
			return getNameStageApprove(stage - 1);
		}
		if (PlatformConst.CODE_STATUS_REVERT.equals(status)) {
			// n次戻
			return getNameStageRemand(stage + 1);
		}
		return status;
	}
	
	@Override
	public String getWorkflowOperation(String status) {
		// ワークフロー状態確認
		if (PlatformConst.CODE_STATUS_DRAFT.equals(status)) {
			// 下書
			return getNameDraft();
		} else if (status.equals(PlatformConst.CODE_STATUS_APPLY)) {
			// 申請
			return getNameApply();
		} else if (status.equals(PlatformConst.CODE_STATUS_APPROVED)) {
			// 承認
			return getNameApprove();
		} else if (status.equals(PlatformConst.CODE_STATUS_REVERT)) {
			// 差戻
			return getNameRemand();
		} else if (status.equals(PlatformConst.CODE_STATUS_CANCEL)) {
			// 承解除(取消)
			return getNameRelease();
		} else if (status.equals(PlatformConst.CODE_STATUS_WITHDRAWN)) {
			// 取下
			return getNameWithdraw();
		} else if (status.equals(PlatformConst.CODE_STATUS_COMPLETE)) {
			// 承認(最終承認)
			return getNameApprove();
		}
		return status;
	}
	
	@Override
	public boolean isApprover(WorkflowDtoInterface dto, String personalId) throws MospException {
		// ワークフロー段階確認
		if (dto.getWorkflowStage() == PlatformConst.WORKFLOW_STAGE_ZERO) {
			// 操作者が申請者の場合
			return false;
		}
		// ワークフロー状況確認
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_DRAFT)
				|| dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_WITHDRAWN)) {
			// 下書か取下の場合
			return false;
		}
		// 操作権を持つ段階を取得
		List<Integer> approverStageList = getApproverStageList(dto, personalId);
		// ワークフロー段階確認
		if (approverStageList.contains(dto.getWorkflowStage())) {
			return true;
		}
		return false;
	}
	
	/**
	 * 対象ワークフローにおいて、対象個人IDが承認者の一人であるかどうかを確認する。<br>
	 * ここでは、代理は考慮しない。<br>
	 * @param dto 対象ワークフロー情報
	 * @param personalId 対象個人ID
	 * @return 確認結果(true：承認者の一人である、false：承認者の一人ではない)
	 * @throws MospException インスタンスの取得、SQLの作成及び実行に失敗した場合
	 */
	protected boolean isOneOfApprover(WorkflowDtoInterface dto, String personalId) throws MospException {
		// 自己承認確認
		if (dto.getApproverId().equals(PlatformConst.APPROVAL_ROUTE_SELF)
				|| dto.getRouteCode().equals(PlatformConst.APPROVAL_ROUTE_SELF)) {
			// 申請者確認
			if (dto.getPersonalId().equals(personalId)) {
				return true;
			}
		}
		// 承認者個人ID確認
		if (dto.getApproverId().isEmpty() == false) {
			// 承認者個人ID配列取得
			String[] approverArray = split(dto.getApproverId(), SEPARATOR_DATA);
			// 対象個人ID確認
			for (String element : approverArray) {
				if (element.equals(personalId)) {
					// 対象個人IDが承認者個人IDに含まれる場合
					return true;
				}
			}
			return false;
		}
		// ルートユニット情報取得
		List<ApprovalRouteUnitDtoInterface> routeUnitList = routeUnitReference.getApprovalRouteUnitList(dto
			.getRouteCode(), dto.getWorkflowDate());
		// ルートユニット毎に確認
		for (ApprovalRouteUnitDtoInterface routeUnitDto : routeUnitList) {
			// ユニット承認者リスト取得
			List<HumanDtoInterface> humanList = getUnitApproverList(routeUnitDto.getUnitCode(), dto.getWorkflowDate());
			// ユニット承認者毎に処理
			for (HumanDtoInterface humanDto : humanList) {
				// 対象個人ID確認
				if (personalId.equals(humanDto.getPersonalId())) {
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 対象ワークフローにおいて、対象個人IDが操作権を持つ段階のリスト取得する。<br>
	 * 操作権を持たない場合は0を返す。<br>
	 * @param dto        対象ワークフロー情報
	 * @param personalId 対象個人ID
	 * @return 操作権を持つ段階のリスト
	 * @throws MospException インスタンスの取得、SQLの作成及び実行に失敗した場合
	 */
	protected List<Integer> getApproverStageList(WorkflowDtoInterface dto, String personalId) throws MospException {
		// 操作権を持つ段階のリスト準備
		List<Integer> stageList = new ArrayList<Integer>();
		// 承認者個人ID確認
		if (dto.getApproverId().isEmpty() == false) {
			// 承認者個人ID配列取得
			String[] approverArray = split(dto.getApproverId(), SEPARATOR_DATA);
			// 対象個人ID確認
			for (int i = 0; i < approverArray.length; i++) {
				if (approverArray[i].equals(personalId)) {
					// 対象個人IDが承認者個人IDに含まれる場合
					stageList.add(++i);
				}
			}
			return stageList;
		}
		// ルートユニット情報取得
		List<ApprovalRouteUnitDtoInterface> routeUnitList = routeUnitReference.getApprovalRouteUnitList(dto
			.getRouteCode(), dto.getWorkflowDate());
		// ルートユニット毎に確認
		for (ApprovalRouteUnitDtoInterface routeUnitDto : routeUnitList) {
			// ユニット承認者リスト取得
			List<HumanDtoInterface> humanList = getUnitApproverList(routeUnitDto.getUnitCode(), dto.getWorkflowDate());
			// ユニット承認者毎に処理
			for (HumanDtoInterface humanDto : humanList) {
				// 対象個人ID確認
				if (personalId.equals(humanDto.getPersonalId())) {
					// 操作権を持つ段階のリストに追加
					stageList.add(routeUnitDto.getApprovalStage());
					break;
				}
			}
		}
		return stageList;
	}
	
	/**
	 * 対象日におけるユニットに設定されている承認者を取得する。<br>
	 * 但し、代理承認者は含まれない。<br>
	 * @param unitCode   ユニットコード
	 * @param targetDate 対象日
	 * @return ユニット承認者リスト
	 * @throws MospException インスタンスの取得、SQLの作成及び実行に失敗した場合
	 */
	protected List<HumanDtoInterface> getUnitApproverList(String unitCode, Date targetDate) throws MospException {
		// ユニット承認者リスト準備
		List<HumanDtoInterface> approverList = new ArrayList<HumanDtoInterface>();
		// ユニット情報取得
		ApprovalUnitDtoInterface unitDto = unitReference.getApprovalUnitInfo(unitCode, targetDate);
		// ユニット区分が個人IDの場合
		if (PlatformConst.UNIT_TYPE_PERSON.equals(unitDto.getUnitType())) {
			// 個人ID配列取得
			String[] approverIdArray = split(unitDto.getApproverPersonalId(), SEPARATOR_DATA);
			// 各個人IDから人事情報を取得してユニット承認者リストに設定
			for (String approverId : approverIdArray) {
				// 承認者人事情報取得及び確認
				HumanDtoInterface approverDto = humanReference.getHumanInfo(approverId, targetDate);
				if (approverDto == null) {
					continue;
				}
				// ユニット承認者リストに追加
				approverList.add(approverDto);
			}
		}
		// ユニット区分が所属の場合
		if (PlatformConst.UNIT_TYPE_SECTION.equals(unitDto.getUnitType())) {
			// 検索条件設定(対象日)
			humanSearch.setTargetDate(targetDate);
			// 検索条件設定(所属コード)
			humanSearch.setSectionCode(unitDto.getApproverSectionCode());
			// 検索条件設定(職位コード)
			humanSearch.setPositionCode(unitDto.getApproverPositionCode());
			// 検索条件設定(兼務要否)
			humanSearch.setNeedConcurrent(true);
			// 検索条件設定(休退職区分)
			humanSearch.setStateType(PlatformConst.EMPLOYEE_STATE_PRESENCE);
			// 承認者検索
			approverList = humanSearch.search();
		}
		// 承認者リスト確認
		if (approverList.isEmpty()) {
			// エラーメッセージ設定
			addUnitNotHaveApproverMessage(unitDto.getUnitCode(), targetDate);
		}
		return approverList;
	}
	
	/**
	 * 承認者リストを作成する。<br>
	 * 代理承認者はリストには含まれない。<br>
	 * 取得されるリストには、{@link #getApproverOption(HumanDtoInterface)}
	 * で取得した配列が含まれる。<br>
	 * @param list         承認者リスト
	 * @param personalId   申請者個人ID
	 * @param targetDate   対象年月日
	 * @param workflowType フロー区分
	 * @return 承認者リスト(代理含む)
	 * @throws MospException インスタンスの取得、SQLの作成及び実行に失敗した場合
	 */
	protected List<String[]> getApproverList(List<HumanDtoInterface> list, String personalId, Date targetDate,
			int workflowType) throws MospException {
		// 承認者リスト準備
		List<String[]> approverList = new ArrayList<String[]>();
		// 承認者リストから申請用承認者情報を作成
		for (HumanDtoInterface approver : list) {
			// 申請用承認者情報配列作成用リストに追加
			approverList.add(getApproverOption(approver));
		}
		return approverList;
	}
	
	/**
	 * 承認者リスト(自己承認)を作成する。<br>
	 * 取得されるリストには、以下内容の配列のみが含まれる。<br>
	 * <ul><li>
	 * 1列目：{@link PlatformConst#APPROVAL_ROUTE_SELF}
	 * </li><li>
	 * 2列目：自己承認
	 * </li></ul>
	 * @return 承認者リスト(自己承認)
	 */
	protected List<String[]> getSelfApproverList() {
		// 承認者リスト準備
		List<String[]> approverList = new ArrayList<String[]>();
		// 自己承認設定
		approverList.add(new String[]{ PlatformConst.APPROVAL_ROUTE_SELF, mospParams.getName("SelfApproval") });
		// 申請用承認者情報作成
		return approverList;
	}
	
	/**
	 * 申請用承認者情報を作成する。<br>
	 * 取得される配列の内容は以下の通り。<br>
	 * <ul><li>
	 * 1列目：個人ID
	 * </li><li>
	 * 2列目：社員コード+社員名
	 * </li></ul>
	 * @param dto 承認者社員情報
	 * @return 申請用承認者情報
	 */
	protected String[] getApproverOption(HumanDtoInterface dto) {
		// 社員名準備
		String humanName = MospUtility.getHumansName(dto.getFirstName(), dto.getLastName());
		// 申請用承認者情報作成
		return new String[]{ dto.getPersonalId(),
			getCodedName(dto.getEmployeeCode(), humanName, dto.getEmployeeCode().length()) };
	}
	
	/**
	 * ユニットに承認者が存在しない場合のエラーメッセージを追加する。
	 * @param unitCode   ユニットコード
	 * @param targetDate 対象日
	 */
	protected void addUnitNotHaveApproverMessage(String unitCode, Date targetDate) {
		String[] aryMeassage = { unitCode, getStringDate(targetDate) };
		mospParams.addErrorMessage(PlatformMessageConst.MSG_UNIT_NOT_HAVE_APPROVER, aryMeassage);
	}
	
	/**
	 * 下書名称を取得する。<br>
	 * @return 下書名称
	 */
	protected String getNameDraft() {
		return mospParams.getName("WorkPaper");
	}
	
	/**
	 * 申請名称を取得する。<br>
	 * @return 申請名称
	 */
	protected String getNameApply() {
		return mospParams.getName("Application");
	}
	
	/**
	 * 承認名称を取得する。<br>
	 * @return 承認名称
	 */
	protected String getNameApprove() {
		return mospParams.getName("Approval");
	}
	
	/**
	 * 差戻名称を取得する。<br>
	 * @return 差戻名称
	 */
	protected String getNameRemand() {
		return mospParams.getName("SendingBack");
	}
	
	/**
	 * 取下名称を取得する。<br>
	 * @return 取下名称
	 */
	protected String getNameWithdraw() {
		return mospParams.getName("TakeDown");
	}
	
	/**
	 * 承認解除名称を取得する。<br>
	 * @return 承認解除名称
	 */
	protected String getNameRelease() {
		return mospParams.getName("ApprovalRelease");
	}
	
	/**
	 * 未承認名称を取得する。<br>
	 * @return 未承認名称
	 */
	protected String getNameNotApproved() {
		return mospParams.getName("Ram") + mospParams.getName("Approval");
	}
	
	/**
	 * n次済名称を取得する。<br>
	 * @param stage 段階
	 * @return n次済名称
	 */
	protected String getNameStageApprove(int stage) {
		return stage + mospParams.getName("Following") + mospParams.getName("Finish");
	}
	
	/**
	 * n次戻名称を取得する。<br>
	 * @param stage 段階
	 * @return n次戻名称
	 */
	protected String getNameStageRemand(int stage) {
		return stage + mospParams.getName("Following") + mospParams.getName("Back");
	}
	
	/**
	 * 承認済名称を取得する。<br>
	 * @return 承認済名称
	 */
	protected String getNameFinish() {
		return mospParams.getName("Approval") + mospParams.getName("Finish");
	}
	
	/**
	 * 承認可能ワークフロー情報リストを取得する。<br>
	 * 対象個人IDが承認可能なものを抽出する。<br>
	 * 但し、ログインユーザが特権ロールを持つ場合、全ての承認可能なワークフローを返す。<br>
	 * 対象機能コードセットに含まれる機能コードのワークフローのみを取得する。<br>
	 * @param personalId      対象個人ID
	 * @param functionCodeSet 対象機能コードセット
	 * @return 承認可能ワークフロー情報リスト
	 * @throws MospException インスタンスの取得或いはSQL実行に失敗した場合
	 */
	public List<WorkflowDtoInterface> getApprovableList(String personalId, Set<String> functionCodeSet)
			throws MospException {
		// 承認可能ワークフロー情報リスト取得
		List<WorkflowDtoInterface> approvableList = workflowReference.getApprovableList(functionCodeSet);
		// 特権ロール確認
		if (mospParams.getUserRole().isSuper()) {
			// 全ての承認可能ワークフロー情報を取得
			return approvableList;
		}
		// 対象個人IDが承認可能なワークフロー情報リストを準備
		List<WorkflowDtoInterface> list = new ArrayList<WorkflowDtoInterface>();
		// ワークフロー情報毎に処理
		for (WorkflowDtoInterface dto : approvableList) {
			// 承認者確認
			if (isApprover(dto, personalId)) {
				// リストにワークフロー情報を追加
				list.add(dto);
			}
		}
		return list;
	}
	
	@Override
	public Map<String, Map<Long, WorkflowDtoInterface>> getApprovableMap(String personalId, Set<String> functionCodeSet)
			throws MospException {
		// 承認可能ワークフロー情報マップ準備
		Map<String, Map<Long, WorkflowDtoInterface>> approvableMap = new HashMap<String, Map<Long, WorkflowDtoInterface>>();
		// 機能コード毎にリストを作成し承認可能ワークフロー情報情報マップに追加
		for (String function : functionCodeSet) {
			approvableMap.put(function, new HashMap<Long, WorkflowDtoInterface>());
		}
		// 承認可能ワークフロー情報リスト取得
		List<WorkflowDtoInterface> approvableList = getApprovableList(personalId, functionCodeSet);
		// 承認可能ワークフロー毎に処理
		for (WorkflowDtoInterface dto : approvableList) {
			// 承認可能ワークフロー情報マップに設定
			approvableMap.get(dto.getFunctionCode()).put(dto.getWorkflow(), dto);
		}
		return approvableMap;
	}
	
	@Override
	public Map<String, Map<Long, WorkflowDtoInterface>> getSubApprovableMap(String personalId,
			Set<String> functionCodeSet, int workflowType, Map<String, Map<Long, WorkflowDtoInterface>> approvableMap)
			throws MospException {
		// 代理承認可能ワークフロー情報マップ準備
		Map<String, Map<Long, WorkflowDtoInterface>> subAapprovableMap = new HashMap<String, Map<Long, WorkflowDtoInterface>>();
		// 機能コード毎にリストを作成し代理承認可能ワークフロー情報情報マップに追加
		for (String function : functionCodeSet) {
			subAapprovableMap.put(function, new HashMap<Long, WorkflowDtoInterface>());
		}
		// 特権ロール確認
		if (mospParams.getUserRole().isSuper()) {
			// 空の代理承認可能ワークフロー情報マップを取得(マップ特権ロールは全ての申請を操作できるため)
			return subAapprovableMap;
		}
		// システム日付における代理情報を取得
		List<SubApproverDtoInterface> subApproverList = subApproverReference.findForSubApproverId(personalId,
				workflowType, getSystemDate(), getSystemDate());
		// 代理情報毎に処理
		for (SubApproverDtoInterface subApproverDto : subApproverList) {
			// 代理承認可能ワークフロー情報リスト取得
			List<WorkflowDtoInterface> subAapprovableList = getApprovableList(subApproverDto.getPersonalId(),
					functionCodeSet);
			// 代理承認可能ワークフロー毎に処理
			for (WorkflowDtoInterface dto : subAapprovableList) {
				// ワークフロー重複確認
				if (approvableMap.get(dto.getFunctionCode()).containsKey(dto.getWorkflow())) {
					// 設定不要(対象ワークフローが承認可能ワークフロー情報マップに存在する場合)
					continue;
				}
				// 代理承認可能ワークフロー情報マップに設定
				subAapprovableMap.get(dto.getFunctionCode()).put(dto.getWorkflow(), dto);
			}
		}
		return subAapprovableMap;
	}
	
	@Override
	public List<WorkflowDtoInterface> getEffectiveList(String personalId, Date fromDate, Date toDate,
			Set<String> functionCodeSet, String state, Set<String> personalIdSet, Set<String> subordinateIdSet)
			throws MospException {
		// 有効ワークフロー情報リスト準備
		List<WorkflowDtoInterface> effectiveList;
		// ワークフロー状態確認
		if (state.equals(TimeConst.CODE_SEARCH_STATUS_APPLY)) {
			// 未承認ワークフロー情報取得
			effectiveList = workflowReference.getNonApprovedList(fromDate, toDate, functionCodeSet);
		} else if (state.equals(TimeConst.CODE_SEARCH_STATUS_COMPLETE)) {
			// 承認済ワークフロー情報取得
			effectiveList = workflowReference.getCompletedList(fromDate, toDate, functionCodeSet);
		} else if (state.equals(TimeConst.CODE_SEARCH_STATUS_REVERT)) {
			// 差戻ワークフロー情報取得
			effectiveList = workflowReference.getRevertedList(fromDate, toDate, functionCodeSet);
		} else {
			// 有効ワークフロー情報取得
			effectiveList = workflowReference.getEffectiveList(fromDate, toDate, functionCodeSet);
		}
		// 検索条件絞込用ワークフロー情報リストを準備
		List<WorkflowDtoInterface> searchList = new ArrayList<WorkflowDtoInterface>();
		// ワークフロー情報毎に処理
		for (WorkflowDtoInterface dto : effectiveList) {
			// 検索条件による絞込
			if (personalIdSet == null || personalIdSet.contains(dto.getPersonalId())) {
				// 検索条件絞込用ワークフロー情報リストに追加
				searchList.add(dto);
			}
		}
		// 計算ロール確認
		if (mospParams.getUserRole().isCalculator()) {
			// 全ての有効ワークフロー情報を取得
			return searchList;
		}
		// 対象個人IDが承認可能なワークフロー情報リストを準備
		List<WorkflowDtoInterface> list = new ArrayList<WorkflowDtoInterface>();
		// ワークフロー情報毎に処理
		for (WorkflowDtoInterface dto : searchList) {
			// 部下確認
			if (subordinateIdSet.contains(dto.getPersonalId())) {
				// リストにワークフロー情報を追加
				list.add(dto);
				continue;
			}
			// 承認者確認
			if (isOneOfApprover(dto, personalId)) {
				// リストにワークフロー情報を追加
				list.add(dto);
			}
		}
		return list;
	}
	
	@Override
	public boolean isApprovable(WorkflowDtoInterface dto) {
		// ワークフロー状況確認(未承認)
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_APPLY)) {
			return true;
		}
		// ワークフロー状況確認(承認)
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_APPROVED)) {
			return true;
		}
		// ワークフロー状況確認(承認解除)
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_CANCEL)) {
			return true;
		}
		// ワークフロー状況確認(差戻で、ワークフロー段階が0でない)
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_REVERT)
				&& dto.getWorkflowStage() != PlatformConst.WORKFLOW_STAGE_ZERO) {
			return true;
		}
		return false;
	}
	
	@Override
	public boolean isCompleted(long workflow) throws MospException {
		// ワークフロー情報を取得し承認済を確認
		return isCompleted(workflowReference.getLatestWorkflowInfo(workflow));
	}
	
	@Override
	public boolean isCompleted(WorkflowDtoInterface dto) {
		// ワークフロー状況確認
		if (dto.getWorkflowStatus().equals(PlatformConst.CODE_STATUS_COMPLETE)) {
			return true;
		}
		return false;
	}
	
}
