package nc.bs.gl.glvoucher.processor.fip;

import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.gl.dao.GLBaseDAO;
import nc.bs.gl.voucher.service.TempTableUtils;
import nc.gl.utils.GLNumUtils;
import nc.impl.gl.pub.TempTableCreater;
import nc.itf.gl.pub.ICashFlowCase;
import nc.jdbc.framework.processor.BeanListProcessor;
import nc.vo.fi.pub.SqlUtils;
import nc.vo.fip.external.FipBillSumRSVO;
import nc.vo.fip.service.FipRelationInfoVO;
import nc.vo.fipub.freevalue.GlAssVO;
import nc.vo.fipub.freevalue.Module;
import nc.vo.fipub.utils.StrTools;
import nc.vo.gateway60.itfs.AssByPkThreadCache;
import nc.vo.gl.cashflowcase.CashflowcaseVO;
import nc.vo.gl.glvoucher.AggNVoucherVO;
import nc.vo.gl.glvoucher.NDetailVO;
import nc.vo.gl.glvoucher.NVoucherVO;
import nc.vo.glcom.ass.AssVO;
import nc.vo.glcom.constant.GLVoucherKindConst;
import nc.vo.ml.NCLangRes4VoTransl;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.pattern.pub.MapList;
import nc.vo.voucher.fip.SchemeConst;

import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class NVoucherSumInDBProcessor extends NVoucherSumProcessor {
	public NVoucherSumInDBProcessor(String pk_sumrule) {
		super(pk_sumrule);
	}

	private Map<String, NVoucherVO> mergedHead = new HashMap<>();

	private MapList<String, NDetailVO> detailContainer = new MapList<>();

	private MapList<String, String> groupedRelationIdMap = new MapList<>();

	private MapList<String, FipRelationInfoVO> groupedRelationVOMap = new MapList<>();

	private Map<String, AssVO[]> assVOMap = new HashMap<>();

	private String groupTempTableName;

	@Override
	public List<FipBillSumRSVO> processSumData(List<FipBillSumRSVO> sumRSVOs) throws BusinessException {
		fillGroupedRelationMap(sumRSVOs);
		createGroupTempTable();
		prepareGroupTempTableData();
		generateMergedHead();
		loadSchemeInfo();
		sumDetail();
		List<AggNVoucherVO> aggVouchers = constructVouchers();

		List<FipBillSumRSVO> results = new ArrayList<>();
		for(AggNVoucherVO aggVoucher : aggVouchers) {
			NVoucherSupply4FipProcessor supplyProcessor = new NVoucherSupply4FipProcessor(aggVoucher);
			supplyProcessor.process();
			FipBillSumRSVO sumRSVO = new FipBillSumRSVO();
			sumRSVO.setBillVO(aggVoucher);
			NVoucherVO voucher = aggVoucher.getParentVO();
			String groupId = (String)voucher.getAttributeValue("groupid");
			sumRSVO.setRelationvos(groupedRelationVOMap.get(groupId).toArray(new FipRelationInfoVO[0]));
			sumRSVO.setMessageinfo(generateMsgInfo(aggVoucher));
			results.add(sumRSVO);
		}
		return results;
	}

	private void fillGroupedRelationMap(Collection<FipBillSumRSVO> sumRsVOs) {
		int groupIndex = 0; //分组号
		for(FipBillSumRSVO sumRSVO : sumRsVOs) {
			FipRelationInfoVO[] relationInfos = sumRSVO.getRelationvos();
			if(relationInfos == null || relationInfos.length == 0) {
				continue;
			}

			String groupNO = generateGroupNO(++groupIndex);
			for(FipRelationInfoVO relationInfo : relationInfos) {
				groupedRelationIdMap.put(groupNO, relationInfo.getRelationID());
				groupedRelationVOMap.put(groupNO, relationInfo);
			}
		}
	}

	private String generateGroupNO(int groupIndex) {
		int maxLength = 8;
		String groupNO = StrTools.supplementStr(groupIndex, maxLength, '0', true);
		return groupNO;
	}

	private void createGroupTempTable() throws BusinessException {
		String[] columns = new String[]{"pk_voucher", "pk_detail", "groupid"};
		int[] coltype = new int[] {Types.VARCHAR, Types.VARCHAR, Types.VARCHAR};
		try {
			groupTempTableName = TempTableCreater.createTempTable("tmp_gl_fipsum", columns, coltype);
		} catch (SQLException e) {
			throw new BusinessException(e);
		}
	}

	/**
	 * 准备分组临时表数据
	 * @throws BusinessException
	 */
	private void prepareGroupTempTableData() throws BusinessException {
		String[] columns = new String[] {"pk_voucher", "groupid"};
		List<Object[]> datas = new ArrayList<>();
		for(String groupId : groupedRelationIdMap.keySet()) {
			List<String> pk_vouchers = groupedRelationIdMap.get(groupId);
			for(String pk_voucher : pk_vouchers) {
				Object[] data = new Object[] {pk_voucher, groupId};
				datas.add(data);
			}
		}
		new GLBaseDAO().excuteInsertBatch(groupTempTableName, columns, datas);
	}

	/**
	 * 生成主表合并后数据
	 * @throws BusinessException
	 */

	private void generateMergedHead() throws BusinessException {
		String sql = generateMergeHeadSql();
		List<NVoucherVO> vouchers = new GLBaseDAO().excuteQuery(sql, NVoucherVO.class);
		if(vouchers==null || vouchers.size()==0) {
			throw new BusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("glnvoucher_0",
					"0glnvoucher-0114")/* "查询不到临时凭证，可能是冻结状态" */);
		}
		//针对合并凭证，需要用的制单人转换模板制单人设置公式，其余单据不设置，用min取设置公式的制单人 sunfj
		if(StrTools.isEmptyStr((vouchers.get(0)).getPk_prepared())) {
			String sql2 = sql.replace("max(pk_prepared) as pk_prepared","min(pk_prepared) as pk_prepared " );
			List<NVoucherVO> vouchers2 = new GLBaseDAO().excuteQuery(sql2, NVoucherVO.class);
			if(vouchers2 != null && !vouchers2.isEmpty()) {
				 vouchers = vouchers2;
			}
		}
		
		
		for(NVoucherVO voucher : vouchers) {
			//补充制单人
			if(StrTools.isEmptyStr(voucher.getPk_prepared())) {
				voucher.setPk_prepared(InvocationInfoProxy.getInstance().getUserId());
			}

			if(voucher.getVoucherkind() == null) {
				voucher.setVoucherkind(GLVoucherKindConst.NORMAL);
			}
			
			if(voucher.getPk_exratetype2() == null) {
//				RateTypeVO rateTypeVO = new RateTypeThreadCache().getRateTypeByPk_Accountingbook(voucher.getPk_accountingbook());
//				String pk_exratetype2 = RateUtil.getInstance(voucher.getPk_accountingbook()).getDefaultPk_exratetype();//TODO 添加缓存缓存
//				voucher.setPk_exratetype2(rateTypeVO.getPk_ratetype());
			}

			//FIXME 重置adjustperiod，测试提问题：前台生成列表页面 期间都是空的受不了了
			if(voucher.getAdjustperiod() == null && voucher.getPeriod()!=null) {
				voucher.setAdjustperiod(voucher.getPeriod());
			}
			
			String groupid = (String)voucher.getAttributeValue("groupid");
			if(mergedHead.get(groupid) != null) {
				throw new BusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("glnvoucher_0","0glnvoucher-0014")/*@res "数据中因为有些字段不同，有不能合并成一张凭证的情况，请检查下面的字段是否相同：集团，组织，财务核算账簿，凭证类别，交易类型，自定义项。"*/);
			}
			mergedHead.put(groupid, voucher);
		}

		combinSystem();
	}

	private void loadSchemeInfo() throws BusinessException {
		NVoucherVO voucher = mergedHead.values().toArray(new NVoucherVO[0])[0];
		String pk_accountingbook = voucher.getPk_accountingbook();
		String versionDate = voucher.getPrepareddate().toStdString();
		loadSchemeInfo(pk_accountingbook, versionDate);
	}

	private void sumDetail() throws BusinessException {
		String[] sumSqls = getSumSql();
		String[] normalSqls = getNormalSql();

		String[] sumConditions = getSumConditions();
		String unSumCondition = getUnSumCondition();

		if(sumConditions != null && sumConditions.length > 0) {
			for(String sumCondition : sumConditions) {
				String sql = sumSqls[0] + sumSqls[1] + sumSqls[2] + sumCondition + sumSqls[3] + sumSqls[4];
				MapList<String, NDetailVO> results = queryDetailsBySql(sql);
				//cat 现金流量
				catCashFlowCaseForSum(results, sumSqls[3].substring(6), sumSqls[1], sumCondition);
				mixDetail(detailContainer, results);  
			}
		}

		if(isCombinSameAccount()) {
			if(unSumCondition != null && unSumCondition.length() > 0) {
				String sql = normalSqls[0] + normalSqls[1] + normalSqls[2] + unSumCondition + normalSqls[3] + normalSqls[4];
				MapList<String, NDetailVO> results = queryDetailsBySql(sql);
				//cat 现金流量
				catCashFlowCase(results);
				mixDetail(detailContainer, results);
			}
		}else {
			String sql = normalSqls[0] + normalSqls[1] + normalSqls[2] + unSumCondition + normalSqls[3] + normalSqls[4];
			MapList<String, NDetailVO> results = queryDetailsBySql(sql);
			//cat 现金流量
			catCashFlowCase(results);
			mixDetail(detailContainer, results);
		}
	}

	/**
	 * 构造凭证
	 * modify 2022-02-23 没有分录返回主表信息
	 * @return
	 * @throws BusinessException
	 */
	private List<AggNVoucherVO> constructVouchers() throws BusinessException{
		List<AggNVoucherVO> results= new ArrayList<>();
		//加载辅助核算信息
		loadAssInfo();
		//TODO 加载科目信息

		String[] groupIds = groupedRelationIdMap.keySet().toArray(new String[0]);
		Arrays.sort(groupIds);//分组id排序

		for(String groupid : groupIds) {

			NVoucherVO voucher = mergedHead.get(groupid);
			List<NDetailVO> details = detailContainer.get(groupid);
			
			if(details == null) {
				details = new ArrayList<>();
			}
			//补充ASSVO
			for(NDetailVO detail : details) {
				if(StrTools.isEmptyStr(detail.getAssid())) {
					continue;
				}
				if(detail.getAssvos() == null) {
					AssVO[] assVOs = assVOMap.get(detail.getAssid());
					detail.setAssvos(assVOs);
				}
			}
			AggNVoucherVO aggVoucher = combinAggVoucher(voucher, details);
			results.add(aggVoucher);

		}
		return results;
	}

	/**
	 * 生成合并表头的sql
	 * TODO 交易类型
	 * @return
	 */
	private String generateMergeHeadSql() {
		StringBuilder sql = new StringBuilder();
		sql.append("select b.groupid as groupid,pk_group, pk_org,pk_accountingbook,pk_vouchertype, pk_tradetype, 'C0' as pk_billtype, ")
		.append("max(year) as year, max(period) as period, 0 as no, max(prepareddate) as prepareddate, null as pk_system,")
		.append(" null as tallydate, sum(attachment) as attachment, max(pk_prepared) as pk_prepared, null as checker, null as casher, ")
		.append(" null as manager, min(signflag) as signflag,  min(modifyflag) as modifyflag, 'N' as discardflag, max(addclass) as addclass,")
		.append(" max(deleteclass) as deleteclass, min(DETAILMODFLAG) as detailModflag, ")
		.append(" null as pk_billtype, ");
		for(int i = 1; i<= 30; i++) {
			sql.append(" vdef").append(i).append(",");
		}
		sql.append(" null as pk_voucher, max(pk_exratetype2) as pk_exratetype2 ");

		sql.append("from gl_rtvoucher a inner join ").append(groupTempTableName).append(" b on a.pk_voucher = b.pk_voucher ");
		sql.append("group by groupid,pk_group,pk_org,pk_accountingbook,pk_vouchertype,pk_tradetype,");
		for(int i = 1; i<= 30; i++) {
			sql.append(" vdef").append(i).append(",");
		}
		sql.deleteCharAt(sql.length() - 1);
		return sql.toString();
	}


	/**
	 * 合并制单系统
	 * @throws DAOException
	 */
	private void combinSystem() throws DAOException {
		Map<String, String> combinSystemMap = queryCombinSystem();
		for(String groupid : mergedHead.keySet()) {
			NVoucherVO voucher = mergedHead.get(groupid);
			voucher.setPk_system(combinSystemMap.get(groupid));
		}
	}

	/**
	 * 查询制单系统
	 * @return
	 * @throws DAOException
	 */
	private Map<String, String> queryCombinSystem() throws DAOException{
		String sql = "select distinct groupid,pk_system from gl_rtvoucher a inner join "
				+ groupTempTableName + " b on a.pk_voucher = b.pk_voucher where isnull(groupid, '~') <> '~'";
		List<Map<String, Object>> groupSystemList = new GLBaseDAO().excuteQuery(sql);

		MapList<String, String> groupedSystem = new MapList<>();
		for(Map<String, Object> groupSystem : groupSystemList) {
			String pk_system = (String)groupSystem.get("pk_system");
			if(pk_system != null) {
				groupedSystem.put((String)groupSystem.get("groupid"), pk_system.trim());
			}
		}

		Map<String, String> result = new HashMap<>();
		for(String groupid : groupedSystem.keySet()) {
			List<String> systems = groupedSystem.get(groupid);
			if(systems != null && systems.size() > 0) {
				//去重
				Set<String> systemSet = new LinkedHashSet<String>();
				systemSet.addAll(systems);

				result.put(groupid, StrTools.join(systemSet.iterator(), ","));
			}
		}
		return result;
	}




	/**
	 * 0 select
	 * 1 from
	 * 2 where
	 * 3 group
	 * 4 order
	 * @return
	 */
	private String[] getSumSql() {
		String[] results = new String[5];
		results[0] = generateSumSelectSql();
		results[1] = " from gl_rtdetail a inner join " + groupTempTableName + " b on a.pk_voucher = b.pk_voucher ";
		results[2] = " where a.dr = 0 ";
		results[3] = generateSumGroupSql();
		results[4] = " order by groupid ";

		if(isCombinExplan()) {
			results[0] = results[0].replaceFirst("a.explanation", "max(a.explanation)");
			results[3] = results[3].replaceFirst(",a.explanation,", ",");
		}

		if(isCombinPrice()) {
			results[0] = results[0].replaceFirst("a.price", "sum(a.price)");
			results[3] = results[3].replaceFirst(", a.price,", ",");
		}

		if(isCombinExrateInfo()) {
			results[0] = results[0].replaceFirst("a." + NDetailVO.PK_EXRATETYPE2, "max(a." + NDetailVO.PK_EXRATETYPE2 + ")");
			results[0] = results[0].replaceFirst("a." + NDetailVO.EXRATEDATE2, "max(a." + NDetailVO.EXRATEDATE2 + ")");
			results[3] = results[3].replaceFirst(", " + NDetailVO.PK_EXRATETYPE2 + ",", ",");
			results[3] = results[3].replaceFirst(", " + NDetailVO.EXRATEDATE2 + ",", ",");
		}

		return results;
	}

	/**
	 * 0 select
	 * 1 from
	 * 2 where
	 * 3 group
	 * 4 order
	 * @return
	 */
	private String[] getNormalSql() {
		String[] results = new String[5];
		results[0] = generateNormalSelectSql();
		results[1] = " from gl_rtdetail a inner join " + groupTempTableName + " b on a.pk_voucher = b.pk_voucher ";
		results[2] = " where a.dr = 0 ";
		results[3] = "";
		results[4] = " order by groupid ";

		return results;
	}

	private List<String[]> generateDetailFields() {
		/**
		 * 第一个字段 为 a
		 * 第二个字段
		 * "null" => null as a
		 * null => a as a
		 * min => min(a) as a
		 * max => max(a) as a
		 * sum => sum(a) as a
		 */
		List<String[]> fields = new ArrayList<>();
		fields.add(new String[] {NDetailVO.PK_DETAIL, "min"});
		fields.add(new String[] {NDetailVO.NOV, "null"});
		fields.add(new String[] {NDetailVO.OPPOSITESUBJ, "null"});
		fields.add(new String[] {NDetailVO.BANKACCOUNT, null});
		fields.add(new String[] {NDetailVO.PK_VOUCHER, "null"});
		fields.add(new String[] {NDetailVO.PK_ORG, "min"});
		fields.add(new String[] {NDetailVO.MODIFYFLAG, "min"});
		fields.add(new String[] {NDetailVO.RECIEPTCLASS, "min"});
		fields.add(new String[] {NDetailVO.DEBITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.DEBITQUANTITY, "sum"});
		fields.add(new String[] {NDetailVO.LOCALDEBITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.GROUPDEBITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.GLOBALDEBITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.CREDITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.CREDITQUANTITY, "sum"});
		fields.add(new String[] {NDetailVO.LOCALCREDITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.GROUPCREDITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.GLOBALCREDITAMOUNT, "sum"});
		fields.add(new String[] {NDetailVO.PK_ACCOUNTINGBOOK, null});
		fields.add(new String[] {NDetailVO.PK_UNIT, null});
		fields.add(new String[] {NDetailVO.PK_UNIT_V, null});
		fields.add(new String[] {NDetailVO.PK_VOUCHERTYPEV, null});
		fields.add(new String[] {NDetailVO.YEARV, null});
		fields.add(new String[] {NDetailVO.PERIODV, null});
		fields.add(new String[] {NDetailVO.EXPLANATION, null});
		fields.add(new String[] {NDetailVO.PK_ACCASOA, null});
		fields.add(new String[] {NDetailVO.ASSID, null});
		fields.add(new String[] {NDetailVO.PK_CURRTYPE, null});
		fields.add(new String[] {NDetailVO.PRICE, null});
		fields.add(new String[] {NDetailVO.EXCRATE2, null});
		fields.add(new String[] {NDetailVO.EXCRATE3, null});
		fields.add(new String[] {NDetailVO.EXCRATE4, null});
		fields.add(new String[] {NDetailVO.CHECKSTYLE, null});
		fields.add(new String[] {NDetailVO.CHECKNO, null});
		fields.add(new String[] {NDetailVO.CHECKDATE, null});
		fields.add(new String[] {NDetailVO.BILLTYPE, null});
		fields.add(new String[] {NDetailVO.BUSIRECONNO, null});
		fields.add(new String[] {NDetailVO.NETBANKFLAG, null});
		fields.add(new String[] {NDetailVO.VERIFYDATE, null});
		fields.add(new String[] {NDetailVO.VERIFYNO, null});
		fields.add(new String[] {NDetailVO.PK_LIABILITYCENTER, null});
		fields.add(new String[] {NDetailVO.PK_LIABILITYCENTER_V, null});
		fields.add(new String[] {NDetailVO.PK_EXRATETYPE2, null});
		fields.add(new String[] {NDetailVO.EXRATEDATE2, null});
		fields.add(new String[] {NDetailVO.DUEDATE, null});
		fields.add(new String[] {NDetailVO.ORDERSTR, "max"});
		for(int i = 1; i <= 80 ;i ++) {
			fields.add(new String[] {"vbdef" + i, null});
		}
		return fields;
	}

	private String generateSumColumnStr(String tableAlias, String[] field) {
		StringBuilder columnStr = new StringBuilder(" ");
		String fieldName = field[0];
		String oper = field[1];
		if(oper == null) {
			columnStr.append(tableAlias).append(".").append(fieldName);
		}else if(oper.equals("null")) {
			columnStr.append("null");
		}else if(oper.equals("sum")) {
			columnStr.append("sum(").append(tableAlias).append(".").append(fieldName).append(")");
		}else if(oper.equals("min")) {
			columnStr.append("min(").append(tableAlias).append(".").append(fieldName).append(")");
		}else if(oper.equals("max")){
			columnStr.append("max(").append(tableAlias).append(".").append(fieldName).append(")");
		}

		columnStr.append(" as ").append(fieldName);
		return columnStr.toString();
	}

	private String generateSumSelectSql() {
		StringBuilder selectSql = new StringBuilder("select ");
		List<String[]> fields = generateDetailFields();
		for(String[] field : fields) {
			selectSql.append(generateSumColumnStr("a", field));
			selectSql.append(", ");
		}
		selectSql.append("b.groupid as groupid ");
		return selectSql.toString();
	}

	private String generateSumGroupSql() {
		StringBuilder groupSql = new StringBuilder();
		groupSql.append(" group by b.groupid,a.pk_accountingbook,a.pk_unit,a.pk_unit_v,pk_vouchertypev,yearv,periodv,a.explanation,");
		groupSql.append(" a.pk_accasoa, assid, pk_currtype, a.price, excrate2, excrate3,excrate4,bankaccount,checkstyle, checkno,");
		groupSql.append(" checkdate, billtype, innerbusno, innerbusdate, busireconno,netbankflag, verifydate, verifyno, ");
		groupSql.append(" pk_liabilitycenter, pk_liabilitycenter_v, duedate, ");

		for(int i = 1; i <= 80; i++) {
			groupSql.append("vbdef").append(i).append(",");
		}
		groupSql.append(" pk_exratetype2, exratedate2 ");
		return groupSql.toString();
	}

	private String generateNormalSelectSql() {
		StringBuilder selectSql = new StringBuilder();
		selectSql.append("select ");
		List<String[]> fields = generateDetailFields();
		for(String[] field : fields) {
			selectSql.append(" a.").append(field[0]).append(" as ").append(field[0]);
			selectSql.append(", ");
		}
		selectSql.append(" b.groupid as groupid ");
		return selectSql.toString();
	}

	/**
	 * 构造需要合并的查询条件
	 * @return
	 * @throws BusinessException
	 */
	private String[] getSumConditions() throws BusinessException {
		List<String> conditions = new ArrayList<>();

		boolean isCombinAccount = isCombinSameAccount();

		StringBuilder condition = new StringBuilder();
		if(isCombinAccount) {//合并科目，排除例外科目
			//科目条件
			if(getExAccountPks() != null && getExAccountPks().length > 0) {
				String inAccountSql = SqlUtils.getInStr("a.pk_accasoa", getExAccountPks(),Boolean.TRUE);
				String notInAccountSql = inAccountSql.replaceFirst("in", "not in");
				condition.append(" and ").append(notInAccountSql);
			}
		}else {//不合并科目，则只合并例外科目
			if(getExAccountPks() == null || getExAccountPks().length == 0) {
				return null;
			}else {
				String inAccountSql = SqlUtils.getInStr("a.pk_accasoa", getExAccountPks(),Boolean.TRUE);
				condition.append(" and ").append(inAccountSql);
			}
		}

		//借贷条件
		if(SchemeConst.COMBINTYPE_D.equals(getCominType())){
			condition.append(" and ").append("(debitquantity<>0 or localdebitamount<>0 or groupdebitamount<>0 or globaldebitamount<>0) ");
			conditions.add(condition.toString());
		}else if(SchemeConst.COMBINTYPE_C.equals(getCominType())) {
			condition.append(" and ").append("(creditquantity<>0 or localcreditamount<>0 or groupcreditamount<>0 or globalcreditamount<>0) ");
			conditions.add(condition.toString());
		}else {
			if(isCombinDiffDirection()) {
				conditions.add(condition.toString());
			}else {				
				//借方条件
				String conditionDebit = condition.toString() + " and (debitquantity<>0 or localdebitamount<>0 or groupdebitamount<>0 or globaldebitamount<>0) ";
				conditions.add(conditionDebit);
				//贷方条件
				String conditionCredit = condition.toString() + " and (creditquantity<>0 or localcreditamount<>0 or groupcreditamount<>0 or globalcreditamount<>0) ";
				conditions.add(conditionCredit);
			}
		}

		return conditions.toArray(new String[0]);

	}

	/**
	 * 构造不需要合并的查询条件
	 * @return null 说明 不合并科目且没有例外科目，全部采用不合并逻辑处理
	 * @throws BusinessException
	 */
	private String getUnSumCondition() throws BusinessException {
		StringBuilder condition = new StringBuilder();

		if(isCombinSameAccount()) {
			if(getExAccountPks() != null && getExAccountPks().length > 0) {
				String inAccountSql = SqlUtils.getInStr("a.pk_accasoa", getExAccountPks(),Boolean.TRUE);
				condition.append(inAccountSql);
			}
		}else {
			if(getExAccountPks() != null && getExAccountPks().length > 0) {
				String inAccountSql = SqlUtils.getInStr("a.pk_accasoa", getExAccountPks(),Boolean.TRUE);
				String notInAccountSql = inAccountSql.replaceFirst("in", "not in");
				condition.append(notInAccountSql);
			}else {
				return "";
			}
		}

		if(SchemeConst.COMBINTYPE_D.equals(getCominType())) {
			if(condition.length() > 0){
				condition.append(" or ");
			}
			condition.append("(creditquantity<>0 or localcreditamount<>0 or groupcreditamount<>0 or globalcreditamount<>0) ");
		}else if(SchemeConst.COMBINTYPE_C.equals(getCominType())) {
			if(condition.length() > 0){
				condition.append(" or ");
			}
			condition.append(" (debitquantity<>0 or localdebitamount<>0 or groupdebitamount<>0 or globaldebitamount<>0) ");
		}

		if(condition.length() > 0) {
			condition.insert(0, " and (");
			condition.append(") ");
		}

		return condition.toString();
	}

	private MapList<String, NDetailVO> queryDetailsBySql(String sql) throws DAOException{
		MapList<String, NDetailVO> results = new MapList<>();
		@SuppressWarnings("unchecked")
		List<NDetailVO> details = (List<NDetailVO>)new BaseDAO().executeQuery(sql, new BeanListProcessor(NDetailVO.class));
		if(details != null && details.size() > 0) {
			for(NDetailVO detail : details) {
				results.put((String)detail.getAttributeValue("groupid"), detail);
			}
		}
		return results;
	}

	private void mixDetail(MapList<String, NDetailVO> container, MapList<String, NDetailVO> queryResults) {
		if(queryResults != null) {
			for(String key : queryResults.keySet()) {
				container.putAll(key, queryResults.get(key));
			}
		}
	}

	private boolean isAllZero(NDetailVO detail) {
		return GLNumUtils.allZero(
				detail.getCreditquantity(), detail.getDebitquantity(),
				detail.getCreditamount(), detail.getDebitamount(),
				detail.getLocalcreditamount(), detail.getLocaldebitamount(),
				detail.getGroupcreditamount(), detail.getGroupdebitamount(),
				detail.getGlobalcreditamount(), detail.getGlobaldebitamount()
				);
	}

	/**
	 * 参数顺序决定优先级
	 *
	 * result > 0 借
	 * result < 0 贷
	 * result == 0 平
	 * @return
	 */
	private int detailDirection(UFDouble... amounts) {
		for(UFDouble amount : amounts) {
			if(amount.compareTo(UFDouble.ZERO_DBL) != 0) {
				return amount.compareTo(UFDouble.ZERO_DBL);
			}
		}
		return 0;
	}

	private int detailDirection(NDetailVO detail) {
		UFDouble amount = GLNumUtils.sub(detail.getDebitamount(), detail.getCreditamount());
		UFDouble quantity = GLNumUtils.sub(detail.getDebitquantity(), detail.getCreditquantity());
		UFDouble localAmount = GLNumUtils.sub(detail.getLocaldebitamount(), detail.getLocalcreditamount());
		UFDouble groupAmount = GLNumUtils.sub(detail.getGroupdebitamount(), detail.getGroupcreditamount());
		UFDouble globalAmount = GLNumUtils.sub(detail.getGlobaldebitamount(), detail.getGlobalcreditamount());
		return detailDirection(localAmount, groupAmount, globalAmount, amount, quantity);
	}

	private void clearAmount(NDetailVO detail, boolean isDebit) {
		if(isDebit) {
			detail.setDebitquantity(UFDouble.ZERO_DBL);
			detail.setDebitamount(UFDouble.ZERO_DBL);
			detail.setLocaldebitamount(UFDouble.ZERO_DBL);
			detail.setGroupdebitamount(UFDouble.ZERO_DBL);
			detail.setGlobaldebitamount(UFDouble.ZERO_DBL);
		}else {
			detail.setCreditquantity(UFDouble.ZERO_DBL);
			detail.setCreditamount(UFDouble.ZERO_DBL);
			detail.setLocalcreditamount(UFDouble.ZERO_DBL);
			detail.setGroupcreditamount(UFDouble.ZERO_DBL);
			detail.setGlobalcreditamount(UFDouble.ZERO_DBL);
		}
	}

	private void loadAssInfo() throws BusinessException {
		Set<String> assidSet = new HashSet<>();
		for(String groupid : detailContainer.keySet()) {
			List<NDetailVO> details = detailContainer.get(groupid);
			if(details == null || details.size() == 0) {
				continue;
			}
			for(NDetailVO detail : details) {
				if(detail == null) {
					continue;
				}
				if(!StrTools.isEmptyStr(detail.getAssid())) {
					assidSet.add(detail.getAssid());
				}
			}
		}

		if(assidSet.size() == 0) {
			return;
		}
//		IFreevaluePub freevalue = (IFreevaluePub) NCLocator.getInstance().lookup(IFreevaluePub.class.getName());
//		GlAssVO[] glAssVOs = freevalue.queryAllByIDs(assidSet.toArray(new String[0]),null,Module.GL);
		GlAssVO[] glAssVOs = AssByPkThreadCache.getInstance().getAssVO(assidSet.toArray(new String[0]), Module.GL);
		if(glAssVOs != null && glAssVOs.length>0) {
			for(int i=0;i<glAssVOs.length;i++) {
				String assID = glAssVOs[i].getAssID();
				assVOMap.put(assID, glAssVOs[i].getAssVos());
			}
		}
	}

	private void catCashFlowCaseForSum(MapList<String, NDetailVO> details, String groupByStr, String fromStr, String sumSqlWherePart) throws BusinessException {
		if(details == null || details.size() == 0) {
			return;
		}

		String querysql = "select a.pk_detail,min(a.pk_detail) over(partition " + groupByStr + ") as groupid " + fromStr + "" + (sumSqlWherePart == null ? "" : sumSqlWherePart);

		String tablename = "T_DETAILGROUP";
		String pkfield = "pk_detail";
		String groupfield = "groupid";
		try {
			tablename = TempTableUtils.createTempTable(tablename, " " + pkfield + " varchar(40), " + groupfield + " varchar(40) ", null);
		} catch (SQLException e) {
			throw new BusinessException(e);
		}
		String insertsql = "insert into " + tablename + " (" + pkfield + "," + groupfield + ") (" + querysql + ")";
		new GLBaseDAO().excuteUpdate(insertsql);

		// 获得现金流量信息
		String[] tableinfo = new String[] {
				tablename, pkfield, groupfield
		};

		ICashFlowCase cashflowproxy = (ICashFlowCase) NCLocator.getInstance().lookup(ICashFlowCase.class.getName());
		CashflowcaseVO[] cashvo = cashflowproxy.querySumCashflow4Fip(tableinfo);

		if(cashvo == null || cashvo.length == 0) {
			return;
		}

		//补充币种信息

		MapList<String, CashflowcaseVO> cashflowCaseMapList = new MapList<>();
		for (CashflowcaseVO cashflowcaseVO : cashvo) {
			cashflowCaseMapList.put(cashflowcaseVO.getPk_detail(), cashflowcaseVO);
		}

		ArrayList<String> extGroup = new ArrayList<String>();// 需要展开不汇总的分组，这是由于该组分录合计为0可是又有现金流量造成的

		for(String groupId : details.keySet()) {
			List<NDetailVO> detailList = details.get(groupId);
			List<NDetailVO> newDetails = new ArrayList<>();
			for(NDetailVO detail : detailList) {
				List<CashflowcaseVO> cashflowList = cashflowCaseMapList.get(detail.getPk_detail());
				if(cashflowList == null || cashflowList.isEmpty()) {
					continue;
				}
				cashFlowCaseFillCurrtype(cashflowList, detail.getPk_currtype());

				if(isAllZero(detail)) {
					extGroup.add(detail.getPk_detail());
				}else {
					// 金额的校验，有现金流量的分录不允许出现借贷合计为0的情况，因为借贷合计为0的分录可能被删除，会缺失部分现金流量数据
					if(detailDirection(detail) == 0) {
						NDetailVO newDetail = (NDetailVO) detail.clone();
						clearAmount(detail, false);
						clearAmount(newDetail, true);
						newDetails.add(newDetail);
					}
					detail.setCashflow(cashflowList.toArray(new CashflowcaseVO[0]));
				}
				cashflowCaseMapList.remove(detail.getPk_detail());
			}
		}
		if (cashflowCaseMapList.size() > 0) {
			throw new BusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("glnvoucher_0","0glnvoucher-0015")/*@res "补现金流量出错，分录和现金流量表的分组不一致造成部分现金流量没有归属。"*//*-=notranslate=-*/);
		}

		//FIXME 原有逻辑在这里将合并后借贷为0，现金流量不为0的分录补充了回来，但是后续并没有使用，这里先不补充

	}


	private void catCashFlowCase(MapList<String, NDetailVO> details) throws BusinessException {
		if(details == null || details.size() == 0) {
			return;
		}

		List<String> pk_details = new ArrayList<>();
		Map<String, String> detailCurrTypeMap = new HashMap<>();
		for(String groupid : details.keySet()) {
			for(NDetailVO detail : details.get(groupid)) {
				pk_details.add(detail.getPk_detail());
				detailCurrTypeMap.put(detail.getPk_detail(), detail.getPk_currtype());
			}
		}

		ICashFlowCase cashflowproxy = (ICashFlowCase) NCLocator.getInstance().lookup(ICashFlowCase.class.getName());
		CashflowcaseVO[] cashflowcasevos = cashflowproxy.queryByPKRtDetails(pk_details.toArray(new String[0]));
		if(cashflowcasevos == null || cashflowcasevos.length == 0) {
			return;
		}

		// 如果不是总帐凭证，那么这个接口读出来的CashflowcaseVO里有可能没有币种信息，需要补上
		for(CashflowcaseVO caseVO : cashflowcasevos) {
			if(caseVO != null && StrTools.isEmptyStr(caseVO.getM_pk_currtype())) {
				caseVO.setM_pk_currtype(detailCurrTypeMap.get(caseVO.getPk_detail()));
			}
		}

		MapList<String, CashflowcaseVO> caseMapList = new MapList<>();
		for(CashflowcaseVO caseVO : cashflowcasevos) {
			caseMapList.put(caseVO.getPk_detail(), caseVO);
		}

		for(String groupid : details.keySet()) {
			for(NDetailVO detail : details.get(groupid)) {
				List<CashflowcaseVO> caseVOs = caseMapList.get(detail.getPk_detail());
				if(caseVOs == null || caseVOs.isEmpty()) {
					continue;
				}
				detail.setCashflow(caseVOs.toArray(new CashflowcaseVO[0]));
			}
		}
	}

	private void cashFlowCaseFillCurrtype(List<CashflowcaseVO> cashflowCases, String defaultCurrtype) {
		for(CashflowcaseVO caseVO : cashflowCases) {
			if(caseVO != null && StrTools.isEmptyStr(caseVO.getM_pk_currtype())) {
				caseVO.setM_pk_currtype(defaultCurrtype);
			}
		}
	}
}