/*
 * MosP - Mind Open Source Project         http://www.mosp.jp/
 * Copyright (C) 1987-2009 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 General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.payroll.bonus.action;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Calendar;
import java.util.Iterator;
import java.math.BigDecimal;

import org.apache.commons.csv.CSVParser;

import jp.mosp.payroll.base.Employee;
import jp.mosp.payroll.base.PayrollAction;

import jp.mosp.payroll.dao.PtCurrentDao;
import jp.mosp.payroll.dao.CmCompanyDao;
import jp.mosp.payroll.dao.CmPaymentMeansDao;
import jp.mosp.payroll.dao.CmSocInsuranceDao;

import jp.mosp.payroll.dto.DetailTimeDto;
import jp.mosp.payroll.dto.PtCurrentDto;
import jp.mosp.payroll.dto.CmCompanyDto;
import jp.mosp.payroll.dto.CmPaymentMeansDto;
import jp.mosp.payroll.dto.CmSocInsuranceDto;

import jp.mosp.system.dao.MKaisyaDao;
import jp.mosp.system.dto.MKaisyaDto;

import jp.mosp.payroll.bonus.dto.PdPaymentDto;
import jp.mosp.payroll.bonus.dto.PmExtraDto;


import jp.mosp.payroll.bonus.dao.PdBonAllowanceDao;
import jp.mosp.payroll.bonus.dao.PdBonDeductionDao;
import jp.mosp.payroll.bonus.dao.PdBonSumDao;
import jp.mosp.payroll.bonus.dao.PiBonusDao;

import jp.mosp.payroll.bonus.dao.PdBonusDao;
import jp.mosp.payroll.bonus.dao.PhBounsDao;
import jp.mosp.payroll.bonus.dao.PmExtraDao;

import jp.mosp.payroll.bonus.dto.PdBonusDto;
import jp.mosp.payroll.bonus.dto.PhBounsDto;
import jp.mosp.payroll.bonus.dto.PmDetailBsDto;
import jp.mosp.payroll.bonus.dto.PsDetailDto;
import jp.mosp.payroll.bonus.dto.PhBasisDto;
import jp.mosp.payroll.bonus.vo.BonusCalcVo;
import jp.mosp.payroll.bonus.BonusConst;

import jp.mosp.payroll.base.PayrollConst;

import jp.mosp.common.common.FileUploadUtility;
import jp.mosp.common.common.MospConst;
import jp.mosp.common.common.MospException;
import jp.mosp.common.common.MospUtility;
import jp.mosp.common.common.ValidateUtility;
import jp.mosp.common.dao.MosPConfDao;
import jp.mosp.common.dto.MosPConfDto;

import jp.mosp.legal.LegalConst;
import jp.mosp.legal.LegalUtility;

public class BonusCalcAction extends PayrollAction {
	// フィールド
	// 計算処理実行インスタンス
	protected PdPaymentDto pdPaymentDto;	// 処理対象給与データ
	// 計算処理実行インスタンス(賞与)
	protected PhBounsDto phBounsDto;	// 処理対象賞与データ

	// コマンド
	private static final String CMD_BONUS_CALC_SHOW   = "P2001";
	private static final String CMD_BONUS_CALC_UPLOAD = "P2002";
	private static final String CMD_BONUS_CALC_DELETE = "P2003";
	private static final String CMD_BONUS_CALC_TAX    = "P2004";
	private static final String CMD_BONUS_CALC_TOTAL  = "P2005"; /* 確定処理 */
	
	public String[][] aryBonusHeaderItem;
	
	private String[] aryCsvData;
	private String[] aryHeaderData;
	private String[] aryRegistCsvData;
	private String[] aryRegistHeaderData;
	private String[] aryHeaderNo;
	private int csvRegistDataCount = 0;
	private int csvDataCount = 0;
	
	private boolean isAddRegistData = true;

	protected PhBasisDto phBasisDto;
	
	/** ↓↓継承関係が築ければ、継承元で定義 **/
	// 計算用
	public		int	sumAllowance	= 0;
	public		int	sumDeduction	= 0;
	public		int	sumDatail		= 0;

	protected int befHealthPremium   = 0; // 賞与対象年月健康保険料(対象日以外)
	protected int befNursingPremium  = 0;	// 賞与対象年月介護保険料(対象日以外)
	protected int befPensionPremium  = 0;	// 賞与対象年月厚生年金保険料(対象日以外)
	protected int befFundPremium     = 0;	// 賞与対象年月厚生年金基金保険料(対象日以外)
	protected int befSocInsPremium   = 0;	// 賞与対象年月社会保険金額(対象日以外)
	protected int bank1Amount = 0;	// 銀行1振込額
	protected int bank2Amount = 0;	// 銀行2振込額
	protected int cashAmount  = 0;	// 現金支給額
	// 会社情報(共通)
	protected float  ratioHealthPremium   = 0;	// 健康保険料率 
	protected float  ratioNursingPremium  = 0;	// 介護保険料率
	protected float  ratioPensionPremium  = 0;	// 厚生年金料率
	protected float  ratioFundPremium     = 0;	// 厚生年金基金料率
	protected float  ratioUnemployPremium = 0;	// 雇用保険料率
	protected String  fundParticipation    = "";	// 厚生年金基金加入
	protected Date startDateOfYear;	// 年開始日
	protected Date lastDateOfYear ;	// 年最終日
	protected String kCode;
	protected Date   targetMonth    ;
	// 明細設定(賞与共通)
	protected Map<String, Map<String, PsDetailDto>> mapBonusDetailAll;
	Date befTargetMonth;	//　前月の課税対象額取得用年月
	

	
	/**
	 * アクション
	 */
	public void action() throws Exception {
		// DBコネクション取得
		getConnection();
		// PAYROLL権限確認
		confirmPayrollAuth();
		// VOの取得
		BonusCalcVo vo;
		Object obj = session.getAttribute(MospConst.ATT_FORMER_VO);
		if (obj instanceof BonusCalcVo) {
			vo = (BonusCalcVo)obj;
		} else {
			vo = new BonusCalcVo();
		}
		
		vo.clearMessage();
		vo.clearErrField();
		vo.initVo(cfg, msg, cmd, aspUser, user);
		
		// VO及びフォワード先URLの設定
		setVo(vo);
		setUrl(vo.getRetUrl());
		
		session.setAttribute(MospConst.ATT_FORMER_VO, vo);
		// コマンド毎の処理
		if (cmd.equals(CMD_BONUS_CALC_SHOW)) {
			// 年月日プルダウン設定(計算年月)
			setYearMonth  (vo, getCalcCurrent(),true);
			// リスト取得
			vo.aryPltStationAll = getStationArrayAll();
		} else 	if (cmd.equals(CMD_BONUS_CALC_UPLOAD)) {
			// ファイル情報設定
			vo.setUploadFile(multipartFieldList);
			// 対象年月日取得
			setTargetMonth(vo);
			// 賞与情報アップロード
			upload(vo);
		} else 	if (cmd.equals(CMD_BONUS_CALC_TAX)) {
			// 年月の設定
			vo.setYearParams(request);
			// 対象年月、賞与支給日設定
			setTargetMonth(vo);
			// 法定控除計算
			calcLegalDeduction(vo);
			
		} else 	if (cmd.equals(CMD_BONUS_CALC_TOTAL)) {
			// 年月の設定
			vo.setYearParams(request);
			// 対象年月、賞与支給日設定
			setTargetMonth(vo);
			// 確定処理
			calcTotal(vo);
		} else if (cmd.equals(CMD_BONUS_CALC_DELETE)) {
			// リクエストを設定
			vo.setYearParams(request);
			// 対象年月日取得
			setTargetMonth(vo);
			// 削除(計算年月日)
			delete(vo);

		} else {
			throw new MospException(MospConst.EX_CMD_INVALID);
		}
	}
	
	/**
	 * 年月設定
	 * @param vo          設定対象VO
	 * @param calcCurrent 計算月
	 */
	private void setYearMonth(BonusCalcVo vo, Date calcCurrent,boolean isSetPulldown) {
		// プルダウン取得
		vo.aryPltYear  = getYearArrayLong(MospUtility.getYear(calcCurrent));
		vo.aryPltMonth = getMonthArray();
		vo.aryPltDay   = getDayArray();
		if (isSetPulldown) {
			// 年月設定
			vo.setPltYear(String.valueOf(MospUtility.getYear(calcCurrent)));
			vo.setPltMonth(String.valueOf(MospUtility.getMonth(calcCurrent)));
			vo.setPltDay(String.valueOf(MospUtility.getDay(calcCurrent)));
			vo.setPltDetailYear (String.valueOf(MospUtility.getYear(calcCurrent)));
			vo.setPltDetailMonth(String.valueOf(MospUtility.getMonth(calcCurrent)));
			vo.setPltDetailDay  (String.valueOf(MospUtility.getDay(calcCurrent)));
		}
	}
	
	/**
	 * アップロード
	 * @param vo
	 */
	private void upload (BonusCalcVo vo)  throws Exception {
		// DBコネクション取得
		getConnection();
		// 給与ヘッダー項目取得
		aryBonusHeaderItem = getBonusUploadHeadItem();
		// 年月の確認
		chkRegsitDate(vo);
		// ファイルタイプチェック
		if (vo.getFilUploadFile().getName().matches(".*" + ".csv" + ".*")) {
			// csvパースファイル取得
			CSVParser csvp = FileUploadUtility.parseCSVData(vo.getFilUploadFile());
			String[] lineHead;
			lineHead = getCSVHeadData(csvp);
			String[] lineData;
			boolean isInsertData = true;
			// ヘッダ作成
			createUploadHeader(vo,lineHead);
			// データ作成
			while (isInsertData) {
				lineData = getCSVData(csvp,csvDataCount);
				if (lineData != null) {
					// データ作成
					if (createUploadData(vo, lineData)) {
						// 社員存在確認
						if(chkEmploymentData(vo) == false) {
							csvDataCount++;
							// データ行にエラーがある場合はその行を登録させない
							continue;
						}
						// 致命的エラー確認(データ)
						chkErrorData(vo);
						// アップロード可能エラー確認
						chkUploadErrorData(vo);
						// PI_BONUS登録
						uploadData(vo, lineHead, lineData);
					}
					csvDataCount++;
				} else {
					isInsertData = false;
				}
			}
			if (csvRegistDataCount == 0) {
				// データ無しエラー
				String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NO_CSV_DATA, "");
				addErrMessage(errMessage);
				throw new MospException(MospConst.EX_PARAMS_INVALID);
			}
			// メッセージ設定
			vo.setMessage(MospUtility.getMessage(msg, BonusConst.MSG_CSV_UPLOAD,String.valueOf(csvRegistDataCount)));
		} else {
			// ファイルタイプエラー
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_INVALID_FILE_TYPE, "");
			addErrMessage(errMessage);
			throw new MospException(MospConst.EX_PARAMS_INVALID);
		}
	}
	
	/**
	 * 対象年月設定
	 * @param vo          対象VO
	 * @param targetMonth 設定年月
	 * @throws Exception
	 */
	public void setTargetMonth(BonusCalcVo vo) throws Exception {
		// 計算年月
		vo.setCalcDate();
		// 賞与支給日
		vo.setPayday();
	}

	
	/**
	 * 登録日時妥当性確認
	 * @param vo
	 * @throws Exception 
	 */
	protected void chkRegsitDate(BonusCalcVo vo) throws Exception {
		// 支給日の妥当性確認
		vo.checkTargetDate();
		// 計算年月確認
//		checkCalcCurrentAfter(vo.getCalcDate());
	}
	
	/**
	 * 致命的エラー確認(データ)
	 * @param vo
	 * @throws MospException
	 */
	protected void chkErrorData(BonusCalcVo vo) throws MospException {
		// 社員コードヘッダ取得チェック
		if (vo.getIdxKCode() == 99) {
			// キーの取得エラー
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NO_UPLOAD_KEY, "社員コード" + "(" + BonusConst.UP_K_CODE + ")");
			addErrMessage(errMessage);
			throw new MospException(MospConst.EX_PARAMS_INVALID);
		}
		// ヘッダと値の数差異チェック
		if (aryHeaderData.length != aryCsvData.length) {
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_INVALID_CSV_COUNT, String.valueOf(csvDataCount + 1));
			addErrMessage(errMessage);
			throw new MospException(MospConst.EX_PARAMS_INVALID);
		}
	}
	
	/**
	 * アップロード可能データ取得
	 * @param vo
	 * @throws Exception 
	 */
	protected void chkUploadErrorData(BonusCalcVo vo) throws Exception {
		// エラー確認前の状態を格納
		ArrayList<String> listHeadItem = new ArrayList<String>();
		ArrayList<String> listDataItem = new ArrayList<String>();
		for (int i = 0; i < aryCsvData.length; i++ ) {
			// データ妥当性確認
			isAddRegistData = validate(aryCsvData[i],aryHeaderData[i],i,csvDataCount, vo);
			if (isAddRegistData) {
				// 明細設定確認
				if (!aryHeaderData[i].equals(BonusConst.UP_K_CODE)) {
					isAddRegistData = chkNoSettingDetail(i);
				}
				if (isAddRegistData) {
					//正しいデータのみ取得
					listHeadItem.add(aryHeaderNo[i]);
					listDataItem.add(aryCsvData[i]);
				}
			}
			// 初期化
			isAddRegistData = true;
		}
		// 登録配列設定
		aryRegistHeaderData = new String[listHeadItem.size()];
		aryRegistCsvData = new String[listDataItem.size()];
		
		for (int i = 0; i < listHeadItem.size(); i++) {
			
			String headerData = listHeadItem.get(i);
			String csvData = listDataItem.get(i);
			aryRegistHeaderData[i] = headerData;
			aryRegistCsvData[i] = csvData;
		}
	}
	
	/**
	 * 明細設定がない項目確認
	 * @param vo PayCalculateVoインスタンス
	 * @throws Exception
	 */
	private boolean chkNoSettingDetail(int idx) throws Exception {
		// 計算準備
		boolean isRegist = true;
		// 選択された社員の雇用区分職位が持つ明細項目のマップを取得
		Map<String, PsDetailDto> mapDetail = getMapBonusDetail();
		if (mapDetail == null) {
			// 設定が一切されていない場合はエラー
//			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NO_DETAIL_DATA, this.phBounsDto.getEmploymentType());
//			addErrMessage(errMessage);
			isRegist = false;
			return isRegist;
		}
		PsDetailDto PsDetailDto = mapDetail.get(aryHeaderData[idx]);
		// 設定がなかったら金額を見る
		if (PsDetailDto == null) {
			if (!aryCsvData[idx].equals("0")) {
				// 設定が無いのに０円でない場合はエラー
				String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NOT_UP_EMP_TYPE,
						aryHeaderData[idx]+ BonusConst.KEY_SEPARATOR + String.valueOf(csvDataCount + 1)
				);
				addErrMessage(errMessage);
				isRegist = false;
				
			}
		}
		return isRegist;
	}

	/**
	 * アップロードデータ更新
	 * @param vo
	 * @throws Exception
	 */
	protected void uploadData(BonusCalcVo vo, String[] lineHead, String[] lineData) throws Exception {
		PmDetailBsDto allowanceDto;
		PmDetailBsDto deductionDto;
		PmDetailBsDto sumDto;
		Employee employee;
		sumAllowance	= 0;	// 総支給額
		sumDeduction	= 0;	// 総控除額
		sumDatail		= 0;	// 差引支給額
		bank1Amount		= 0;	// 銀行1振込額
		bank2Amount		= 0;	// 銀行2振込額
		cashAmount		= 0;	// 現金支給額
		
		employee = getEmployee(lineData[0]);
		if(null == employee){
			return;
		}

		List<PmDetailBsDto> listAllowance = new ArrayList<PmDetailBsDto>();
		List<PmDetailBsDto> listDeduction = new ArrayList<PmDetailBsDto>();
		List<PmDetailBsDto> listSum = new ArrayList<PmDetailBsDto>();
		
		for(int i= 1; i<lineData.length ; i++){
			if(lineHead[i].startsWith("BA")  ){
				allowanceDto = new PmDetailBsDto();
				allowanceDto.setKCode(lineData[0]);
				allowanceDto.setCalcDate(vo.getPayday());
				allowanceDto.setDetailCode(lineHead[i]);
				allowanceDto.setAmount(Integer.valueOf(lineData[i]));
				listAllowance.add(allowanceDto);
				sumAllowance += Integer.valueOf(lineData[i]); 
			}else if(lineHead[i].startsWith("BD")  ){
				deductionDto = new PmDetailBsDto();
				deductionDto.setKCode(lineData[0]);
				deductionDto.setCalcDate(vo.getPayday());
				deductionDto.setDetailCode(lineHead[i]);
				deductionDto.setAmount(Integer.valueOf(lineData[i]));
				listDeduction.add(deductionDto);
				sumDeduction += Integer.valueOf(lineData[i]); 
			}
		}
		// 支給テーブルの作成
		sumDatail = sumAllowance - sumDeduction;
		calcDivideAccounts(employee); // 各銀行への振込額及び現金支給額を計算
		
		// 総支給額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS001");
		sumDto.setAmount(sumAllowance);
		listSum.add(sumDto);
		// 総控除額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS002");
		sumDto.setAmount(sumDeduction);
		listSum.add(sumDto);
		// 差引支給額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS003");
		sumDto.setAmount(sumDatail);
		listSum.add(sumDto);

		// 銀行1振込額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS011");
		sumDto.setAmount(bank1Amount);
		listSum.add(sumDto);
		// 銀行2振込額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS012");
		sumDto.setAmount(bank2Amount);
		listSum.add(sumDto);
		// 現金支給額
		sumDto = new PmDetailBsDto();
		sumDto.setKCode(lineData[0]);
		sumDto.setCalcDate(vo.getPayday());
		sumDto.setDetailCode("BS013");
		sumDto.setAmount(cashAmount);
		listSum.add(sumDto);
		
		PhBounsDto phBounsDto = new PhBounsDto();  
		phBounsDto.setKCode(lineData[0]);
		phBounsDto.setCalcMonth(vo.getPayday());
		phBounsDto.setPayDate(MospUtility.getDate("1970/01/01"));
		phBounsDto.setSlipCode("B***");
		phBounsDto.setAllowanceTotal(sumAllowance);
		phBounsDto.setDeductionTotal(sumDeduction);
		phBounsDto.setPaymentTotal(sumDatail);
		
		// DAOの補足
		PdBonAllowanceDao pdBonAllowanceDao = new PdBonAllowanceDao(cfg, cmd, aspUser, user, conn); 
		pdBonAllowanceDao.initDao(cfg, cmd, aspUser, user, conn);

		PdBonDeductionDao pdBonDeductionDao = new PdBonDeductionDao(cfg, cmd, aspUser, user, conn);
		pdBonDeductionDao.initDao(cfg, cmd, aspUser, user, conn);

		PdBonSumDao pdBonSumDao = new PdBonSumDao(cfg, cmd, aspUser, user, conn); 
		pdBonSumDao.initDao(cfg, cmd, aspUser, user, conn);
		
		PhBounsDao phBounsDao = new PhBounsDao(cfg, cmd, aspUser, user, conn); 
		phBounsDao.initDao(cfg, cmd, aspUser, user, conn);

		// 旧データの削除
		pdBonAllowanceDao.delete(lineData[0], vo.getPayday()); 
		pdBonDeductionDao.delete(lineData[0], vo.getPayday());
		pdBonSumDao.delete(lineData[0], vo.getPayday());
		phBounsDao.deleteForCalcdate(lineData[0], vo.getPayday()); 
	
		// 登録
		phBounsDao.insert(phBounsDto, true);
		pdBonAllowanceDao.insert(listAllowance); 
		pdBonDeductionDao.insert(listDeduction);
		pdBonSumDao.insert(listSum);
		
		// コミット
		commit();

		csvRegistDataCount ++;
	}
	
	/**
	 * アップロード用配列作成（ヘッダ）
	 * @param vo PayCalculateVoインスタンス
	 * @throws Exception
	 */
	private boolean createUploadHeader(BonusCalcVo vo,String[] line) throws Exception {
		aryHeaderNo   = new String[line.length];
		aryHeaderData = new String[line.length];
		aryHeaderData = line;
		// 社員コード存在チェック用
		vo.setIdxKCode(99);
		// ヘッダー中身チェック
		for (int i = 0; i < aryHeaderData.length; i++) {
			if(vo.checkHeadData(aryHeaderData[i],aryBonusHeaderItem)) {
				aryHeaderNo  [i] = vo.getHeadNo(aryHeaderData[i],aryBonusHeaderItem,i);
			} else {
				// ヘッダ不正エラー
				String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_INVALID_CSV_HEAD, aryHeaderData[i]);
				addErrMessage(errMessage);
				throw new MospException(MospConst.EX_PARAMS_INVALID);
			}
		}
		// ヘッダ重複確認
		chkDeplicateHeader();
		return true;
	}
	
	/**
	 * ヘッダー重複確認
	 * @throws MospException
	 */
	protected void chkDeplicateHeader() throws MospException {
		for (int i = 0; i < aryHeaderNo.length; i++) {
			for (int j = i+1; j < aryHeaderNo.length; j++) {
				if (aryHeaderNo[i].equals(aryHeaderNo[j])) {
					// ヘッダ重複エラー
					String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_INVALID_HEAD, aryHeaderData[i]);
					addErrMessage(errMessage);
					throw new MospException(MospConst.EX_PARAMS_INVALID);
				}
			}
		}
	}
	
	/**
	 * アップロード用配列作成（データ）
	 * @param vo PayCalculateVoインスタンス
	 * @throws Exception
	 */
	private boolean createUploadData(BonusCalcVo vo,String[] line) throws Exception {
		// データ行取得
		aryCsvData = new String[line.length];
		aryCsvData = line;
		for (int i = 0; i < aryCsvData.length; i++ ) {
			// キーデータ取得
			vo.setKeyData(aryCsvData[i], i);
		}
		return true;
	}
	
	/**
	 * 社員存在確認
	 * @param vo
	 * @throws Exception
	 */
	public boolean chkEmploymentData(BonusCalcVo vo)
		throws Exception
	{
		boolean isRegist = true;
		// 社員コード未設定
		if (vo.getKCode() == null) {
			// キーの取得エラー
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NO_UPLOAD_KEY, "社員コード" + "(" + BonusConst.UP_K_CODE + ")");
			addErrMessage(errMessage);
			isRegist = false;
		}
		// 社員コード存在チェック(人事情報)
		Employee employee = getUpdateBasisHistory(vo.getKCode(), getEndDate(vo.getCalcDate()));
		if (employee == null) {
			// 未登録社員コードエラー
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NO_UPLOAD_HUMAN, String.valueOf(csvDataCount + 1));
			addErrMessage(errMessage);
			isRegist = false;
			return isRegist;
		}
		// 退職している社員の場合エラー
//		if (phBasisDto.getRetirementDate() != null && getTimeRemainder(
//				phBasisDto.getRetirementDate(), getStartDate(vo.getCalcDate())) < 0) {
//			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_UP_RETIRED, String.valueOf(csvDataCount + 1));
//			addErrMessage(errMessage);
//			isRegist = false;
//		}
		return isRegist;
	}
	
	/**
	 * csvデータの妥当性確認
	 * @throws MospException
	 */
	public boolean validate(String value, String aryHeaderName,int idx, int csvRowCount, BonusCalcVo vo) throws MospException {
		// 件数設定
		int rowCount = csvRowCount;
		rowCount += 1;
		// エラー名称設定
		String errReplace = aryHeaderName + "," + String.valueOf(rowCount);
		if (idx != vo.getIdxKCode()) {
			// 数値の妥当性チェック
			checkCsvNumeric(value, errReplace);
			checkCsvLength(value, BonusCalcVo.LEN_AMOUNT, aryHeaderName, rowCount);
		}
		return isAddRegistData;
	}
	
	/**
	 * 文字列タイプ確認(数値：double)
	 * @param value 確認対象文字列
	 * @param name  確認対象名称
	 * @param id    確認対象フィールドID
	 * @throws MospException
	 */
	protected void checkCsvNumeric(String value, String name) throws MospException {
		if (!ValidateUtility.chkNumeric(value)) {
			String errMsg = MospUtility.getMessage(msg, "WC0022", name);
			addErrMessage(errMsg);
			isAddRegistData = false;
		}
	}
	
	/**
	 * 文字列長確認(最大文字数)
	 * @param value     確認対象文字列
	 * @param maxLength 最大文字数
	 * @param name      確認対象名称
	 * @param id        確認対象フィールドID
	 * @throws MospException
	 */
	protected void checkCsvLength(String value, int maxLength, String name, int idx) throws MospException {
		String regex = ".{0," + String.valueOf(maxLength) + "}";
		if (!ValidateUtility.chkRegex(regex, value)) {
			String errMsg = MospUtility.getMessage(msg, "WC0021", name + MospConst.DATA_SEPARATOR + String.valueOf(maxLength) + MospConst.DATA_SEPARATOR + idx);
			addErrMessage(errMsg);
			isAddRegistData = false;
		}
	}
	
	/**
	 * ヘッダ項目設定
	 */
	protected String[][] getBonusUploadHeadItem() {
		ArrayList<String> listItem = new ArrayList<String>();
		listItem.add(BonusConst.UP_K_CODE);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_01);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_02);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_03);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_04);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_05);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_06);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_07);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_08);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_09);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_10);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_11);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_12);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_13);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_14);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_15);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_16);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_17);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_18);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_19);
		listItem.add(BonusConst.UP_BONUS_ALLOWANCE_20);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_01);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_02);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_03);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_04);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_05);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_06);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_07);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_08);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_09);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_10);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_11);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_12);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_13);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_14);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_15);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_16);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_17);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_18);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_19);
		listItem.add(BonusConst.UP_BONUS_DEDUCTION_20);
		String[][] aryItem = new String[listItem.size()][2];
		for (int i = 0; i < listItem.size(); i++) {
			aryItem[i][0] = String.valueOf(i);
			aryItem[i][1] = listItem.get(i);
		}
		return aryItem;
	}
		
	/**
	 * 法定控除計算(賞与データ更新)
	 * @param vo 対象VO
	 * @throws Exception
	 */
	private void calcLegalDeduction(BonusCalcVo vo) throws Exception {
		// DAO準備
		PhBounsDao phBounsDao = new PhBounsDao(cfg, cmd, aspUser, user, conn);
		phBounsDao.initDao(cfg, cmd, aspUser, user, conn);
		// 社会保険料率設定
		setSocInsRatio();
		// 計算対象賞与データ取得(支給日で検索)
		List<PhBounsDto> listBonus = phBounsDao.findForPayday(vo.getPayday());
		// 計算対象賞与データ存在確認
		if (listBonus.size() == 0) {
			addErrMessage(MospUtility.getMessage(msg, "IC0011", ""));
			return;
		}
		// 法定控除計算(賞与データ毎)
		for (int i = 0; i < listBonus.size(); i++) {
			// 計算対象設定
						phBounsDto = listBonus.get(i);
			// 社員コード設定
						kCode = phBounsDto.getKCode();
			// 計算年月を対象年月に設定
			targetMonth = vo.getCalcDate();
			// 法定控除計算
			calcBonusLegalDeduction();
			// 更新
			phBounsDao.update(phBounsDto, false);
		}
		// コミット
		commit();
		// メッセージ設定
		vo.setMessage(MospUtility.getMessage(msg, BonusConst.MSG_FINISH, BonusConst.NAM_TAX_CALCULATE));
	}

	/**
	 * 確定計算(賞与データ更新)
	 * @param vo 対象VO
	 * @throws Exception 
	 */	public void calcTotal(BonusCalcVo vo) throws Exception {
		 PhBounsDto dto;
		 // 日付の妥当性確認
		if (vo.checkTargetDate()) {
			// DAO準備
			PhBounsDao phBounsDao = new PhBounsDao(cfg, cmd, aspUser, user, conn);
			phBounsDao.initDao(cfg, cmd, aspUser, user, conn);
			// 支給日で賞与ヘッダーテーブル(PH_BONUS)を検索
			List<PhBounsDto > listPhBonus = phBounsDao.findForCalcday( vo.getPayday() );
			if (listPhBonus.size() != 0) {
					// 支払日に計算日設定
				for (Iterator<PhBounsDto> it = listPhBonus.iterator(); it.hasNext();) {
					dto = it.next();
					dto.setCalcMonth(vo.getPayday());
					dto.setPayDate(vo.getPayday());
					phBounsDao.deleteForCalcdate(dto.getKCode(),vo.getPayday());
					phBounsDao.insert(dto, true);
					commit();
				}
				vo.setMessage(MospUtility.getMessage(msg, BonusConst.MSG_APPLY,""));
			}else{
				// ヘッダ重複エラー
				String errMessage = MospUtility.getMessage(msg, "WP2002", "");
				addErrMessage(errMessage);
				throw new MospException(MospConst.EX_PARAMS_INVALID);
				
			}
		}		
	 }
	/**
	 * 支給日のデータを削除
	 * @param vo 対象VO
	 * @throws Exception
	 */
	public void delete(BonusCalcVo vo) throws Exception {
		// 日付の妥当性確認
		if (vo.checkTargetDate()) {
			// DAO準備
			PhBounsDao phBounsDao = new PhBounsDao(cfg, cmd, aspUser, user, conn);
			phBounsDao.initDao(cfg, cmd, aspUser, user, conn);
			// 支給日で賞与ヘッダーテーブル(PH_BONUS)を検索
			List<PhBounsDto > listPhBonus = phBounsDao.findForCalcday( vo.getPayday() );
			if (listPhBonus.size() != 0) {
				// 計算日でデータを削除
				phBounsDao.deleteForCalcdateAll(vo.getPayday());
				// コミット
				commit();
				// メッセージ設定
				vo.setMessage(MospUtility.getMessage(msg, MospConst.MSG_DELETE,""));
				// 支給日（計算年月）で賞与明細支給部テーブル(PD_BON_ALLOWANCE)を削除
				deleteAllowamce(vo.getPayday());
				// 支給日（計算年月）で賞与明細控除部テーブル(PD_BON_DEDUCTION)を削除
				deleteDeduction(vo.getPayday());
				// 支給日（計算年月）で賞与明細合計部テーブル(PD_BON_SUM)を削除
				deleteSum(vo.getPayday());
				// 支給日（計算年月）で中間テーブル(PI_BONUS)を削除
				deletePiBonus(vo.getPayday());
			} else {
				PiBonusDao piBonusDao = new PiBonusDao(cfg, cmd, aspUser, user, conn);
				piBonusDao.initDao(cfg, cmd, aspUser, user, conn);
				// 支給日で賞与ヘッダーテーブル(PH_BONUS)を検索
				List< DetailTimeDto > listPiBonus = piBonusDao.find( vo.getPayday() );
				if(listPiBonus.size() != 0 ){
					vo.setMessage(MospUtility.getMessage(msg, MospConst.MSG_DELETE,""));
					piBonusDao.delete( vo.getPayday() );
					// コミット
					commit();
				}else{
					// 対象データが存在しない
					String errMessage = MospUtility.getMessage(msg, "IC0011", "");
					addErrMessage(errMessage);
					throw new MospException(MospConst.EX_PARAMS_INVALID);
				}

			}
		}
	}
	
	/**
	 * 人事基本履歴情報取得(エラーなし)
	 * @param kCode 情報取得対象社員コード
	 * @param date  対象年月
	 * @return 人事基本情報(Employee)
	 * @throws SQLException
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 * @throws MospException
	 */
	protected Employee getUpdateBasisHistory(
			String kCode, Date date
	) throws SQLException, NoSuchFieldException, IllegalAccessException {
		Employee employee = getEmployee( kCode );
		return employee;
	}


/**---- ↓↓各BonusActionで実装（共通化したい） :: Base ----**/

	/**
	 * 事業所コード取得(M_KAISYA)
	 * @return 所属事業所コード
	 * @throws SQLException
	 * @throws NoSuchFieldException
	 * @throws IllegalAccessException
	 */
	protected String[][] getStationArrayAll() throws SQLException, NoSuchFieldException, IllegalAccessException {
		// DAOの準備
		MKaisyaDao dao = new MKaisyaDao();
		dao.initDao(cfg, cmd, aspUser, user, conn);
		// 情報の取得
		List<MKaisyaDto> listStation = dao.findAll();
		// 配列の作成
		String[][] aryStation = new String[listStation.size()][2];
		for (int i = 0; i < listStation.size(); i++) {
			aryStation[i][0] = listStation.get(i).getId();
			aryStation[i][1] = listStation.get(i).getKaiMei();
		}
		return aryStation;
	}
	
	/**
	 * 計算月取得
	 * @return 計算月
	 * @throws Exception
	 */
	protected Date getCalcCurrent() throws Exception {
		// DAO準備
		PtCurrentDao dao = new PtCurrentDao(cfg, cmd, aspUser, user, conn);
		dao.initDao(cfg, cmd, aspUser, user, conn);
		PtCurrentDto dto = dao.findForKey(
							PayrollConst.OFFICE_ALL,
							PayrollConst.CALC_BONUS,
							getCurrent(PayrollConst.OFFICE_ALL, PayrollConst.CALC_PAYMENT));
		if (dto != null) {
			return dto.getCalcDate();
		}
		return new Date();
	}

	/**
	 * 年プルダウン取得(前後はBonusConstで指定)
	 * @param year 基準年
	 * @return 年プルダウン用文字列配列
	 */
	protected String[][] getYearArrayLong(int year) {
		return getYearArray(year,
				BonusConst.TIME_YEAR_FORMER, BonusConst.TIME_YEAR_FURTHER);
	}
	
	/**
	 * 年プルダウン取得
	 * @param year 基準年
	 * @return 年プルダウン用文字列配列
	 */
	protected String[][] getYearArray(int year, int former, int further) {
		String[][] aryYear = new String[former + further + 1][2];
		int i = 0;
		int index = 0;
		for (i = year - former; i < year; i++) {
			aryYear[index][0] = String.valueOf(i);
			aryYear[index][1] = String.valueOf(i);
			index++;
		}
		aryYear[index][0] = String.valueOf(year);
		aryYear[index][1] = String.valueOf(year);
		index++;
		for (i = year + 1; i <= year + further; i ++) { 
			aryYear[index][0] = String.valueOf(i);
			aryYear[index][1] = String.valueOf(i);
			index++;
		}
		return aryYear;
	}
	
	/**
	 * 計算月確認(計算年月以降)
	 * @param date 対象年月
	 * @throws Exception 
	 */
	protected void checkCalcCurrentAfter(Date date) throws Exception {
		if (getCalcCurrent().compareTo(date) > 0) {
			String errMessage = MospUtility.getMessage(msg, BonusConst.MSG_NOT_CURRENT, "");
			addErrMessage(errMessage);
			throw new MospException(BonusConst.EX_NOT_CURRENT, errMessage);
		}
		
/*** 暫定首絞め ***/
		else{
			String errMessage = MospUtility.getMessage(msg, "WU1120", "");
			addErrMessage(errMessage);
			throw new MospException(BonusConst.EX_NOT_CURRENT, errMessage);
		}
	}
	
	/**
	 * 年開始日取得
	 * @param year 年
	 * @return 年最終日
	 */
	protected Date getStartDateOfYear(int year) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, year);
		cal.set(Calendar.MONTH, 0);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}
	
	/**
	 * 年最終日取得
	 * @param year 年
	 * @return 年最終日
	 */
	protected Date getLastDateOfYear(int year) {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, year + 1);
		cal.set(Calendar.MONTH, 0);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.DAY_OF_MONTH, -1);
		return cal.getTime();
	}

	/**
	 * 社会保険個人負担料率取得
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public void setSocInsRatio(
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// 初期化
		ratioHealthPremium   = 0;	// 健康保険料率 
		ratioNursingPremium  = 0;	// 介護保険料率
		ratioPensionPremium  = 0;	// 厚生年金料率
		ratioFundPremium     = 0;	// 厚生年金基金料率
		ratioUnemployPremium = 0;	// 雇用保険料率
		fundParticipation    = LegalConst.FUND_PARTICIPATION_OFF;	// 厚生年金基金加入区分
		// 会社情報取得
		CmCompanyDao cmCompanyDao = new CmCompanyDao();
		cmCompanyDao.initDao(cfg, cmd, aspUser, user, conn);
		CmCompanyDto cmCompanyDto = cmCompanyDao.findAll();
		if (cmCompanyDto == null) {
			return;
		}
		// 社会保険個人負担料率取得
		ratioHealthPremium   = cmCompanyDto.getHealthPremiumRate  ();	// 健康保険料率  
		ratioNursingPremium  = cmCompanyDto.getNursingPremiumRate ();	// 介護保険料率
		ratioPensionPremium  = cmCompanyDto.getPensionPremiumRate ();	// 厚生年金料率
		ratioFundPremium     = cmCompanyDto.getFundPremiumRate    ();	// 厚生年金基金料率
		ratioUnemployPremium = cmCompanyDto.getUnemployPremiumRate();	// 雇用保険料率
		fundParticipation    = cmCompanyDto.getFundParticipation  ();	// 厚生年金基金加入区分
	}

	/**
	 * 法定控除計算(社会保険料及び税金の設定)(賞与)
	 * @throws Exception
	 */
	protected void calcBonusLegalDeduction() throws Exception {
		// 明細設定による金額計算(非課税額及び社会保険非対象額)
		// 社会保険対象額設定
		// 社会保険料設定
		setBonusSocialInsurance();
		// 所得税設定
		setBonusIncomeTax();
		// 住民税設定は行わない
		// 確定項目初期化
		phBounsDto.setAllowanceTotal(0);
		phBounsDto.setDeductionTotal(0);
		phBounsDto.setPaymentTotal  (0);
//		phBounsDto.setBank1PayAmount(0);
//		phBounsDto.setBank2PayAmount(0);
//		phBounsDto.setCashPayAmount (0);
//		// ステータス設定(税金計算済み)
//		phBounsDto.setCalcStatus(BonusConst.CALC_STATUS_TAX);
	}

	/**
	 * 社会保険料設定(賞与)
	 * @throws NoSuchFieldException 
	 */
	private void setBonusSocialInsurance() throws SQLException, IllegalAccessException, NoSuchFieldException {
		int basisHealth   = 0;	// 健康保険計算基準額
		int basisNursing  = 0;	// 介護保険計算基準額
		int basisPension  = 0;	// 厚生年金計算基準額
		//int basisFund     = 0;	// 厚生年金基金計算基準額
		int basisUnemploy = 0;	// 雇用保険計算基準額
		
		int health   = 0;	// 健康保険料
		int nursing  = 0;	// 介護保険料
		int pension  = 0;	// 厚生年金料
		//int fund     = 0;	// 厚生年金基金料
		int unemploy = 0;	// 雇用保険料
		
		// 総支給額を取得(1000円未満切り捨て)
//		int bonusAllowance = (int)round(phBounsDto.calcBonusAllowanceTotal(), - 3, BigDecimal.ROUND_HALF_DOWN); 
		// 年度累計総支給額(今回の支給を含まない)
		int totalBonusAmount = (int)round(calcAmountToYearTotal(), - 3, BigDecimal.ROUND_HALF_DOWN);
		// 対象月支給額取得
		int targetMonthBonusAmount = (int)round(calcTargetMonthBonusAmount(), - 3, BigDecimal.ROUND_HALF_DOWN);
		
		// 人事給与情報(保険)取得
		CmSocInsuranceDao dao = new CmSocInsuranceDao();
		dao.initDao(cfg, cmd, aspUser, user, conn);
		CmSocInsuranceDto dto = dao.findForEmployee(kCode, targetMonth);
		// 社会保険料計算
		if (dto != null) {
			// 健康保険計算基準額取得
			basisHealth   = LegalUtility.getBasisHealthPayment(totalBonusAmount, targetMonthBonusAmount);
			// 介護保険計算基準額取得
			basisNursing  =  LegalUtility.getBasisNursingPayment(totalBonusAmount, targetMonthBonusAmount);
			// 厚生年金計算基準額取得
			basisPension  =  LegalUtility.getBasisPensionPayment(targetMonthBonusAmount);
/*			// 厚生年金基金計算基準額取得
			basisFund     =  LegalUtility.getBasisFundPayment(targetMonthBonusAmount);
*/			// 雇用保険計算基準額取得
//			basisUnemploy =  LegalUtility.getBasisUnemployPayment(bonusAllowance);
			// 雇保対象基準額設定
//			pdBonusDto.setEmpInsBasic(basisUnemploy * 1000);

			// 健康保険料計算(健保賞与区分が「計算する」の場合)
			if(dto.getBonusHealthType().equals(LegalConst.BONUS_HEALTH_CALC)) {
				health = LegalUtility.calcPremium(basisHealth,
						ratioHealthPremium, BigDecimal.ROUND_HALF_UP);
			}
			// 健康保険料計算(健保賞与区分が「計算する」の場合)
			if (dto.getBonusHealthType().equals(LegalConst.BONUS_HEALTH_CALC)) {
				// 介護保険料計算(介護保険区分「対象」「対象(自動)(第２号被保険者のみ)」の場合)
				if(chkCalcNursingInsType(dto.getNursingInsType())) {
					nursing = LegalUtility.calcPremium(basisNursing,
							ratioNursingPremium, BigDecimal.ROUND_HALF_UP);
				}
			}
			// 厚生年金料計算(厚年賞与区分が「計算する」の場合)
			if(dto.getBonusPensionType().equals(LegalConst.BONUS_PENSION_CALC)) {
				pension = LegalUtility.calcPremium(basisPension,
						ratioPensionPremium, BigDecimal.ROUND_HALF_UP);
			}
/*			// 厚生年金基金料計算(厚生年金基金加入区分が「加入」の場合)
			if (fundParticipation.equals(LegalConst.FUND_PARTICIPATION_ON)) {
				// 厚生年金料計算(厚年賞与区分が「計算する」の場合)
				if(dto.getBonusPensionType().equals(LegalConst.BONUS_PENSION_CALC)) {
					fund = LegalUtility.calcPremium(basisFund,
							ratioFundPremium, BigDecimal.ROUND_HALF_UP);
				}
			}
*/			// 雇用保険料計算(雇用保険区分が「計算する」の場合)
			if (dto.getCalcUnemployType().equals(LegalConst.CALC_UNEMPLOY)) {
				unemploy = LegalUtility.calcPremium(
						basisUnemploy,
						ratioUnemployPremium, BigDecimal.ROUND_HALF_DOWN);
			}
			
			// 対象月に複数支給されている場合は以前の保険料を差し引いて金額を設定
			if (0 < befSocInsPremium) {
				health   -= befHealthPremium  ;
				nursing  -= befNursingPremium ;
				pension  -= befPensionPremium ;
			//	fund     -= befFundPremium    ;
			}
			
		}
		// 社会保険料設定
//		phBounsDto.setHealthInsurance  (health  );
//		phBounsDto.setNursingInsurance (nursing );
//		phBounsDto.setPensionInsurance (pension );
//		phBounsDto.setEmployeeInsurance(unemploy);
		// 年度累計額設定
//		phBounsDto.setTotalYearBonus(totalBonusAmount);
	}

	/**
	 * 丸め処理(四捨五入等)
	 * @param value 対象値
	 * @param scale 対象位置(少数点以下：0で整数)
	 * @param mode  方法
	 * @throws Exception 
	 */
	protected double round(double value, int scale, int mode) {
		BigDecimal bd;
		if (scale >= 0) {
			bd = new BigDecimal(value);
			return bd.setScale(scale, mode).doubleValue();
		}
		int    minusScale = scale * -1;
		double dividing   = 1         ;
		for (int i = 0; i < minusScale; i++) {
			dividing *= 10;
		}
		bd = new BigDecimal(value / dividing);
		return bd.setScale(0, mode).doubleValue() * dividing;
	}

	/**
	 * 年度累計賞与額取得
	 * @param vo
	 * @return 年度累計賞与額取得
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public int calcAmountToYearTotal() throws SQLException, IllegalAccessException {
		int allBonusAllowance = 0;
		List <PdBonusDto> listBonusDto;
		PdBonusDao dao = new PdBonusDao();
		dao.initDao(cfg, cmd, aspUser, user, conn);
		// 検索準備
		Date startDate;
		Date endDate;
		Calendar cal = Calendar.getInstance();
		// 対象年月設定
		cal.setTime(targetMonth);
		// 年度 = 対象年4月1日〜翌年3月最終日
		// 検索対象開始日取得
		cal.set(Calendar.MONTH, Calendar.APRIL);
		cal.set(Calendar.DATE, 1);
		startDate = cal.getTime();
		// 検索対象最終日取得
		cal.set(Calendar.MONTH, Calendar.MARCH);
		cal.set(Calendar.DATE, cal.getMaximum(Calendar.DATE));
		cal.add(Calendar.YEAR, 1);
		endDate = cal.getTime();
		// 今年度総支給額取得
		listBonusDto = dao.selectTheYear(kCode,startDate, endDate);
		if (listBonusDto.size() != 0) {
			for (int i = 0; i < listBonusDto.size(); i ++) {
//				PhBounsDto PdBonusDto = listBonusDto.get(i);
//				// 今回支給分は追加しない。
//				if (PdBonusDto.getPayday().compareTo(this.pdBonusDto.getPayday()) != 0) {
//					// 今回の支給日より以降は追加しない
//					if (PdBonusDto.getPayday().compareTo(this.pdBonusDto.getPayday()) < 0) {
//						// 今までの総支給額を取得
//						allBonusAllowance += PdBonusDto.getAllowanceTotal();
//					}
//				}
			}
		}
		return allBonusAllowance;
	}

	/**
	 * 所得税設定(賞与)
	 * @throws Exception 
	 */
	public void setBonusIncomeTax() throws Exception {
//		// 人事給与情報(所得税)取得
//		CmIncomeTaxDao cmIncomeTaxDao = new CmIncomeTaxDao();
//		cmIncomeTaxDao.initDao(cfg, cmd, aspUser, user, conn);
//		// 給与情報取得準備
//		PdPaymentDao PdPaymentDao = new PdPaymentDao();
//		PdPaymentDao.initDao(cfg, cmd, aspUser, user, conn);
//		// 給与支払間隔取得(翌月払いなら1)
//		String interval = getConfValue(BonusConst.CONF_ID_PAY_ITBL, BonusConst.CONF_CD_ADJUST_PAY);
//		// 対象年月の前月の日付を取得
//		befTargetMonth = addMonth(targetMonth, -1 - Integer.parseInt(interval));
//		CmIncomeTaxDto dto = cmIncomeTaxDao.findForEmployeeDate(kCode, befTargetMonth);
//		// 人事給与情報(所得税)が存在しない場合は0を設定
//		if (dto == null) {
//			// 所得税額計算及び設定
//			pdBonusDto.setIncomeTax(0);
//			// 扶養等の数設定
//			pdBonusDto.setDependentCount(0);
//			return;
//		}
//		// 扶養等の数取得
//		int dependent = dto.getDependentCount();
//		// 課税区分取得
//		String incomeTaxType = dto.getIncomeTaxType();
//		pdPaymentDto = PdPaymentDao.findForUpdate(kCode, befTargetMonth);
//		// 社会保険料等控除後の給与等の金額取得
//		int payTaxation = pdPaymentDto != null ? pdPaymentDto.getAllowanceTotal() - pdPaymentDto.calcSocialInsurance() : 0;
//		// 課税対象額(賞与)
//		int bonusTaxation = pdBonusDto.calcBonusAllowanceTotal() - pdBonusDto.calcBonusSocialInsurance();
//		// 税率取得用配列取得
//		double[] aryIncomeTax = LegalUtility.getBonIncomeTaxArray(incomeTaxType, dependent);
//		// 所得税率設定
//		pdBonusDto.setIncomeRate((float)LegalUtility.getBonusIncomeRate(aryIncomeTax, payTaxation));
//		
//		// 所得税額計算及び設定
//		pdBonusDto.setIncomeTax(
//				LegalUtility.calcBonusIncomeTax(incomeTaxType, payTaxation, bonusTaxation, dependent));
//		// 扶養等の数設定
//		pdBonusDto.setDependentCount(dependent);
//		// 前月の給与等の金額設定
//		pdBonusDto.setLastMonthTaxation(payTaxation);
	}

	/**
	 * 月操作
	 * @param date   操作日付
	 * @param amount 増減月数
	 * @return 操作後日付
	 */
	protected Date addMonth(Date date, int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.MONTH, amount);
		return cal.getTime();
	}

	/**
	 * 対象月支給額取得
	 * @return 対象月総支給額
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException 
	 * @throws NoSuchFieldException
	 */
	public int calcTargetMonthBonusAmount() throws SQLException, IllegalAccessException, NoSuchFieldException {
//		int targetMonthBonusAmount = 0;
//		List <PhBounsDto> listBonusDto;
//		PhBounsDao dao = new PhBounsDao(cfg, cmd, aspUser, user, conn);
//		dao.initDao(cfg, cmd, aspUser, user, conn);
//		// 今年度総支給額取得
//		listBonusDto = dao.findForCalcDate(targetMonth, kCode);
//		if (listBonusDto.size() != 0) {
//			for (int i = 0; i < listBonusDto.size(); i ++) {
//				PhBounsDto PhBounsDto = listBonusDto.get(i);
//				// 対象年月日以前の社会保険料取得
//				if (this.phBounsDto.getPayday().compareTo(PhBounsDto.getPayday()) > 0) {
//					befHealthPremium   = PhBounsDto.getHealthInsurance  ();
//					befNursingPremium  = PhBounsDto.getNursingInsurance ();
//					befPensionPremium  = PhBounsDto.getPensionInsurance ();
//					// 社会保険料合計(比較用)
//					befSocInsPremium   = befHealthPremium + befNursingPremium + befPensionPremium + befFundPremium;
//				}
//				// 対象年月の総支給額を設定
//				targetMonthBonusAmount += PhBounsDto.calcBonusAllowanceTotal();
//			}
//		}
//		return targetMonthBonusAmount;
		return 0;
	}

	/**
	 * 介護保険計算対象確認
	 * @param nursingInsType　介護保険区分
	 * @return　計算対象確認フラグ
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public boolean chkCalcNursingInsType(String nursingInsType) throws SQLException, IllegalAccessException, NoSuchFieldException {
		boolean isCalc = false;
		// 介護保険料計算(介護保険区分が「対象(自動)」の場合)
		if (nursingInsType.equals(LegalConst.NURSING_INS_AUTO)) {
			// 年齢で計算対象か判断
			isCalc = chkCalcNusingInsAuto();
		}
		// 介護保険料計算(介護保険区分が「対象」の場合)
		if (nursingInsType.equals(LegalConst.NURSING_INS_MANUAL)) {
			isCalc = true;
		}
		return isCalc;
	}

	/**
	 * 介護保険計算対象確認(区分：計算対象(自動))
	 * @return 計算対象確認フラグ
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	public boolean chkCalcNusingInsAuto() throws SQLException, IllegalAccessException, NoSuchFieldException {
		// 計算対象確認フラグ
		boolean isCalc = false;
		// DAO準備
		PmExtraDao PmExtraDao = new PmExtraDao();
		PmExtraDao.initDao(cfg, cmd, aspUser, user, conn);
		// 年齢取得準備
		int age = 0;
		// 対象年月最終日+1日取得
		Date targetDate = addDay(getEndDate(targetMonth), 1);
		// 管理情報取得
		PmExtraDto PmExtraDto = PmExtraDao.findForKey(kCode);
		// 管理データが存在しない社員は対象外
		if (PmExtraDto == null) {
			isCalc = false;
			return isCalc;
		}
		// 誕生日が設定されていない社員は対象外
		if (PmExtraDto.getBirthDate() == null) {
			isCalc = false;
			return isCalc;
		}
		// 年齢取得(対象年月最終日+1日で判断)
		age = getAge(PmExtraDto.getBirthDate(), targetDate);
		// 40歳以上65歳未満は計算対象
		if (age >= 40) {
			isCalc = true;
		}
		if (age >= 65 ) {
			isCalc = false;
		}
		return isCalc;
		
	}

	/**
	 * 日操作
	 * @param date   操作日付
	 * @param amount 増減日数
	 * @return 操作後日付
	 */
	protected Date addDay(Date date, int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.DAY_OF_MONTH, amount);
		return cal.getTime();
	}
	
	/**
	 * 年齢取得
	 * @param birthDay
	 * @param targetDay
	 * @return 年齢
	 */
	protected int getAge(Date birthDay, Date targetDay) {
		// 取得準備
		int age = 0;
		// 年齢取得
		age = MospUtility.getYear(targetDay) - MospUtility.getYear(birthDay);
		// 対象年月が誕生年よりなら0
		if (age < 0) {
			age = 0;
			return age;
		}
		// 対象月が誕生月以降
		if (MospUtility.getMonth(targetDay) - MospUtility.getMonth(birthDay) > 0) {
			return age;
		} else if (MospUtility.getMonth(targetDay) - MospUtility.getMonth(birthDay) == 0) {
			
			// 対象日が誕生日以降
			if (MospUtility.getDay(targetDay) - MospUtility.getDay(birthDay) > 0) {
				return age;
			} else if (MospUtility.getDay(targetDay) - MospUtility.getDay(birthDay) == 0) {
				// 対象日が誕生日
				return age;
			} else {
				// 対象日が誕生日以前
				return age - 1;
			}
		} else {
			// 対象月が誕生月以前
			return age - 1;
		}
	}

	/**
	 * 集計終了日取得
	 * @param targetMonth 対象月
	 * @return 集計終了日
	 * @throws NoSuchFieldException 
	 * @throws IllegalAccessException 
	 * @throws SQLException 
	 */
	protected Date getEndDate(
			Date targetMonth
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// カレンダー取得
		Calendar cal = Calendar.getInstance();
		cal.setTime(targetMonth);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		// 締め日取得
		String[][] close = getConfArray(BonusConst.CONF_ID_CLOSE);
		int minusMonth = Integer.parseInt(close[0][0]);
		int closeDay   = Integer.parseInt(close[0][1]);
		// 集計終了日計算
		cal.roll(Calendar.MONTH, minusMonth * -1);
		if (closeDay != 0) {
			cal.roll(Calendar.MONTH, 1);
			cal.set(Calendar.DAY_OF_MONTH, closeDay);
		} else {
			cal.roll(Calendar.DAY_OF_MONTH, -1);
		}
		return cal.getTime();
	}

	/**
	 * 設定リスト取得(UM_CONF)
	 * @param confId 設定区分
	 * @return 設定リスト
	 * @throws NoSuchFieldException 
	 * @throws IllegalAccessException 
	 * @throws SQLException 
	 */
	protected String[][] getConfArray(
			String confId
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// DAOの宣言
		MosPConfDao dao = new MosPConfDao();
		dao.initDao(cfg, cmd, aspUser, user, conn);
		// データの取得
		List<MosPConfDto> list = dao.findForCondition(confId);
		// データの整形
		String[][] array = new String[list.size()][2];
		for (int i = 0; i < list.size(); i++) {
			MosPConfDto dto = list.get(i);
			array[i][0] = dto.getConfCode();
			array[i][1] = dto.getConfValue();
		}
		return array;
	}

	/**
	 * 確定計算(合計及び銀行振分)(賞与)
	 * @throws Exception
	 */
	protected void calcBonusTotal() throws Exception {
//		// 所得税設定
//		setBonusIncomeTax();
//		// 確定計算準備
//		int allowance = 0;	// 総支給額
//		int deduction = 0;	// 総控除額
//		int total     = 0;	// 差引支給額
//		// 総支給額取得
//		allowance = phBounsDto.calcBonusAllowanceTotal();
//		// 総控除額取得
//		deduction = phBounsDto.calcBonusDeductionTotal();
//		// 差引支給額計算(総支給額-総控除額)
//		total = allowance - deduction; 
//		// 銀行振分
//		divideAmongAccounts(total, BonusConst.ALLOWANCE_TYPE_BONUS);
//		// 確定情報設定
//		phBounsDto.setAllowanceTotal(allowance  );
//		phBounsDto.setDeductionTotal(deduction  );
//		phBounsDto.setPaymentTotal  (total      );
//		// 差引支給額がマイナスの場合は銀行1、銀行2、現金の値を0に設定する
//		if (total < 0) {
//			phBounsDto.setBank1PayAmount(0);
//			phBounsDto.setBank2PayAmount(0);
//			phBounsDto.setCashPayAmount (0);
//		} else {
//			phBounsDto.setBank1PayAmount(bank1Amount);
//			phBounsDto.setBank2PayAmount(bank2Amount);
//			phBounsDto.setCashPayAmount (cashAmount );
//		}
//		// ステータス設定(確定処理済み)
//		phBounsDto.setCalcStatus(BonusConst.CALC_STATUS_FIX);
	}

	/**
	 * 明細設定を取得する。(賞与)<br>
	 * @return 明細設定
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	protected Map<String, PsDetailDto> getMapBonusDetail(
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// 明細設定取得
//		if (mapBonusDetailAll == null) {
//			PsDetailDao PsDetailDao = new PsDetailDao();
//			PsDetailDao.initDao(cfg, cmd, aspUser, user, conn);
//			mapBonusDetailAll = PsDetailDao.findBonusAsMap();
//		}
		// 明細設定キー文字列
//		String key = phBounsDto.getEmploymentType() + BonusConst.KEY_SEPARATOR;
//		// キー+職位コードで明細設定を取得
//		Map<String, PsDetailDto> mapDetail =
//			mapBonusDetailAll.get(key + phBounsDto.getPositionCode());
//		// キー+職位コードで明細設定が見つからない場合
//		if (mapDetail == null) {
//			// キー+設定なし文字列で明細設定を取得
//			mapDetail = mapBonusDetailAll.get(key + BonusConst.SETTING_NONE);
//		}
//	return mapDetail;
	return null;
	}

	/**
	 * 銀行振分
	 * @param total         差引支給額
	 * @param allowanceType 支給区分(1：給与、2：賞与)
	 * @throws SQLException
	 * @throws IllegalAccessException
	 * @throws NoSuchFieldException
	 */
	protected void divideAmongAccounts(
			int total, String allowanceType
	) throws SQLException, IllegalAccessException, NoSuchFieldException {
		// 初期化
		bank1Amount = 0;
		bank2Amount = 0;
		cashAmount  = 0;
		// DAO準備
		CmPaymentMeansDao dao = new CmPaymentMeansDao();
		dao.initDao(cfg, cmd, aspUser, user, conn);
		// 支給方法情報取得(支給方法連番順)
		List<CmPaymentMeansDto> list;
		list = dao.findForEmployeeAllowance(kCode, allowanceType);
		// 支給情報毎の処理
		int currentAmount = total;
		for (Iterator<CmPaymentMeansDto> it = list.iterator(); it.hasNext();) {
			CmPaymentMeansDto dto = it.next();
			// 振分金額取得
			int amount = getdividedAmount(dto, total, currentAmount);
			// 振分金額設定
			if (dto.getPaymentMeans().equals(BonusConst.PAYMENT_MEANS_ACCOUNT)) {
				// 振込の場合
				if (dto.getMeansSequence() == BonusConst.PAYMENT_MEANS_SEQ_1
						|| dto.getMeansSequence() == BonusConst.PAYMENT_MEANS_SEQ_3) {
					bank1Amount += amount;
				} else {
					bank2Amount += amount;
				}
			} else {
				// 振込以外(現金)の場合
				cashAmount += amount;
			}
			// 残額計算
			currentAmount -= amount;
		}
		// 残額を現金に設定
		cashAmount += currentAmount;
	}

	/**
	 * 振分支給額取得
	 * @param dto           支給方法DTO
	 * @param totalAmount   振分総額
	 * @param currentAmount 振分元金額(残額)
	 */
	private int getdividedAmount(
			CmPaymentMeansDto dto, int totalAmount, int currentAmount
	) {
		// 支給情報が設定されていない場合
		if (dto == null) {
			return 0;
		}
		// 全額の場合
		if (dto.getAmountType().equals(BonusConst.AMOUNT_TYPE_ALL)) {
			return currentAmount;
		}
		// 残額の場合
		if (dto.getAmountType().equals(BonusConst.AMOUNT_TYPE_REST)) {
			return currentAmount;
		}
		// 振分支給額
		int dividingAmount = 0;
		// 金額区分毎の処理
		if (dto.getAmountType().equals(BonusConst.AMOUNT_TYPE_AMOUNT)) {
			// 定額の場合
			dividingAmount = dto.getFixedAmount();
		} else if (dto.getAmountType().equals(BonusConst.AMOUNT_TYPE_RATIO)) {
			// 定率の場合
			// 振分総額 * 振分率 / 100
			dividingAmount = (int)round(
					dto.getFixedAmount() / 100 * totalAmount,
					0, BigDecimal.ROUND_HALF_UP);
		}
		// 振分元金額以上の場合
		if (dividingAmount > currentAmount) {
			dividingAmount = currentAmount;
		}
		return dividingAmount;
	}
	

	/**
	 * 支給日（計算年月）で賞与明細支給部テーブル(PD_BON_ALLOWANCE)を削除
	 * @param getPayday     支給日付
	 */
	private void deleteAllowamce(Date  getPayday) throws Exception {
		// DAO準備
		PdBonAllowanceDao pdBonAllowanceDao = new PdBonAllowanceDao(cfg, cmd, aspUser, user, conn);
		pdBonAllowanceDao.initDao(cfg, cmd, aspUser, user, conn);
		pdBonAllowanceDao.delete( getPayday );
		// コミット
		commit();
	}
	/**
	* 支給日（計算年月）で賞与明細控除部テーブル(PD_BON_DEDUCTION)を削除
	 * @param getPayday     支給日付
	 */
	private void deleteDeduction(Date  getPayday) throws Exception {
		PdBonSumDao pdBonSumDao = new PdBonSumDao(cfg, cmd, aspUser, user, conn);
		pdBonSumDao.initDao(cfg, cmd, aspUser, user, conn);
		pdBonSumDao.delete( getPayday );
		// コミット
		commit();
}
	
	/**
	* 支給日（計算年月）で賞与明細合計部テーブル(PD_BON_SUM)を削除
	 * @param getPayday     支給日付
	 */
	private void deleteSum(Date  getPayday) throws Exception {
		PdBonDeductionDao pdBonDeductionDao = new PdBonDeductionDao(cfg, cmd, aspUser, user, conn);
		pdBonDeductionDao.initDao(cfg, cmd, aspUser, user, conn);
		pdBonDeductionDao.delete( getPayday );
		// コミット
		commit();
	}

	/**
	* 支給日（計算年月）で賞与中間テーブル(PID_BONUS)を削除
	 * @param getPayday     支給日付
	 */
	private void deletePiBonus(Date  getPayday) throws Exception {
		PiBonusDao piBonusDao = new PiBonusDao(cfg, cmd, aspUser, user, conn);
		piBonusDao.initDao(cfg, cmd, aspUser, user, conn);
		piBonusDao.delete( getPayday );
		// コミット
		commit();
	}
	

	/**
	 * 各銀行への振込額及び現金支給額を計算する。
	 * @param employee 社員情報
	 */
	private void calcDivideAccounts(Employee employee) {
		// 支給方法情報取得(支給方法連番順)
		List<CmPaymentMeansDto> list = employee.getPaymentMeans();
		// 差引支給額取得
		int total = sumDatail;
		// 支給情報毎の処理
		int currentAmount = total;
		for (Iterator<CmPaymentMeansDto> it = list.iterator(); it.hasNext();) {
			CmPaymentMeansDto dto = it.next();
			// 振分金額取得
			int amount = getdividedAmount(dto, total, currentAmount);
			// 振分金額設定
			if (dto.getPaymentMeans().equals(PayrollConst.PAYMENT_MEANS_ACCOUNT)) {
				// 振込の場合
				if (dto.getMeansSequence() == PayrollConst.PAYMENT_MEANS_SEQ_1
						|| dto.getMeansSequence() == PayrollConst.PAYMENT_MEANS_SEQ_3) {
					bank1Amount += amount;
				} else {
					bank2Amount += amount;
				}
			} else {
				// 振込以外(現金)の場合
				cashAmount += amount;
			}
			// 残額計算
			currentAmount -= amount;
		}
		// 残額を現金に設定
		cashAmount += currentAmount;
		// 給与情報設定
	}



}
