/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*    システム名        ：eo顧客基幹システム
*    モジュール名    ：JFUOptPackAplyChkIfCC
*    ソースファイル名：JFUOptPackAplyChkIfCC.java
*    作成者            ：FJ
*    日付            ：2020年06月05日
*＜機能概要＞
*    電話オプションサービスお得判定IF(FUIFE170)の共通コンポーネント
*＜修正履歴＞
*    バージョン      修正日      修正者      修正内容
*    v50.00.00       2020/06/05  FJ)西川     ANK-3754-00-00_トビラフォン対応
*    v50.00.01       2020/10/10  FJ)西川     OM-2020-0001346_サービス契約内訳データ特定不具合
**********************************************************************/
package com.fujitsu.futurity.bp.custom.common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.fujitsu.futurity.bp.x21.bpm.ServiceComponentRequestInvoker;
import com.fujitsu.futurity.bp.x21.bpm.db.SessionHandle;
import com.fujitsu.futurity.bp.x21.bpm.exception.RequestParameterException;
import com.fujitsu.futurity.bp.x21.bpm.parameter.IRequestParameterReadWrite;
import com.fujitsu.futurity.bp.x21.cc.exception.SCCallException;
import com.fujitsu.futurity.common.JCMConstants;
import com.fujitsu.futurity.common.JSYLogBase;
import com.fujitsu.futurity.common.x01.sc.SCControlMapKeys;
import com.fujitsu.futurity.mapping.bp.common.TemplateErrorUtil;
import com.fujitsu.futurity.model.base.CAANMsg;
import com.fujitsu.futurity.model.common.JCMAPLConstMgr;
import com.fujitsu.futurity.model.common.check.HalfCharCheck;
import com.fujitsu.futurity.model.ejb.common.JSYejbLog;

import eo.common.constant.JFUStrConst;
import eo.common.constant.JPCModelConstant;
import eo.ejb.cbs.cbsmsg.EKK0081A010CBSMsg;
import eo.ejb.cbs.cbsmsg.EKK0081A010CBSMsg1List;
import eo.ejb.cbs.cbsmsg.EKK0191B002CBSMsg;
import eo.ejb.cbs.cbsmsg.EKK0191B002CBSMsg1List;
import eo.ejb.cbs.cbsmsg.EKK0371B001CBSMsg;
import eo.ejb.cbs.cbsmsg.EKK0371B001CBSMsg1List;
import eo.ejb.cbs.cbsmsg.EKK0401B001CBSMsg;
import eo.ejb.cbs.cbsmsg.EKK0401B001CBSMsg1List;

/**
 * 電話オプションサービスお得判定IF(FUIFE170)の共通コンポーネントクラスです。
 * <br>
 * @author 富士通
 *
 */
public class JFUOptPackAplyChkIfCC extends JFUOptPackAplyChkCC
{

	/** テンプレートID(EKK0081A010) サービス契約一意照会(機能コード：2) */
	private static final String TEMPLATE_ID_EKK0081A010 = "EKK0081A010";

	/** テンプレートID(EKK0191B002) 電話番号情報履歴一覧照会(機能コード：2) */
	private static final String TEMPLATE_ID_EKK0191B002 = "EKK0191B002";

	/** テンプレートID(EKK0371B001) オプションサービス契約＜電話＞一覧照会（サービス契約内訳番号）(機能コード：1) */
	private static final String TEMPLATE_ID_EKK0371B001 = "EKK0371B001";

	/** テンプレートID(EKK0401B001) サブオプションサービス契約一覧照会(機能コード：1) */
	private static final String TEMPLATE_ID_EKK0401B001 = "EKK0401B001";

	/** リクエストパラメータキー 機能コード */
	private static final String REQ_KEY_FUNC_CD = "func_cd";

	/** リクエストパラメータキー SYSID */
	private static final String REQ_KEY_SYSID = "sysid";

	/** リクエストパラメータキー サービス契約番号 */
	private static final String REQ_KEY_SVC_KEI_NO = "svc_kei_no";

	/** リクエストパラメータキー eo光電話番号 */
	private static final String REQ_KEY_TELNO = "telno";

	/** リクエストパラメータキー オプションサービスリスト */
	private static final String REQ_KEY_OPTION_SERVICE_LIST = "option_service_list";

	/** リクエストパラメータキー オプション／サブオプションサービスコード */
	private static final String REQ_KEY_OP_SBOP_SVC_CD = "op_sbop_svc_cd";

	/** リクエストパラメータキー 申込区分 */
	private static final String REQ_KEY_APP_DIV = "app_div";

	/** レスポンスキー 戻りコード */
	private static final String RES_KEY_RETURN_CD = "return_cd";

	/** レスポンスキー パック判定区分 */
	private static final String RES_KEY_PACK_JUDGE_DIV = "pack_judge_div";

	/** レスポンスキー 表示文言（お客さま向け） */
	private static final String RES_KEY_DSP_MSG_CONSUMER = "dsp_msg_consumer";

	/** レスポンスキー 表示文言（オペレータ向け） */
	private static final String RES_KEY_DSP_MSG_OPERATOR = "dsp_msg_operator";

	/** レスポンスキー 単品オプションサービスリスト */
	private static final String RES_KEY_SINGLE_OPTION_SERVICE_LIST = "single_option_service_list";

	/** CCパラメータ：エラー情報 */
	private static final String ERROR_INFO = "ERROR_INFO";

	/** CCパラメータ：エラーコード */
	private static final String ERROR_CODE = "errCode";

	/** CCパラメータ：エラーメッセージ */
	private static final String ERROR_MESSAGE = "errMessage";

	/** レスポンス値 戻りコード果 000：正常終了 */
	private static final String RES_RETURN_CD_RESULT_000 = "000";

	/** レスポンス値 戻りコード 999：業務エラー */
	private static final String RES_RETURN_CD_RESULT_999 = "999";

	/** お得判定部品 IN項目 オプションサービスリスト */
	private static final String IN_OTK_JUDGE_OP_SVC_CD_LIST = "judge_op_svc_cd_list";
	/** お得判定部品 IN項目 オプション／サブオプションサービスコード */
	private static final String IN_OTK_JUDGE_OP_SBOP_SVC_CD = "op_sbop_svc_cd";
	/** お得判定部品 IN項目 基準年月日 */
	private static final String IN_OTK_BASE_DATE = "base_date";

	/** お得判定部品 OUT項目：置換型割引適用判定パターン区分 */
	private static final String OUT_OTK_RTWAJG_PTN_DIV = "rtwajg_ptn_div";
	/** お得判定部品 OUT項目：コンシューマ用表示文言 */
	private static final String OUT_OTK_CSM_YO_DSP_MNGN = "csm_yo_dsp_mngn";
	/** お得判定部品 OUT項目：オペレータ用表示文言 */
	private static final String OUT_OTK_OPRT_YO_DSP_MNGN = "oprt_yo_dsp_mngn";
	/** お得判定部品 OUT項目：単品オプションサービスリスト */
	private static final String OUT_OTK_SINGLE_OPTION_SERVICE_LIST = "single_option_service_list";
	/** お得判定部品 OUT項目：戻りコード */
	private static final String OUT_OTK_RETURN_CD = "return_cd";

	/** お得判定部品 OUT項目：戻りコード：正常終了 */
	private static final String OUT_OTK_RETURN_CD_000 = "000";
	/** お得判定部品 OUT項目：戻りコード：オプション組み合わせ不正(判定条件値組合せ無効の場合) */
	private static final String OUT_OTK_RETURN_CD_100 = "100";

	/** APIエラーコード_1001：必須チェックエラー */
	private static final String RES_KEY_ERROR_CODE_1001 = "1001";
	/** APIエラーコード_1002：属性チェックエラー */
	private static final String RES_KEY_ERROR_CODE_1002 = "1002";
	/** APIエラーコード_1003：桁数チェックエラー */
	private static final String RES_KEY_ERROR_CODE_1003 = "1003";

	/** APIエラーコード_2001（重複チェックエラー） */
	private static final String RES_KEY_ERROR_CODE_2001 = "2001";
	/** APIエラーコード_2002（電話番号存在チェックエラー） */
	private static final String RES_KEY_ERROR_CODE_2002 = "2002";
	/** APIエラーコード_2003（電話番号ステータスチェックエラー） */
	private static final String RES_KEY_ERROR_CODE_2003 = "2003";

	/** APIリターンコード_0000 正常終了 */
	private static final String RETURN_CD_0000 = "0000";
	/** APIリターンコード_9000_システムエラー */
	private static final String RETURN_CD_9000 = "9000";
	/** リターンメッセージ_システムエラー */
	public static final String RETURN_MESSAGE_SYSTEM_ERROR = "システムエラー";

	/** 半角コロン */
	private static final String COLON = ":";
	/** カンマ */
	public static final String COMMA = ",";

	/**
	 * 電話オプションサービスお得判定IFを行います。
	 * <br>
	 * @param handle セッションハンドル
	 * @param param リクエストパラメータ
	 * @param fixedText ユーザ定義文字列
	 * @return リクエストパラメータ
	 * @throws Throwable 例外が発生した場合
	 */
	public IRequestParameterReadWrite execute(SessionHandle handle, IRequestParameterReadWrite param, String fixedText) throws Throwable
	{

		// 電話オプションサービスお得判定IFCCマップ
		Map<String, Object> ccMsg = (Map<String, Object>)param.getData(fixedText);

		// SC呼び出し部品のインスタンス生成（引数にはログに出力するクラス名を渡す。空文字を設定した場合はログに出力されない）
		ServiceComponentRequestInvoker scCall = new ServiceComponentRequestInvoker();

		HashMap resultMap = new HashMap();
		resultMap.putAll(ccMsg);

		try
		{
			// ===========================================================================
			// 返却値の初期値を設定する。
			// ===========================================================================
			// 戻りコード	return_cd
			resultMap.put(RES_KEY_RETURN_CD, RES_RETURN_CD_RESULT_999);
			// パック判定区分	pack_judge_div
			resultMap.put(RES_KEY_PACK_JUDGE_DIV, JFUStrConst.EMPTY);
			// 表示文言（お客さま向け）	dsp_msg_consumer
			resultMap.put(RES_KEY_DSP_MSG_CONSUMER, JFUStrConst.EMPTY);
			// 表示文言（オペレータ向け）	dsp_msg_operator
			resultMap.put(RES_KEY_DSP_MSG_OPERATOR, JFUStrConst.EMPTY);

			// ===========================================================================
			// 単項目チェック
			// ===========================================================================
			// パラメータチェック（必須、属性、桁数）
			// ・いずれかの項目でエラーが発生した場合、戻りコードを「"999"：業務エラー」で返却する。

			// エラーリスト
			List<Map<String, String>> errorList = new ArrayList<Map<String, String>>();

			// ●機能コード
			//「0：新規申込、または電話サービス追加、番号追加」（eo光電話未契約）
			//「1：変更申込」（eo光電話契約中）
			String reqFuncCode = (String)ccMsg.get(REQ_KEY_FUNC_CD);

			if (JFUBPCommon.isNull(reqFuncCode))
			{
				errorList = setGyomuErrorList(errorList, REQ_KEY_FUNC_CD, reqFuncCode, RES_KEY_ERROR_CODE_1001);
			}
			else if (reqFuncCode.length() != 1)
			{
				errorList = setGyomuErrorList(errorList, REQ_KEY_FUNC_CD, reqFuncCode, RES_KEY_ERROR_CODE_1003);
			}
			else if (!("0".equals(reqFuncCode) || JPCModelConstant.FUNC_CD_1.equals(reqFuncCode)))
			{
				//「0」または「1」以外の場合はエラー
				errorList = setGyomuErrorList(errorList, REQ_KEY_FUNC_CD, reqFuncCode, RES_KEY_ERROR_CODE_1002);
			}

			// ===========================================================================
			// 機能コードを判定する。
			//「0：新規申込、または電話サービス追加、番号追加」（eo光電話未契約）
			//「1：変更申込」（eo光電話契約中）
			// ===========================================================================
			// 機能コードが「1：変更申込」の場合に「true」
			boolean isChangeOffer = JPCModelConstant.FUNC_CD_1.equals(ccMsg.get(REQ_KEY_FUNC_CD));
			// SYSID
			String reqSysid = JFUStrConst.EMPTY;
			// サービス契約番号
			String reqSvcKeiNo = JFUStrConst.EMPTY;
			// eo光電話番号
			String reqTelno = JFUStrConst.EMPTY;

			if (isChangeOffer)
			{
				//「1：変更申込」（eo光電話契約中）の場合、以下は必須とする。

				// ●SYSID
				reqSysid = (String)ccMsg.get(REQ_KEY_SYSID);

				if (JFUBPCommon.isNull(reqSysid))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SYSID, reqSysid, RES_KEY_ERROR_CODE_1001);
				}
				else if (reqSysid.length() != 10)
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SYSID, reqSysid, RES_KEY_ERROR_CODE_1003);
				}
				else if (!HalfCharCheck.isEnNumber1Check(reqSysid))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SYSID, reqSysid, RES_KEY_ERROR_CODE_1002);
				}

				// ●サービス契約番号
				reqSvcKeiNo = (String)ccMsg.get(REQ_KEY_SVC_KEI_NO);

				if (JFUBPCommon.isNull(reqSvcKeiNo))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_1001);
				}
				else if (reqSvcKeiNo.length() != 10)
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_1003);
				}
				else if (!HalfCharCheck.isEnNumber1Check(reqSvcKeiNo))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_1002);
				}

				// ●eo光電話番号
				reqTelno = (String)ccMsg.get(REQ_KEY_TELNO);

				if (JFUBPCommon.isNull(reqTelno))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_TELNO, reqTelno, RES_KEY_ERROR_CODE_1001);
				}
				else if (!(reqTelno.length() == 10 || reqTelno.length() == 11))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_TELNO, reqTelno, RES_KEY_ERROR_CODE_1003);
				}
				else if (!HalfCharCheck.isTelno2Check(reqTelno))
				{
					errorList = setGyomuErrorList(errorList, REQ_KEY_TELNO, reqTelno, RES_KEY_ERROR_CODE_1002);
				}
			}

			// ●オプションサービスリスト
			ArrayList<HashMap<String, String>> reqOptSvcList = null;
			if (ccMsg.get(REQ_KEY_OPTION_SERVICE_LIST) == null || JFUStrConst.EMPTY.equals(ccMsg.get(REQ_KEY_OPTION_SERVICE_LIST)))
			{
				reqOptSvcList = new ArrayList<HashMap<String, String>>();
			}
			else
			{
				reqOptSvcList = (ArrayList)ccMsg.get(REQ_KEY_OPTION_SERVICE_LIST);
			}

			if (reqOptSvcList != null && reqOptSvcList.size() > 0)
			{
				for (Iterator iterator = reqOptSvcList.iterator(); iterator.hasNext();)
				{
					// ●オプションサービスマップ
					HashMap<String, String> reqOptSvcMap = (HashMap<String, String>)iterator.next();
					if (reqOptSvcMap != null && reqOptSvcMap.size() > 0)
					{
						// ●オプション／サブオプションサービスコード
						String reqOpSbopSvcCd = reqOptSvcMap.get(REQ_KEY_OP_SBOP_SVC_CD);

						if (JFUBPCommon.isNull(reqOpSbopSvcCd))
						{
							if (!isChangeOffer)
							{
								errorList = setGyomuErrorList(errorList, REQ_KEY_OP_SBOP_SVC_CD, reqOpSbopSvcCd, RES_KEY_ERROR_CODE_1001);
							}
						}
						else if (!(reqOpSbopSvcCd.length() == 3 || reqOpSbopSvcCd.length() == 4))
						{
							errorList = setGyomuErrorList(errorList, REQ_KEY_OP_SBOP_SVC_CD, reqOpSbopSvcCd, RES_KEY_ERROR_CODE_1003);
						}
						else if (!HalfCharCheck.isEnNumber1Check(reqOpSbopSvcCd))
						{
							errorList = setGyomuErrorList(errorList, REQ_KEY_OP_SBOP_SVC_CD, reqOpSbopSvcCd, RES_KEY_ERROR_CODE_1002);
						}

						// ●申込区分
						String reqAppDiv = reqOptSvcMap.get(REQ_KEY_APP_DIV);

						if (JFUBPCommon.isNull(reqAppDiv))
						{
							if (!isChangeOffer)
							{
								errorList = setGyomuErrorList(errorList, REQ_KEY_APP_DIV, reqAppDiv, RES_KEY_ERROR_CODE_1001);
							}
						}
						else if (reqAppDiv.length() != 1)
						{
							errorList = setGyomuErrorList(errorList, REQ_KEY_APP_DIV, reqAppDiv, RES_KEY_ERROR_CODE_1003);
						}
						else if (!("0".equals(reqAppDiv) || "1".equals(reqAppDiv)))
						{
							// 「0：申し込む」または「1：解約する」以外の場合はエラー
							errorList = setGyomuErrorList(errorList, REQ_KEY_APP_DIV, reqAppDiv, RES_KEY_ERROR_CODE_1002);
						}
					}
				}
			}

			if (errorList.size() > 0)
			{
				// エラーが存在する場合

				param.removeData(fixedText);
				resultMap.put(ERROR_INFO, errorList);
				param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
				param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);
				outBusLog(resultMap, "単項目チェックエラー");
				param.setData(fixedText, resultMap);
				return param;
			}

			if (reqOptSvcList != null && reqOptSvcList.size() > 0)
			{
				// 重複チェック
				ArrayList<String> summryList = new ArrayList<String>();
				ArrayList<String> errCheckList = new ArrayList<String>();
				for (Iterator iterator = reqOptSvcList.iterator(); iterator.hasNext();)
				{
					// ●オプションサービスマップ
					HashMap<String, String> reqOptSvcMap = (HashMap<String, String>)iterator.next();
					if (reqOptSvcMap != null)
					{
						// ●オプション／サブオプションサービスコード
						String reqOpSbopSvcCd = reqOptSvcMap.get(REQ_KEY_OP_SBOP_SVC_CD);

						if (summryList.contains(reqOpSbopSvcCd) && !errCheckList.contains(reqOpSbopSvcCd))
						{
							errCheckList.add(reqOpSbopSvcCd);

							errorList = setGyomuErrorList(errorList, REQ_KEY_OP_SBOP_SVC_CD, reqOpSbopSvcCd, RES_KEY_ERROR_CODE_2001);
						}
						else
						{
							summryList.add(reqOpSbopSvcCd);
						}
					}
				}

				if (errorList.size() > 0)
				{
					param.removeData(fixedText);
					resultMap.put(ERROR_INFO, errorList);
					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "オプション／サブオプションサービスコード重複チェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}
			}

			// ●契約中のオプションサブオプションサービスリスト
			ArrayList<String> nowOpSbopSvcCdList = new ArrayList<String>();

			if (isChangeOffer)
			{
				// 機能コードが「1：変更申込」（eo光電話契約中）の場合

				// ===========================================================================
				// サービス契約情報の取得。
				// ===========================================================================
				// 「EKK0081A010_サービス契約一意照会(機能コード：2)」で検索し、サービス契約情報を取得する。

				// 上りマッピング
				Object[][] ekk0081a010In =
						{ { EKK0081A010CBSMsg.TEMPLATEID, TEMPLATE_ID_EKK0081A010 }, { EKK0081A010CBSMsg.FUNC_CODE, JPCModelConstant.FUNC_CD_2 }, { EKK0081A010CBSMsg.KEY_SVC_KEI_NO, reqSvcKeiNo },
								{ EKK0081A010CBSMsg.KEY_RSV_APLY_YMD, JCCBPCommon.getOpeDate(null) } };

				// サービスIF実行
				CAANMsg ekk0081a010Msg = callSC(handle, scCall, param, fixedText, ekk0081a010In, new EKK0081A010CBSMsg().getContents());

				// サービス契約一意照会明細
				CAANMsg[] ekk0081a010Msg1List = ekk0081a010Msg.getCAANMsgList(EKK0081A010CBSMsg.EKK0081A010CBSMSG1LIST);

				// サービスIF実行結果チェック
				if (ekk0081a010Msg1List == null || ekk0081a010Msg1List.length == 0)
				{
					// サービス契約番号で該当データが存在しない場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_2002);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約情報(サービス契約番号)の存在チェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				// SYSID
				String sysid = nullToStr(ekk0081a010Msg1List[0].getString(EKK0081A010CBSMsg1List.SYSID));
				if (!reqSysid.equals(sysid))
				{
					// リクエスト項目のSYSIDと検索したカレントデータのSYSIDが異なる場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_SYSID, reqSysid, RES_KEY_ERROR_CODE_2002);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約情報(SYSID)の存在チェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				// サービス契約ステータス
				String svcKeiStat = nullToStr(ekk0081a010Msg1List[0].getString(EKK0081A010CBSMsg1List.SVC_KEI_STAT));
				if (JFUStrConst.CD00037_910.equals(svcKeiStat) || JFUStrConst.CD00037_920.equals(svcKeiStat))
				{
					// サービス契約ステータスが「910:解約済」、「920:キャンセル済」の何れかの場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_2003);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約情報のステータスチェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				// ===========================================================================
				// サービス契約内訳＜eo光電話＞情報の取得。
				// ===========================================================================
				// 「EKK0191B002_電話番号情報履歴一覧照会(機能コード：2)」で検索し、サービス契約内訳＜eo光電話＞情報を取得する。

				// 上りマッピング
				Object[][] ekk0191B002In =
						{ { EKK0191B002CBSMsg.TEMPLATEID, TEMPLATE_ID_EKK0191B002 }, { EKK0191B002CBSMsg.FUNC_CODE, JPCModelConstant.FUNC_CD_2 }, { EKK0191B002CBSMsg.KEY_SVC_KEI_NO, reqSvcKeiNo } };

				// サービスIF実行
				CAANMsg ekk0191B002Msg = callSC(handle, scCall, param, fixedText, ekk0191B002In, new EKK0191B002CBSMsg().getContents());

				// 電話番号情報履歴一覧照会明細
				CAANMsg[] ekk0191B002Msg1List = ekk0191B002Msg.getCAANMsgList(EKK0191B002CBSMsg.EKK0191B002CBSMSG1LIST);

				// サービスIF実行結果チェック
				if (ekk0191B002Msg1List == null || ekk0191B002Msg1List.length == 0)
				{
					// サービス契約番号で該当データが存在しない場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_SVC_KEI_NO, reqSvcKeiNo, RES_KEY_ERROR_CODE_2002);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約内訳＜eo光電話＞情報(サービス契約番号)の存在チェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				// 電話番号が存在する場合：true、存在しない場合：false
				boolean isExistsTelNoFlg = false;
				String svcKeiUcwkNo = JFUStrConst.EMPTY;
				for (int i = 0; i < ekk0191B002Msg1List.length; i++)
				{
					CAANMsg ekk0191B002MsgDtl = ekk0191B002Msg1List[i];
					if (reqTelno.equals(ekk0191B002MsgDtl.getObject(EKK0191B002CBSMsg1List.TELNO)))
					{
						// 電話番号が一致する場合
						isExistsTelNoFlg = true;

						// サービス契約内訳ステータス
						String svcKeiUcwkStat = nullToStr(ekk0191B002MsgDtl.getString(EKK0191B002CBSMsg1List.SVC_KEI_UCWK_STAT));
						if (!(JFUStrConst.CD00037_910.equals(svcKeiUcwkStat) || JFUStrConst.CD00037_920.equals(svcKeiUcwkStat)))
						{
							// サービス契約内訳ステータスが「910:解約済」、「920:キャンセル済」以外の場合

							//継続後契約変更手続中フラグ(0:変更手続中でない/1:変更手続中である)
							String keizkAfKeiChgechuFlg = nullToStr(ekk0191B002MsgDtl.getString(EKK0191B002CBSMsg1List.KEIZK_AF_KEI_CHGECHU_FLG));

							// サービス契約内訳番号
							svcKeiUcwkNo = nullToStr(ekk0191B002MsgDtl.getString(EKK0191B002CBSMsg1List.SVC_KEI_UCWK_NO));

							break;
						}
					}
				}

				if (isExistsTelNoFlg && JFUBPCommon.isNull(svcKeiUcwkNo))
				{
					// リクエスト項目の電話番号と一致する有効な電話番号契約が存在しない場合
					// 電話番号が存在するが、サービス契約内訳ステータスが「910:解約済」、「920:キャンセル済」以外の有効な電話番号が存在しない場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_TELNO, reqTelno, RES_KEY_ERROR_CODE_2003);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約内訳＜eo光電話＞情報のステータスチェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				if (!isExistsTelNoFlg)
				{
					// リクエスト項目の電話番号と一致する有効な電話番号契約が存在しない場合
					// 電話番号存在チェックエラーとして、戻りコードを「"999"：業務エラー」で返却する。

					param.removeData(fixedText);
					errorList = setGyomuErrorList(errorList, REQ_KEY_TELNO, reqTelno, RES_KEY_ERROR_CODE_2002);
					resultMap.put(ERROR_INFO, errorList);

					param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
					param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);

					outBusLog(resultMap, "サービス契約内訳＜eo光電話＞情報(電話番号)の存在チェックエラー");
					param.setData(fixedText, resultMap);
					return param;
				}

				// ===========================================================================
				// 現在契約中(カレント)のオプション／サブオプション情報を取得する。
				// ===========================================================================
				// 既契約者ので電話オプションサービスの情報を取得する。
				// ===========================================================================
				// 「EKK0371B001_ オプションサービス契約＜電話＞一覧照会（サービス契約内訳番号）(機能コード：1)」で検索し、
				// オプションサービス契約＜電話＞情報を取得する。

				// 上りマッピング
				Object[][] ekk0371B001In =
						{ { EKK0371B001CBSMsg.TEMPLATEID, TEMPLATE_ID_EKK0371B001 }, { EKK0371B001CBSMsg.FUNC_CODE, JPCModelConstant.FUNC_CD_1 },
								{ EKK0371B001CBSMsg.KEY_SVC_KEI_UCWK_NO, svcKeiUcwkNo } };

				// サービスIF実行
				CAANMsg ekk0371B001Msg = callSC(handle, scCall, param, fixedText, ekk0371B001In, new EKK0371B001CBSMsg().getContents());

				// オプションサービス契約＜電話＞一覧照会明細
				CAANMsg[] ekk0371B001Msg1List = ekk0371B001Msg.getCAANMsgList(EKK0371B001CBSMsg.EKK0371B001CBSMSG1LIST);

				// サービスIF実行結果チェック
				if (ekk0371B001Msg1List != null && ekk0371B001Msg1List.length > 0)
				{

					for (int i = 0; i < ekk0371B001Msg1List.length; i++)
					{
						CAANMsg ekk0371B001MsgDtl = ekk0371B001Msg1List[i];
						if (reqSysid.equals(ekk0371B001MsgDtl.getObject(EKK0371B001CBSMsg1List.SYSID)))
						{

							// オプションサービス契約ステータス
							String opSvcKeiStat = nullToStr(ekk0371B001MsgDtl.getString(EKK0371B001CBSMsg1List.OP_SVC_KEI_STAT));

							if (!JFUStrConst.CD00037_910.equals(opSvcKeiStat) && !JFUStrConst.CD00037_920.equals(opSvcKeiStat))
							{
								// 【対象とするステータス】
								// 「010:受付済」、「020:照査済」、「030:締結済」、「100:サービス提供中」、「210:休止・中断中」、「220:停止中」

								String opSvcKeiNo = nullToStr(ekk0371B001MsgDtl.getString(EKK0371B001CBSMsg1List.OP_SVC_KEI_NO));
								String opSvcCd = nullToStr(ekk0371B001MsgDtl.getString(EKK0371B001CBSMsg1List.OP_SVC_CD));

								nowOpSbopSvcCdList.add(opSvcCd);

								if (JFUStrConst.CD00136_B024.equals(opSvcCd) || JFUStrConst.CD00136_B025.equals(opSvcCd))
								{
									// 「B024：転送電話」又は「B025：発信者番号表示」の場合

									// ===========================================================================
									// 既契約者ので電話サブオプションサービスの情報を取得する。
									// ===========================================================================
									// 「EKK0401B001_サブオプションサービス契約一覧照会(機能コード：1)」で検索し、
									// サブオプションサービス契約情報を取得する。

									// 上りマッピング
									Object[][] ekk0401B001In =
											{ { EKK0401B001CBSMsg.TEMPLATEID, TEMPLATE_ID_EKK0401B001 }, { EKK0401B001CBSMsg.FUNC_CODE, JPCModelConstant.FUNC_CD_1 },
													{ EKK0401B001CBSMsg.KEY_OP_SVC_KEI_NO, opSvcKeiNo } };

									// サービスIF実行
									CAANMsg ekk0401B001Msg = callSC(handle, scCall, param, fixedText, ekk0401B001In, new EKK0401B001CBSMsg().getContents());

									// サブオプションサービス契約一覧照会明細
									CAANMsg[] ekk0401B001Msg1List = ekk0401B001Msg.getCAANMsgList(EKK0401B001CBSMsg.EKK0401B001CBSMSG1LIST);

									// サービスIF実行結果チェック
									if (ekk0401B001Msg1List != null && ekk0401B001Msg1List.length > 0)
									{
										for (int j = 0; j < ekk0401B001Msg1List.length; j++)
										{
											CAANMsg ekk0401B001MsgDtl = ekk0401B001Msg1List[j];

											// サブオプションサービス契約ステータス
											String sbOpSvcKeiStat = nullToStr(ekk0401B001MsgDtl.getString(EKK0401B001CBSMsg1List.SBOP_SVC_KEI_STAT));

											if (!JFUStrConst.CD00037_910.equals(sbOpSvcKeiStat) && !JFUStrConst.CD00037_920.equals(sbOpSvcKeiStat))
											{
												// 【対象とするステータス】
												// 「010:受付済」、「020:照査済」、「030:締結済」、「100:サービス提供中」、「210:休止・中断中」、「220:停止中」

												String sbOpSvcCd = nullToStr(ekk0401B001MsgDtl.getString(EKK0401B001CBSMsg1List.SBOP_SVC_CD));
												nowOpSbopSvcCdList.add(sbOpSvcCd);
											}
										}
									}
								}
							}
						}
					}
				}
			}

			// ●オプションサービスリスト
			if (reqOptSvcList != null && reqOptSvcList.size() > 0)
			{
				for (Iterator iterator = reqOptSvcList.iterator(); iterator.hasNext();)
				{
					// ●オプションサービスマップ
					HashMap<String, String> reqOptSvcMap = (HashMap<String, String>)iterator.next();
					if (reqOptSvcMap != null)
					{
						// ●オプション／サブオプションサービスコード
						String reqOpSbopSvcCd = reqOptSvcMap.get(REQ_KEY_OP_SBOP_SVC_CD);

						// ●申込区分　「0：申し込む」「1：解約する」
						String reqAppDiv = reqOptSvcMap.get(REQ_KEY_APP_DIV);

						if (!JFUBPCommon.isNull(reqOpSbopSvcCd) && !JFUBPCommon.isNull(reqAppDiv))
						{
							if (isChangeOffer)
							{
								// 機能コードが「1：変更申込」（eo光電話契約中）の場合
								if ("0".equals(reqAppDiv) && !nowOpSbopSvcCdList.contains(reqOpSbopSvcCd))
								{
									//「0：申し込む」
									// 且つ契約中のオプションサブオプションサービスリストに存在しない場合

									nowOpSbopSvcCdList.add(reqOpSbopSvcCd);
								}
								else if ("1".equals(reqAppDiv) && nowOpSbopSvcCdList.contains(reqOpSbopSvcCd))
								{
									//「1：解約する」
									// 且つ契約中のオプションサブオプションサービスリストに存在する場合

									nowOpSbopSvcCdList.remove(reqOpSbopSvcCd);
								}
							}
							else
							{
								if ("0".equals(reqAppDiv) && !nowOpSbopSvcCdList.contains(reqOpSbopSvcCd))
								{
									//「0：申し込む」
									// 且つ契約中のオプションサブオプションサービスリストに存在しない場合

									nowOpSbopSvcCdList.add(reqOpSbopSvcCd);
								}
							}
						}
					}
				}
			}

			// ●契約中のオプションサブオプションサービスリスト
			ArrayList<HashMap<String, String>> judegOpSvcCdList = new ArrayList<HashMap<String, String>>();
			for (Iterator iterator = nowOpSbopSvcCdList.iterator(); iterator.hasNext();)
			{
				HashMap<String, String> judegOpSvcCdMap = new HashMap<String, String>();
				judegOpSvcCdMap.put(IN_OTK_JUDGE_OP_SBOP_SVC_CD, (String)iterator.next());
				judegOpSvcCdList.add(judegOpSvcCdMap);
			}

			// お得判定部品用パラメータ オプションサービスリスト
			ccMsg.put(IN_OTK_JUDGE_OP_SVC_CD_LIST, judegOpSvcCdList);

			// お得判定部品用パラメータ 運用日付
			ccMsg.put(IN_OTK_BASE_DATE, JCCBPCommon.getOpeDate(null));

			// ◆オプションパック適用判定処理（API用）の呼出◆
			HashMap resultOtkMap = super.judgeOptPackIf(handle, param, fixedText);

			// パック判定区分[pack_judge_div]
			resultMap.put(RES_KEY_PACK_JUDGE_DIV, nullToStr(resultOtkMap.get(OUT_OTK_RTWAJG_PTN_DIV)));

			// 表示文言（お客さま向け）[dsp_msg_consumer]
			resultMap.put(RES_KEY_DSP_MSG_CONSUMER, nullToStr(resultOtkMap.get(OUT_OTK_CSM_YO_DSP_MNGN)));

			// 表示文言（オペレータ向け）[dsp_msg_operator]
			resultMap.put(RES_KEY_DSP_MSG_OPERATOR, nullToStr(resultOtkMap.get(OUT_OTK_OPRT_YO_DSP_MNGN)));

			if (resultOtkMap.get(OUT_OTK_SINGLE_OPTION_SERVICE_LIST) != null)
			{
				// 単品オプションサービスリスト[single_option_service_list]
				resultMap.put(RES_KEY_SINGLE_OPTION_SERVICE_LIST, resultOtkMap.get(OUT_OTK_SINGLE_OPTION_SERVICE_LIST));
			}

			if (OUT_OTK_RETURN_CD_000.equals(resultOtkMap.get(OUT_OTK_RETURN_CD)) || OUT_OTK_RETURN_CD_100.equals(resultOtkMap.get(OUT_OTK_RETURN_CD)))
			{
				// 戻りコードが『000』…正常終了／『100』…オプション組み合わせ不正の場合

				// 戻りコード[return_cd]「000：正常終了」とする。
				resultMap.put(RES_KEY_RETURN_CD, RES_RETURN_CD_RESULT_000);
			}
		}
		// サービスインターフェース呼び出しでエラーが発生した場合
		catch (SCCallException scCallEx)
		{
			// システムエラーを設定する
			param.removeData(fixedText);
			param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_9000);
			param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, RETURN_MESSAGE_SYSTEM_ERROR);
			outBusLog(resultMap, RETURN_MESSAGE_SYSTEM_ERROR);
			param.setData(fixedText, resultMap);
			return param;
		}

		param.removeData(fixedText);
		param.setControlMapData(SCControlMapKeys.RETURN_CODE, RETURN_CD_0000);
		param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, null);
		param.setData(fixedText, resultMap);

		return param;
	}

	/**
	 * 引数がnullの場合、""(空文字)を返却する。
	 *
	 * @param str 項目名
	 * @return 項目名
	 */
	private String nullToStr(Object obj)
	{
		if (obj == null)
		{
			return JFUStrConst.EMPTY;
		}
		return (String)obj;
	}

	/**
	 * 業務エラー情報作成処理
	 *
	 * @param itemName
	 * @param itemValue
	 * @param errorCode
	 * @return
	 * @throws Exception
	 */
	private List<Map<String, String>> setGyomuErrorList(List<Map<String, String>> errorList, String itemName, String itemValue, String errorCode) throws Exception
	{

		if (errorList == null)
		{
			errorList = new ArrayList<Map<String, String>>();
		}

		Map<String, String> returnMap = new HashMap<String, String>();

		// 業務エラー設定
		returnMap.put(ERROR_CODE, errorCode);

		// エラーメッセージ設定
		String errMessage = itemName + COLON + itemValue;

		returnMap.put(ERROR_MESSAGE, errMessage);

		if (!errorList.contains(returnMap))
		{
			errorList.add(returnMap);
		}

		return errorList;
	}

	/**
	 * サービスIF呼出処理
	 *
	 * @param handle セッションハンドル
	 * @param scCall SC呼び出し部品
	 * @param param リクエストパラメータ
	 * @param dataMapKey パラメータキー
	 * @param mappingData マッピングデータ
	 * @param contents CAANMsgのコンテンツ
	 * @return CAANMsg
	 * @throws Throwable 例外が発生した場合
	 */
	private CAANMsg callSC(SessionHandle handle, ServiceComponentRequestInvoker scCall, IRequestParameterReadWrite param, String dataMapKey, Object[][] mappingData, Object[][] contents)
			throws Throwable
	{
		HashMap<String, Object> paramMap = editInMsg(param, mappingData);

		Map<?, ?> result = scCall.run(paramMap, handle);

		CAANMsg[] templates = (CAANMsg[])result.get(JCMConstants.TEMPLATE_LIST_KEY);

		CAANMsg msg = templates[0];

		// リターンコード取得
		int return_code = (Integer)result.get(JCMConstants.RET_CD_INT_KEY);
		int status = msg.getInt(JCMConstants.STATUS_INT_KEY);

		editErrorInfoCom(param, templates, return_code, dataMapKey, mappingData, contents);

		// エラー情報のマップを取得
		ArrayList<Object> errorList = (ArrayList<Object>)param.getControlMapData(SCControlMapKeys.ERROR_INFO);

		if (null == errorList)
		{
			errorList = new ArrayList<Object>();
		}

		// コントロールマップに設定
		param.setControlMapData(SCControlMapKeys.ERROR_INFO, TemplateErrorUtil.getErrorInfo(result, errorList));

		// 異常の場合、SCCallExceptionを生成してスローする
		if (JPCModelConstant.NORMAL_END < return_code || JPCModelConstant.WARNING < status)
		{
			throw new SCCallException("戻り値不正", String.valueOf(return_code), status);
		}

		return msg;
	}

	/**
	 * 共通項目のメッセージを作成します。
	 *
	 * @param param リクエストパラメータ
	 * @param mappingData マッピングデータ
	 * @return HashMap<String, Object>
	 * @throws RequestParameterException リクエストパラメータの操作でエラーが発生した場合
	 */
	private HashMap<String, Object> editInMsg(IRequestParameterReadWrite param, Object[][] mappingData) throws RequestParameterException
	{
		HashMap<String, Object> paramMap = new HashMap<String, Object>();

		// 【取得元：電文ヘッダ(ヘッダ)】
		// 電文ID
		paramMap.put(JCMConstants.TRANZACTION_ID_KEY, param.getTelegramID());

		// ユースケースID
		paramMap.put(JCMConstants.USECASE_ID_KEY, param.getUsecaseID());

		// オペレーションID
		paramMap.put(JCMConstants.OPERATION_ID_KEY, param.getOperationID());

		// サービス呼び出し区分
		paramMap.put(JCMConstants.CALL_TYPE_KEY, param.getCallType());

		// 【取得元：ユーザエリア(コントロールマップ)】
		// 依頼先ホスト名
		paramMap.put(JCMConstants.CLIENT_HOST_NAME_KEY, param.getControlMapData(SCControlMapKeys.REQ_HOSTNAME));

		// 依頼元IPアドレス
		paramMap.put(JCMConstants.CLIENT_IP_ADDRESS_KEY, param.getControlMapData(SCControlMapKeys.REQ_HOSTIP));

		// 依頼元画面ID
		paramMap.put(JCMConstants.INVOKE_GAMEN_ID_KEY, param.getControlMapData(SCControlMapKeys.REQ_VIEWID));

		// オペレータID
		paramMap.put(JCMConstants.OPERATOR_ID_KEY, param.getControlMapData(SCControlMapKeys.OPERATOR_ID));

		String svcIf = (String)mappingData[0][1];

		CAANMsg template = new CAANMsg(String.format("eo.ejb.cbs.cbsmsg.%sCBSMsg", svcIf));

		// オペレータID
		template.set(JCMConstants.OPERATOR_ID_KEY, param.getControlMapData(SCControlMapKeys.OPERATOR_ID));

		// 運用日付
		template.set(JCMConstants.OPERATE_DATE_KEY, param.getControlMapData(SCControlMapKeys.OPE_DATE));

		// 運用日時
		template.set(JCMConstants.OPERATE_DATETIME_KEY, param.getControlMapData(SCControlMapKeys.OPE_TIME));

		for (int i = 0; i < mappingData.length; i++)
		{
			if (JFUStrConst.EMPTY.equals(mappingData[i][1]))
			{
				template.setNull((String)mappingData[i][0]);
			}
			else
			{
				template.set((String)mappingData[i][0], mappingData[i][1]);
			}
		}

		CAANMsg[] templates = new CAANMsg[1];

		templates[0] = template;

		paramMap.put(JCMConstants.TEMPLATE_LIST_KEY, templates);

		return paramMap;
	}

	/**
	 * エラー情報を取得します。
	 *
	 * @param param リクエストパラメータ
	 * @param templates CAANMsgクラス
	 * @param returnCode リターンコード
	 * @param dataMapKey パラメータキー
	 * @param mappingData マッピングデータ
	 * @param contents CAANMsgのコンテンツ
	 * @return IRequestParameterReadWrite
	 * @throws RequestParameterException リクエストパラメータの操作でエラーが発生した場合
	 */
	private IRequestParameterReadWrite editErrorInfoCom(IRequestParameterReadWrite param, CAANMsg[] templates, int returnCode, String dataMapKey, Object[][] mappingData, Object[][] contents)
			throws RequestParameterException
	{
		CAANMsg template = templates[0];

		int templateStatus = template.getInt(JCMConstants.STATUS_INT_KEY);

		if (0 != returnCode)
		{
			templateStatus = 9000;
		}

		if (null == JCMAPLConstMgr.getString("RETURN_MESSAGE_" + String.format("%1$04d", templateStatus)))
		{
			templateStatus = 0;
		}

		int bpStatus = 0;

		Object obj = param.getControlMapData(SCControlMapKeys.RETURN_CODE);

		if (null == obj)
		{
			bpStatus = -1;
		}
		else
		{
			bpStatus = Integer.parseInt((String)param.getControlMapData(SCControlMapKeys.RETURN_CODE));
		}

		if (templateStatus > bpStatus)
		{
			// BPにサービスコンポーネントのステータスを設定する。
			String formatStatus = String.format("%1$04d", templateStatus);

			String message = JCMAPLConstMgr.getString("RETURN_MESSAGE_" + formatStatus);

			param.setControlMapData(SCControlMapKeys.RETURN_CODE, formatStatus);

			param.setControlMapData(SCControlMapKeys.RETURN_MESSAGE, message);
		}

		Map<String, String> inMap = null;

		// ユーザデータ情報
		inMap = (Map<String, String>)param.getData(dataMapKey);

		for (int i = 0; null != contents && i < contents.length; i++)
		{
			String itemNm = (String)contents[i][0];
			if (itemNm.endsWith("_err"))
			{
				String errCd = template.getString(itemNm);
				if (!JFUBPCommon.isNull(errCd))
				{
					inMap.put(itemNm, errCd);
				}
			}
		}
		return param;
	}

	/**
	 * ビジネスログ出力メッセージ
	 *
	 * @param ccMsg
	 * @param msg
	 */
	private void outBusLog(Map<String, Object> ccMsg, String msg)
	{
		StringBuffer outBusLogMsg = new StringBuffer();
		outBusLogMsg.append("FUIFE170#JFUProfitJudgeOptPackIfCC#func_cd(").append(ccMsg.get(REQ_KEY_FUNC_CD)).append(")：sysid(").append(ccMsg.get(REQ_KEY_SYSID)).append(")：svc_kei_no(").append(
				ccMsg.get(REQ_KEY_SVC_KEI_NO)).append(")：telno(").append(ccMsg.get(REQ_KEY_TELNO)).append(")：").append(msg);

		JSYejbLog.println(JSYLogBase.DEBUG, this.getClass(), outBusLogMsg);
	}

}