package nc.vo.fbm.pub.util;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import nc.bs.sec.esapi.NCESAPI;
import org.apache.commons.codec.binary.StringUtils;

import nc.bs.dao.DAOException;
import nc.bs.framework.common.NCLocator;
import nc.itf.fbm.gather.IGatherQueryService;
import nc.md.model.MetaDataException;
import nc.md.persist.framework.IMDPersistenceQueryService;
import nc.pub.templet.converter.util.helper.ExceptionUtils;
import nc.pubitf.uapbd.IRateTypePubService;
import nc.vo.bd.notetype.NotetypeVO;
import nc.vo.bd.ratetype.RateTypeVO;
import nc.vo.fbm.impawn.ImpawnVO;
import nc.vo.fbm.pub.constant.FbmBusConstant;
import nc.vo.fbm.pub.constant.FbmFieldConstant;
import nc.vo.fbm.pub.constant.FbmPubConst;
import nc.vo.fbm.register.AggRegisterVO;
import nc.vo.fbm.register.RegisterVO;
import nc.vo.fbm.util.BillRangeTools;
import nc.vo.pub.AggregatedValueObject;
import nc.vo.pub.BusinessException;
import nc.vo.pub.CircularlyAccessibleValueObject;
import nc.vo.pub.ISuperVO;
import nc.vo.pub.SuperVO;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.pattern.model.entity.bill.AbstractBill;
import nc.vo.tmpub.util.StringUtil;
import nc.vo.tmpub.util.TMCurrencyUtil;
import nccloud.itf.fbm.gather.GatherConstant;
import nccloud.itf.fbm.pub.IFBMCommonService4NCC;
import nccloud.pubitf.tmpub.pub.ITMPrecisionServiceForNCC;
import nccloud.vo.tmpub.precison.PrecisionField;

/**
 * @author zhangjp6
 *
 *
 *         2019-3-13
 *
 */
public class FbmCommonTools {
	private static final IFBMCommonService4NCC commonService = NCLocator.getInstance()
			.lookup(IFBMCommonService4NCC.class);

	/**
	 * 根据票据类型主键判断是否是电子票据
	 *
	 * @param pk_fbmBilltype
	 * @return
	 */
	public static boolean isEBill(String pk_fbmBilltype) {
		return commonService.isEBill(pk_fbmBilltype);
	}

	/**
	 * 根据票据pk判断是否是电票大类
	 * 
	 * @param pk_register
	 * @return
	 */
	public static boolean isEBillByPk_register(String pk_register) {
		try {
			return commonService.isEcdNoteClass(pk_register);
		} catch (BusinessException e) {
			ExceptionUtils.wrapException(e);
		}
		return false;
	}

	/**
	 * 根据票据类型主键，判断是不是银行承兑票据
	 *
	 * @param pk_fbmBilltype
	 * @return
	 */
	public static boolean isBankType(String pk_fbmBilltype) {
		return commonService.isBankType(pk_fbmBilltype);
	}

	/**
	 * 根据票据类型主键，判断是不是商业承兑票据
	 *
	 * @param pk_fbmBilltype
	 * @return
	 */
	public static boolean isBusType(String pk_fbmBilltype) {
		return commonService.isBusType(pk_fbmBilltype);
	}

	/**
	 * 根据票据编号获取票据类型主键
	 *
	 * @param fbmbillno
	 * @throws BusinessException
	 */
	public static String getfbmbilltypeByNo(String fbmbillno) throws BusinessException {
		String flag = null;
		String type = null;
		boolean isEbill = true;
		if (fbmbillno.length() == 16) {
			flag = String.valueOf(fbmbillno.charAt(6));
			isEbill = false;
		} else if (fbmbillno.length() == 30) {
			flag = String.valueOf(fbmbillno.charAt(0));
		} else {
			// FbmCommonTools.checkFbmbillnoLength(fbmbillno);
			flag = "5";
		}
		if (isEbill) {
			switch (flag) {
			case "5":
				type = FbmBusConstant.PKNOTETYPE_EBANK;
				break;
			case "6":
				type = FbmBusConstant.PKNOTETYPE_EBUSI;
				break;
			case "1":
				type = FbmBusConstant.PKNOTETYPE_EBANK;
				break;
			case "2":
				type = FbmBusConstant.PKNOTETYPE_EBUSI;
				break;
			default:
				break;
			}
		} else {
			switch (flag) {
			case "5":
				type = FbmBusConstant.PKNOTETYPE_BANK;
				break;
			case "6":
				type = FbmBusConstant.PKNOTETYPE_BUSI;
				break;
			default:
				break;
			}
		}

		return type;
	}

	/**
	 * @description: 根据单据主键判断分包标志的值
	 * @author: baifyun
	 * @date: 2022/10/27 14:45
	 * @param: [registerVO]
	 * @return: void
	 **/
	@SuppressWarnings("unchecked")
	public static void setSubcontractcirculation(RegisterVO registerVO, String fbmbilltype) throws MetaDataException {
		if (!StringUtil.isNull(fbmbilltype)) {
			String condtion = "PK_NOTETYPE = '" + fbmbilltype + "'";
			Collection<NotetypeVO> collection = NCLocator.getInstance().lookup(IMDPersistenceQueryService.class)
					.queryBillOfVOByCond(NotetypeVO.class, condtion, false);
			if (collection != null && collection.size() > 0) {
				NotetypeVO notetypeVO = collection.iterator().next();
				// 电子商业汇票 为 9 商业汇票为2
				if (notetypeVO.getNoteclass() == 9) {
					registerVO.setSubcontractcirculation(UFBoolean.TRUE);
					// 当切换为true时设置对应的默认值
					registerVO.setBillrangestart("000000000000");
					registerVO.setBillrangeend("000000000000");
				} else if (notetypeVO.getNoteclass() == 2) {
					registerVO.setSubcontractcirculation(UFBoolean.FALSE);
				}
			}
		}
	}

	/**
	 * 根据票据类型编码 获取票据类型主键
	 *
	 * @param billtypeCode
	 * @return
	 * @throws Exception
	 */
	public static String getPKBilltypeByCode(String billtypeCode) throws Exception {
		return commonService.getPKBilltypeByCode(billtypeCode);
	}

	/**
	 * 根据票据类型主键 获取票据编码
	 *
	 * @param billtypeCode
	 * @return
	 * @throws DAOException
	 * @throws Exception
	 */
	public static String getCodeByPKBilltype(String pk_billtype) throws BusinessException {

		return commonService.getCodeByPKBilltype(pk_billtype);

	}

	/**
	 * 票据号校验
	 *
	 * @param fbmbillno
	 * @throws BusinessException
	 */
	public static void billNoValication(String fbmbillno) throws BusinessException {

		if (fbmbillno == null || fbmbillno.length() == 0) {
			throw new BusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--187")/* @res "票据编号不为空!" */);
		}

		Pattern blankCheck = Pattern.compile("\\s*|\t|\r|\n");
		if (blankCheck.matcher(fbmbillno) == null) {
			throw new BusinessException(fbmbillno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--186")/* @res "票据编号中有空格!" */);
		}
		// zhangxinah 去掉收票登记导入时票据长度校验
		// checkFbmbillnoLength(fbmbillno);

		// 电子票据号
		if (fbmbillno.length() == 30) {
			eBillNoCheck(fbmbillno);
		}
		// 纸质票据号
		/*
		 * else { paperBillCheck(fbmbillno); }
		 */

	}

	/**
	 * 统一使用的票据号长度校验
	 * 
	 * @param fbmbillno 票据号码
	 * @throws BusinessException
	 */
	public static void checkFbmbillnoLength(String fbmbillno) throws BusinessException {
		/*
		 * if (fbmbillno.length() != 30 && fbmbillno.length() != 16) { throw new
		 * BusinessException(fbmbillno +
		 * nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
		 * "03618nccd--185")
		 *//* @res "票据编号长度不合要求!" *//*
									 * ); }
									 */
	}

	/**
	 * 统一使用的票据号长度校验
	 * 
	 * @param fbmbillno 票据号码
	 * @param isEbill   是否电票
	 * @throws BusinessException
	 */
	public static void checkFbmbillnoByIsEbill(String fbmbillno, boolean isEbill) throws BusinessException {
		/*
		 * if (isEbill) { // 电票 if (fbmbillno != null && fbmbillno.length() != 30) {
		 * throw new BusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
		 * .getStrByID("3618nccd_0", "03618nccd--185")
		 *//* @res "票据号码长度有误，正确输入应为电票30位长度，纸票16位长度！" *//*
														 * ); } } else { // 纸票 if (fbmbillno != null &&
														 * fbmbillno.length() != 16) { throw new
														 * BusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
														 * .getStrByID("3618nccd_0", "03618nccd--185")
														 *//* @res "票据号码长度有误，正确输入应为电票30位长度，纸票16位长度！" *//*
																										 * ); } }
																										 */
	}

	/**
	 * 纸质票据号校验
	 *
	 * @param fbmbillno
	 * @throws BusinessException
	 */
	private static void paperBillCheck(String fbmbillno) throws BusinessException {
		// 30位数字
		Pattern pattern = Pattern.compile("\\d{16}");
		Boolean ismatch = pattern.matcher(fbmbillno).matches();
		if (!ismatch) {
			throw new BusinessException(fbmbillno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--184")/* @res "票据编号长度应为16位数字!" */);
		}

		String bankCode = fbmbillno.substring(0, 3);
		String provinceCode = fbmbillno.substring(4, 6);
		String billtypeCode = fbmbillno.substring(6, 7);
		String printCode = fbmbillno.substring(7, 8);

		// 省别码统一使用全国代码00
		if (!"00".equals(provinceCode)) {// 票据号第5,6位表示省别码，此省别码不符合标准！
			throw new BusinessException(
					nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0", "03618nccd--183", null,
							new String[] { String.valueOf(fbmbillno) })/* @res "票据号第5,6位表示省别码，票据号{0}对应的省别码不符合标准！" */);
		}

		// 5位银行承兑汇票；6为商业承兑汇票
		if (!"5".equals(billtypeCode) && !"6".equals(billtypeCode)) {// 票据号第7位表示票据种类码，此票据种类码不符合标准！
			throw new BusinessException(fbmbillno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--182")/* @res "票据号第7位表示票据种类码，此票据种类码不符合标准！" */);
		}

		// 商业承兑汇票不分行别，银行机构代码 统一使用人行代码001
		if ("6".equals(billtypeCode) && !("001".equals(bankCode))) {
			throw new BusinessException(fbmbillno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--181")/* @res "纸票商业承兑汇票第1~3位为银行机构代码，不分行别统一使用人行代码001！" */);
		}

		// 编码从0到6，其中1至6代表其中六家印刷厂，0代表其他的十家印刷厂，第八位不可能出现数字7、8、9。
		if ("7".equals(printCode) || "8".equals(printCode) || "9".equals(printCode)) {
			throw new BusinessException(fbmbillno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--180")/* @res "纸票票据号码第8位表示印制识别码，此印制识别码不符合标准！" */);
		}
	}

	/**
	 * 电子票据号检验
	 *
	 * @param fbmno
	 * @throws BusinessException
	 */
	private static void eBillNoCheck(String fbmno) throws BusinessException {

		// 30位数字
		Pattern pattern = Pattern.compile("\\d{30}");
		Boolean ismatch = pattern.matcher(fbmno).matches();
		// 电票不校验票据号长度
		ismatch = true;
		if (!ismatch) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--179")/* @res "电票票据号码长度应为30位数字！" */);
		}

		String firstNum = fbmno.substring(0, 1);
		if (!"2".equals(firstNum) && !"1".equals(firstNum) && !"5".equals(firstNum) && !"6".equals(firstNum)) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd--178")/* @res "电票票据号码第1位表示票据种类码，此票据种类码不符合标准！" */);
		}

		int year = Integer.parseInt(fbmno.substring(13, 17));
		int month = Integer.parseInt(fbmno.substring(17, 19));
		int day = Integer.parseInt(fbmno.substring(19, 21));

		if (!(month > 0 && month <= 12)) {// 票据号第14~22(包含14和22)位数字表示出票日期对应年月日，此段日期格式不符合标准！
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd-0243")/* @res "票据号第14~21(包含14和21)位数字表示出票日期对应年月日，此段日期格式不符合日期输入标准！" */);
		}

		// 闰年(日校验)
		if ((year % 4 == 0) && (month == 2) && !(day > 0 && day <= 29)) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd-0243")/* @res "票据号第14~21(包含14和21)位数字表示出票日期对应年月日，此段日期格式不符合日期输入标准！" */);
		}
		// 平年（日校验）
		if (!(year % 4 == 0) && (month == 2) && !(day > 0 && day <= 28)) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd-0243")/* @res "票据号第14~21(包含14和21)位数字表示出票日期对应年月日，此段日期格式不符合日期输入标准！" */);
		}

		Integer[] bigMonthArray = { 1, 3, 5, 7, 8, 10, 12 };
		Integer[] smallMonthArray = { 4, 6, 9, 11 };

		List<Integer> bigMonth = Arrays.asList(bigMonthArray);
		List<Integer> smallMonth = Arrays.asList(smallMonthArray);

		// 日校验
		if (bigMonth.contains(month) && !(day > 0 && day <= 31)) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd-0243")/* @res "票据号第14~22(包含14和22)位数字表示出票日期对应年月日，此段日期格式不符合日期输入标准！" */);
		} else if (smallMonth.contains(month) && !(day > 0 && day <= 30)) {
			throw new BusinessException(fbmno + nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("3618nccd_0",
					"03618nccd-0243")/* @res "票据号第14~22(包含14和22)位数字表示出票日期对应年月日，此段日期格式不符合日期输入标准！" */);
		}
	}

	/**
	 * 银行档案缓存查询 接口
	 */
	public String getBankdocPkByAccessorAndName(String pk_org, String name) throws BusinessException {
		return commonService.getBankdocPkByAccessorAndName(pk_org, name);

	}

	/**
	 * 人行联行缓存查询 接口
	 */
	public String getAssLineNumPkByAccessorAndName(String pk_org, String name) throws BusinessException {
		return commonService.getAssLineNumPkByAccessorAndName(pk_org, name);

	}

	/**
	 * 客商银行账户子户 接口
	 */
	public String getBankaccsubPkByAccessorAndName(String name, String pk_curr) throws BusinessException {
		return commonService.getBankaccsubPkByAccessorAndName(name, pk_curr);

	}

	/**
	 * 上游推单下游重算汇率方法：如果没有上游 可以为空 那么需要在调用此方法之前将上游的三个多汇率方案字段复制到下游的aggvo中
	 * 
	 * @param aggregvo            上游单据
	 * @param destAggvo           下游单据
	 * @param headPrecisionFields 下游单据表头重算汇率字段
	 * @param bodyPrecisionFields 下游单据表体重算汇率字段
	 * @param errmsg              推单重算汇率后 组织本币汇率为空的时候的提示语
	 * @return
	 * @throws BusinessException
	 */
	public static AbstractBill resetProcessAmounts(AggregatedValueObject aggregvo, AbstractBill destAggvo,
			List<PrecisionField> headPrecisionFields,
			Map<Class<? extends ISuperVO>, List<PrecisionField>> bodyPrecisionFields, String errmsg)
			throws BusinessException {
		if (destAggvo == null) {
			return null;
		}
		Object olcratecategoryObj = new Object();
		Object olcratedate = new Object();
		Object olcratetype = new Object();
		Object olcrate = new Object();
		SuperVO destHeadVO = (SuperVO) destAggvo.getParentVO();
		if (aggregvo != null) {
			SuperVO srcHeadVO = (SuperVO) aggregvo.getParentVO();
			olcratecategoryObj = srcHeadVO.getAttributeValue(FbmFieldConstant.OLCRATECATEGORY);
			olcratedate = srcHeadVO.getAttributeValue(FbmFieldConstant.OLCRATEDATE);
			olcratetype = srcHeadVO.getAttributeValue(FbmFieldConstant.OLCRATETYPE);
			olcrate = srcHeadVO.getAttributeValue(FbmFieldConstant.OLCRATE);
		} else {
			olcratecategoryObj = destHeadVO.getAttributeValue(FbmFieldConstant.OLCRATECATEGORY);
			olcratedate = destHeadVO.getAttributeValue(FbmFieldConstant.OLCRATEDATE);
			olcratetype = destHeadVO.getAttributeValue(FbmFieldConstant.OLCRATETYPE);
			olcrate = destHeadVO.getAttributeValue(FbmFieldConstant.OLCRATE);
		}

		String billtype = (String) destHeadVO.getAttributeValue(FbmFieldConstant.PK_BILLTYPECODE);
		Integer olcratecategory = null;
		if (olcratecategoryObj != null) {
			olcratecategory = (Integer) olcratecategoryObj;
		} else if (olcratecategoryObj == null && olcratetype != null) {// 应收应付的付款单没有类别这个字段 为什么财务这么棒
			IRateTypePubService service = NCLocator.getInstance().lookup(IRateTypePubService.class);
			RateTypeVO[] rateTypeVOs = service.queryRateTypeByPKs(new String[] { (String) olcratetype });
			if (rateTypeVOs != null && rateTypeVOs.length != 0) {
				olcratecategory = rateTypeVOs[0].getRate_category();
			}
		}
		// 1.上游单据的组织汇率类型不是固定汇率、自定义汇率时，下游单据的组织汇率类型的默认优先级是：上游单据的组织汇率类型—>下游单据自身的默认组织汇率类型的优先级
		// 2.上游单据的组织汇率类型是固定汇率、自定义汇率时，下游单据默认显示上游单据的组织汇率类型、组织本币汇率、组织汇率来源日期。
		destHeadVO.setAttributeValue(FbmFieldConstant.OLCRATETYPE, olcratetype);
		// 组织本币主键
		boolean isResetRate = true;
		if (olcratecategory != null && TMCurrencyUtil.judgeRatecategory(olcratecategory)) {
			destHeadVO.setAttributeValue(FbmFieldConstant.OLCRATEDATE, olcratedate);
			destHeadVO.setAttributeValue(FbmFieldConstant.OLCRATECATEGORY, olcratecategoryObj);
			destHeadVO.setAttributeValue(FbmFieldConstant.OLCRATE, olcrate);
			if (!StringUtil.isNull(billtype) && (billtype.equals(FbmPubConst.CONST_BILLTYPE_IMPAWN)
					|| billtype.equals(FbmPubConst.CONST_BILLTYPE_IMPAWNPOOL))) {
				destHeadVO.setAttributeValue(ImpawnVO.OLCBRATE, olcrate);
			}
			FbmPublicUtil.copyRateTyefieldsHead2Body(destAggvo);
			isResetRate = false;
		}
		AbstractBill[] returnAggvos = NCLocator.getInstance().lookup(ITMPrecisionServiceForNCC.class)
				.processAggVOPrecision(new AbstractBill[] { destAggvo }, headPrecisionFields, bodyPrecisionFields,
						isResetRate, true);
		boolean notHasOlcrate = false;
		if (!StringUtil.isNull(billtype) && (billtype.equals(FbmPubConst.CONST_BILLTYPE_IMPAWN)
				|| billtype.equals(FbmPubConst.CONST_BILLTYPE_IMPAWNPOOL))) {
			if (StringUtil.isNull(destAggvo.getParent().getAttributeValue(ImpawnVO.OLCBRATE))) {
				notHasOlcrate = true;
			}
		} else if (StringUtil.isNull(destAggvo.getParent().getAttributeValue(FbmFieldConstant.OLCRATE))) {
			notHasOlcrate = true;
		}
		if (notHasOlcrate) {
			throw new BusinessException(errmsg);
		}
		return returnAggvos[0];

	}

	/**
	 * @description: 判断票据号码是否有重叠
	 * @author: baifyun
	 * @date: 2022/10/24 15:29
	 * @param: [registerVO]
	 * @return: void
	 **/
	@SuppressWarnings("unchecked")
	public static void validRepeatBill(RegisterVO registerVO) throws BusinessException {
		// 当时是否分包标志为是的时候校验子区间是否重叠
		String fbmBillNo = registerVO.getFbmbillno();
		if (registerVO.getSubcontractcirculation() == null
				|| registerVO.getSubcontractcirculation().equals(UFBoolean.FALSE)
				|| registerVO.getPk_register() != null) {
			return;
		}
		Long billrangestart = BillRangeTools.handleBillRange(registerVO.getBillrangestart());
		Long billrangeend = BillRangeTools.handleBillRange(registerVO.getBillrangeend());
		StringBuffer condition = new StringBuffer();
		condition.append(" fbmbillno = '" + NCESAPI.sqlEncode(registerVO.getFbmbillno()) + "' ");
		condition.append(" and pk_org = '" + NCESAPI.sqlEncode(registerVO.getPk_org()) + "' ");
		condition.append(" and (pk_billtypecode = '" + FbmBusConstant.BILLTYPE_SIGN + "' or pk_billtypecode='"
				+ FbmBusConstant.BILLTYPE_INITPAYBILL + "') ");
		condition.append(" and dr = 0 ");
		condition.append(" and registerstatus <> 'has_disable' ");

		Collection<AggRegisterVO> collections = NCLocator.getInstance().lookup(IMDPersistenceQueryService.class)
				.queryBillOfVOByCond(AggRegisterVO.class, condition.toString(), false);
		// 当数据库中有对应的票据时，判断子票区间是否有重复的，有重复则报错
		if (collections != null && collections.size() > 0) {
			throw new BusinessException("应付票据票据号码" + fbmBillNo + "已存在");
		}
	}
	
	
	/**
	 * @description: 计算金额是否与子票区间的金额相等
	 * @author: baifyun
	 * @date: 2022/10/24 19:47
	 * @param: []
	 * @return:
	 **/
	public static void validateAmount(RegisterVO registerVO) throws BusinessException {
		if (registerVO.getSubcontractcirculation() == null
				|| registerVO.getSubcontractcirculation().equals(UFBoolean.FALSE)) {
			return;
		}
		Long billrangestart = BillRangeTools.handleBillRange(registerVO.getBillrangestart());
		Long billrangeend = BillRangeTools.handleBillRange(registerVO.getBillrangeend());

		UFDouble money = registerVO.getMoney();

		if (billrangestart != null && billrangeend != null) {
			// 票据金额=（子票区间最大序号-子票区间最小序号+1）*标准金额0.01
			Long total = billrangeend - billrangestart + 1;
			UFDouble count = new UFDouble("0.01").multiply(total);
			// 金额不一致报错
			if (count.compareTo(money) != 0) {
				throw new BusinessException("票据号码" + registerVO.getFbmbillno() + "子票区间" + billrangestart + "-"
						+ billrangeend + "的金额" + count.toString() + "与票据金额不相等，请修改");
			}

		}
	}

	public static void setBillRange(RegisterVO registerVO) {
		Boolean isSplitAllowed = registerVO.getSubcontractcirculation() != null
				&& registerVO.getSubcontractcirculation().booleanValue();
		if (isSplitAllowed) {
			// 允许分包流转时.
			registerVO.setBillrange(registerVO.getBillrangestart() + "-" + registerVO.getBillrangeend());
		} else {
			// 不允许分包流转时.
			if (ifBelongNewBillSystem(registerVO.getFbmbillno())) {
				// 新一代票据.
				registerVO.setBillrange("0");
			} else {
				// 旧系统票据. DO NOTHING.
			}
		}
	}

	/**
	 * 获取精度服务
	 * 
	 * @return
	 */
	private static ITMPrecisionServiceForNCC getPrecisionService() {
		return NCLocator.getInstance().lookup(ITMPrecisionServiceForNCC.class);
	}

	/**
	 * 获取金额精度
	 * 
	 * @throws BusinessException
	 */
	private static RegisterVO calcMoneyPrecision(RegisterVO registerVO, UFDouble money) throws BusinessException {
		RegisterVO newRegisterVO = new RegisterVO();
		newRegisterVO.setOlcmoney(registerVO.getOlcmoney());
		newRegisterVO.setOlcrate(registerVO.getOlcrate());
		newRegisterVO.setGlcmoney(registerVO.getGlcmoney());
		newRegisterVO.setGlcrate(registerVO.getGlcrate());
		newRegisterVO.setGllcmoney(registerVO.getGllcmoney());
		newRegisterVO.setGllcrate(registerVO.getGllcrate());
		newRegisterVO.setGatherdate(registerVO.getGatherdate());
		newRegisterVO.setPk_curr(registerVO.getPk_curr());
		newRegisterVO.setPaybillunit(registerVO.getPaybillunit());
		newRegisterVO.setMoney(money);
		getPrecisionService().processHeadPrecision(newRegisterVO,
				GatherConstant.getHeadPrecisionFieldsWithFieldRateType(), true);
		return newRegisterVO;
	}

	/**
	 * 子票区间校验
	 * 
	 * @param registerVO
	 * @param isEbill
	 * @throws BusinessException
	 */
	public static void validBillRange(RegisterVO registerVO, boolean isEbill) throws BusinessException {
		// 能否分包流转
		UFBoolean subcontractCirculation = registerVO.getSubcontractcirculation();
		if (subcontractCirculation != null && subcontractCirculation.booleanValue()) {
			String billRangeStart = registerVO.getBillrangestart();
			String billRangeEnd = registerVO.getBillrangeend();
			String firstNum = registerVO.getFbmbillno().substring(0, 1);
			if (isEbill && !"5".equals(firstNum) && !"6".equals(firstNum)) {
				throw new BusinessException("票据" + registerVO.getFbmbillno() + "非新一代票据，不可进行分包流转！");
			}

			if (StringUtil.isNull(billRangeStart) || StringUtil.isNull(billRangeEnd)) {
				throw new BusinessException("票据可分包流转状态下，票据子票区间开始和截止值不能为空！");
			}

			if (Long.valueOf(billRangeStart) <= 0 || Long.valueOf(billRangeEnd) <= 0) {
				throw new BusinessException("子票区间开始值和截止值都必须大于0！");
			}

			// 检查子票区间开始和截止值的长度
			if (billRangeStart.length() < 12 || billRangeEnd.length() < 12) {
				throw new BusinessException("子票区间开始值和截止值长度都必须为12位！");
			}

			// 检查截止值不能小于开始值
			if (Long.parseLong(billRangeEnd) < Long.parseLong(billRangeStart)) {
				throw new BusinessException("子票区间截止值不能小于开始值！");
			}
			// 校验金额格式
			checkMoneyFormatCorrect(registerVO.getMoney());

			// 计算子票区间的金额和修改的金额是否相等，不相等给出提示
			Long rangeDiff = Long.parseLong(registerVO.getBillrangeend())
					- Long.parseLong(registerVO.getBillrangestart());
			UFDouble diffMoney = new UFDouble(rangeDiff).add(1).multiply(0.01);
			// 1.重算金额2.处理精度
			RegisterVO newRegisterVO = calcMoneyPrecision(registerVO, diffMoney);
			if (newRegisterVO.getMoney().doubleValue() != registerVO.getMoney().doubleValue()) {
				throw new BusinessException(
						"票据号码" + registerVO.getFbmbillno() + "，子票区间" + (billRangeStart + "-" + billRangeEnd) + "的金额"
								+ newRegisterVO.getMoney().toString() + "（根据公式自动计算出的金额）与票据金额不相等，请修改！");
			}

			// 如果是纸票，不能拆分子票区间
			if (!isEbill) {
				throw new BusinessException("纸票不能进行分包流转！");
			}

			// 检查截止值不能小于开始值
			if (Long.parseLong(billRangeEnd) < Long.parseLong(billRangeStart)) {
				throw new BusinessException("子票区间截止值不能小于开始值！");
			}
		}

		// 票据是否属于新一代票据
		boolean isBelongNewBillSys = ifBelongNewBillSystem(registerVO.getFbmbillno());
		// 新一代票据系统，且不可分包流转的情况下，子票区间开始和结束值应该为0
		if (isBelongNewBillSys && (subcontractCirculation == null || !subcontractCirculation.booleanValue())
				&& (!"0".equals(registerVO.getBillrangestart()) || !"0".equals(registerVO.getBillrangeend()))) {
			throw new BusinessException("票据[" + registerVO.getFbmbillno() + "]为新一代票且不可分包流转，子票区间开始和结束应为0！");
		}
		// 为勾选“能否分包流转”的情况下，传了子票区间的值
		if (!isBelongNewBillSys && (StringUtil.isNotNull(registerVO.getBillrangestart())
				|| StringUtil.isNotNull(registerVO.getBillrangeend()))) {
			throw new BusinessException("非新一代票据不可进行分包流转，不可输入子票区间！");
		}

	}

	/**
	 * 检查子票区间或剩余子票区间是否有重复的，用于判断是回头票还是录入重复的票据
	 * 
	 * @param aggRegisterVO
	 * @param newRegisterVO
	 * @return
	 */
	private static boolean checkBillRangeOrAvailableBillRangesRepeat(AggRegisterVO aggRegisterVO,
			RegisterVO newRegisterVO) {
		// 如果ref_pk_register为空，则说明是原始票新一代
		if (StringUtil.isNull(aggRegisterVO.getparentVO().getRef_pk_register())) {
			String avaliableRanges = aggRegisterVO.getparentVO().getAvailablebillranges();
			if (StringUtil.isNull(avaliableRanges)) {
				return false;
			}
			String[] billrangeArr = null;
			if (avaliableRanges.indexOf(",") > 0) {
				billrangeArr = avaliableRanges.split(",");
			} else {
				billrangeArr = new String[] { avaliableRanges };
			}
			AggRegisterVO curAggRegisterVO = new AggRegisterVO();
			for (String curBillrange : billrangeArr) {
				RegisterVO vo = new RegisterVO();
				String[] splitRangeArr = curBillrange.split("-");
				vo.setBillrangestart(splitRangeArr[0]);
				vo.setBillrangeend(splitRangeArr[1]);
				curAggRegisterVO.setParentVO(vo);
				if (ifIncludesInterval(curAggRegisterVO, newRegisterVO)) {
					return true;
				}
			}
		} else {
			return ifIncludesInterval(aggRegisterVO, newRegisterVO);
		}
		return false;
	}

	/**
	 * 检查新数据是否跨越前、后子票区间
	 * 
	 * @param actionVO
	 * @return
	 */
	private static boolean ifIncludesInterval(AggRegisterVO aggRegisterVO, RegisterVO newRegisterVO) {

		boolean flag = false;

		if (Long.parseLong(aggRegisterVO.getparentVO().getBillrangestart()) <= Long
				.parseLong(newRegisterVO.getBillrangeend())
				&& Long.parseLong(aggRegisterVO.getparentVO().getBillrangeend()) >= Long
						.parseLong(newRegisterVO.getBillrangeend())) {
			flag = true;
		}
		if (Long.parseLong(aggRegisterVO.getparentVO().getBillrangestart()) <= Long
				.parseLong(newRegisterVO.getBillrangestart())
				&& Long.parseLong(aggRegisterVO.getparentVO().getBillrangeend()) >= Long
						.parseLong(newRegisterVO.getBillrangestart())) {
			flag = true;
		}

		if (Long.parseLong(newRegisterVO.getBillrangestart()) <= Long
				.parseLong(aggRegisterVO.getparentVO().getBillrangeend())
				&& Long.parseLong(newRegisterVO.getBillrangeend()) >= Long
						.parseLong(aggRegisterVO.getparentVO().getBillrangeend())) {
			flag = true;
		}

		if (Long.parseLong(newRegisterVO.getBillrangestart()) <= Long
				.parseLong(aggRegisterVO.getparentVO().getBillrangestart())
				&& Long.parseLong(newRegisterVO.getBillrangeend()) >= Long
						.parseLong(aggRegisterVO.getparentVO().getBillrangestart())) {
			flag = true;
		}

		if (Long.parseLong(aggRegisterVO.getparentVO().getBillrangestart()) >= Long
				.parseLong(newRegisterVO.getBillrangestart())
				&& Long.parseLong(aggRegisterVO.getparentVO().getBillrangeend()) <= Long
						.parseLong(newRegisterVO.getBillrangeend())) {
			flag = true;
		}

		if (Long.parseLong(newRegisterVO.getBillrangestart()) >= Long
				.parseLong(aggRegisterVO.getparentVO().getBillrangestart())
				&& Long.parseLong(newRegisterVO.getBillrangeend()) <= Long
						.parseLong(aggRegisterVO.getparentVO().getBillrangeend())) {
			flag = true;
		}

		return flag;
	}

	/**
	 * 校验金额是否为标准金额（0.01）的整数倍 并返回整数倍的数值
	 * 
	 * @param money
	 * @return
	 * @throws BusinessException
	 */
	public static Long checkMoneyFormatCorrect(UFDouble money) throws BusinessException {
		if (money == null) {
			return 0L;
		}
		// 金额 除以 标准金额（0.01） 除以.0.01=乘以100
		UFDouble standardAmountMultiplier = money.multiply(100);
		// 保存到额金额是否为标准金额（0.01）的整数倍
		long roundmum = standardAmountMultiplier.longValue();
		// 是否大于0，如果大于0标识
		if (standardAmountMultiplier.sub(new UFDouble(roundmum)).doubleValue() > 0
				|| standardAmountMultiplier.sub(new UFDouble(roundmum)).doubleValue() < 0) {
			throw new BusinessException("等分票据的票据金额要等于标准金额的整数倍，请修改！");
		}
		return roundmum;
	}

	/**
	 * 格式化子票区间的值，固定为12为长度字符串
	 * 
	 * @param rangeValue
	 * @return
	 */
	public static String formatBillRangeValue(String rangeValue) {
		if (StringUtil.isNull(rangeValue)) {
			return FbmBusConstant.DEFAULT_BILL_RANGESTART_INIT_VALUE;
		}
		if (rangeValue.length() == 12 || rangeValue.length() > 12) {
			return rangeValue;
		}
		// 需要补充0的字符长度
		long needSupplyCharacterLeng = (12 - rangeValue.length());
		StringBuilder builder = new StringBuilder();
		while (needSupplyCharacterLeng > 0) {
			builder.append("0");
			needSupplyCharacterLeng--;
		}
		builder.append(rangeValue);
		return builder.toString();
	}

	/**
	 * 根据票号判断票据是否为新一代
	 * 
	 * @param fbmBillNo
	 * @return
	 */
	public static String getBillSysTypeByFbmBillNo(String fbmBillNo) {
		if (StringUtil.isNull(fbmBillNo)) {
			return null;
		}
		String firstNum = fbmBillNo.substring(0, 1);
		if (("5".equals(firstNum) || "6".equals(firstNum)) && fbmBillNo.length() == 30) {
			// 标识为新一代电票
			return FbmBusConstant.CS02;
		}
		return FbmBusConstant.CS01;
	}

	/**
	 * 票据是否属于新一代票据
	 * 
	 * @param fbmBillNo
	 * @return
	 */
	public static boolean ifBelongNewBillSystem(String fbmBillNo) {
		String billsystype = getBillSysTypeByFbmBillNo(fbmBillNo);
		if (FbmBusConstant.CS02.equals(billsystype)) {
			return true;
		}
		return false;
	}

	/**
	 * 检查区间是否完整匹配.
	 */
	public static boolean ifRangeMatched(CircularlyAccessibleValueObject one, CircularlyAccessibleValueObject other) {
		String oneStart = (String) one.getAttributeValue(FbmPubConst.BILLRANGESTART);
		String oneEnd = (String) one.getAttributeValue(FbmPubConst.BILLRANGEEND);
		String otherStart = (String) other.getAttributeValue(FbmPubConst.BILLRANGESTART);
		String otherEnd = (String) other.getAttributeValue(FbmPubConst.BILLRANGEEND);
		return StringUtils.equals(oneStart, otherStart) && StringUtils.equals(oneEnd, otherEnd);
	}

	/**
	 * 子票区间自动补齐12位
	 * 
	 * @param billRange
	 * @return
	 */
	public static String autoFillRange(String billRange) {
		if (org.apache.commons.lang3.StringUtils.isNotEmpty(billRange) && !"0".equals(billRange)) {
			Long number = Long.valueOf(billRange);
			billRange = String.format("%012d", number);
		}
		return billRange;
	}
}