package nc.bs.ic.general.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import nccloud.commons.lang.ArrayUtils;

import nc.bs.framework.common.NCLocator;
import nc.bs.ic.pub.env.ICBSContext;
import nc.itf.scmpub.reference.uap.group.SysInitGroupQuery;
import nc.itf.scmpub.reference.uap.pf.PfServiceScmUtil;
import nc.pubitf.ic.transtype.IInOutTransTypeQueryService;
import nc.pubitf.to.m5x.ic.IM5xTranTypeForIC;
import nc.vo.bd.material.IMaterialEnumConst;
import nc.vo.bd.material.stock.MaterialStockVO;
import nc.vo.bd.stordoc.StordocVO;
import nc.vo.ia.bill.entity.base.AbstractBaseBill;
import nc.vo.ia.bill.entity.base.AbstractBaseItemVO;
import nc.vo.ic.general.define.ICBillBodyVO;
import nc.vo.ic.general.define.ICBillHeadVO;
import nc.vo.ic.general.define.ICBillVO;
import nc.vo.ic.general.define.MetaNameConst;
import nc.vo.ic.material.define.InvCalBodyVO;
import nc.vo.ic.org.OrgInfoQuery;
import nc.vo.ic.pub.define.ICPubMetaNameConst;
import nc.vo.ic.pub.lang.RuleRes;
import nc.vo.ic.pub.util.CollectionUtils;
import nc.vo.ic.pub.util.NCBaseTypeUtils;
import nc.vo.ic.pub.util.StringUtil;
import nc.vo.ic.pub.util.VOEntityUtil;
import nc.vo.ic.pub.util.ValueCheckUtil;
import nc.vo.ic.warehouse.WhVO;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDateTime;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.pattern.exception.CarrierRuntimeException;
import nc.vo.pubapp.pattern.exception.ExceptionUtils;
import nc.vo.pubapp.pattern.log.Log;
import nc.vo.pubapp.pattern.pub.AssertUtils;
import nc.vo.pubapp.pattern.pub.Constructor;
import nc.vo.scmpub.res.billtype.IABillType;
import nc.vo.to.m5xtrantype.entity.M5xTranTypeVO;
import nc.vo.to.m5xtrantype.enumeration.FModyType;

/**
 * <p>
 * <b>库存财务处理组件：</b>
 * <ul>
 * <li>处理传存货核算(出入库单传存货核算条件)
 * <ul>
 * <li>物料是存货核算管理
 * <li>仓库传存货核算
 * <li>交易类型属性“影响成本”为“是”
 * </ul>
 * <li>处理传内部交易
 * </ul>
 * <p>
 * 
 * @version v60
 * @since v60
 * @author yangb
 * @time 2010-7-20 下午10:55:31
 */
public abstract class GenFinanceProcess<E extends ICBillVO, T extends ICBillBodyVO> {

  // 交易类型是否传存货 key --交易类型
  private Map<String, UFBoolean> toTransTypeToIA =
    new HashMap<String, UFBoolean>();

  private GeneralFinanceContext financeContext = null;

  // 根据物料+交易类型+仓库过滤后的传IA的VO
  private E[] ic2IAbills = null;

  // VMI过滤前的传TO的VO
  private E[] ic2TObills = null;

  public GenFinanceProcess(GeneralFinanceContext context) {
    super();
    this.financeContext = context;
  }

  /**
   * 处理库存财务，处理过程不会改变bills的属性
   */
  public void processFinance(E[] bills) throws BusinessException {
    List<E> lias = new ArrayList<E>();
    List<E> ltos = new ArrayList<E>();
    // 由于传IA和TO会过滤表体的行 所以拷贝VO，防止前后台合并出错
    E[] cloneBills = this.cloneBills(bills);
    this.getBilltoIAandTO(cloneBills, lias, ltos);
    if (ltos.size() > 0 && true) {//SysInitGroupQuery.isTOEnabled()获取的状态都是false
      E[] tempToBills = CollectionUtils.listToArray(ltos);
      this.ic2TObills = tempToBills;
      if (ValueCheckUtil.isNullORZeroLength(tempToBills)) {
        return;
      }
      this.saveTOSettle(tempToBills);
    }
    if (lias.size() > 0 && true) {//this.isEnableIA()获取的状态都是false
      //List<E> toiaBills= this.filterIABillsByMainBook(lias); //注掉该校验，由存货核算处理
      if (ValueCheckUtil.isNullORZeroLength(lias))
        return ;
      this.saveIABill(CollectionUtils.listToArray(lias));
    }
  }

  /**
   * 反向处理
   */
  public void unProcessFinance(E[] bills) throws BusinessException {
    if (SysInitGroupQuery.isTOEnabled()) {
      this.deleteTOStelle(bills);
    }
    if (this.isEnableIA()) {
      this.deleteIABill(VOEntityUtil.getPksFromAggVO(bills));
    }

  }


  
  public E[] getIC2IABills() {
    return this.ic2IAbills;
  }

  public E[] getIC2TOBills() {
    return this.ic2TObills;
  }
  
  /**
   * v61需求：成本域所属财务组织的主账簿启用了存货核算模块
   * 
   * @param iabills
   * @return
   */
//  private List<E> filterIABillsByMainBook(List<E> iabills) {
//    List<E> retList = new ArrayList<E>();
//    String[] pk_orgs = this.getPk_calbodys(iabills).toArray(new String[0]);
//    String[] financeOrgs =
//        new OrgInfoQuery().getFinanceOrgIDsByCalBodyID(pk_orgs);
//    Set<String> financeOrgSet = new HashSet<String>();
//    CollectionUtils.addArrayToSet(financeOrgSet, financeOrgs);
//    Map<String, String> financeOrgtoBook =
//        FinanceOrgPubService.queryMainBookID(financeOrgSet
//            .toArray(new String[0]));
//    Map<String, UFBoolean> booktoIAEnable =
//        this.getIAEnable(financeOrgtoBook.values().toArray(new String[0]));
//    for (int i = 0; i < iabills.size(); i++) {
//      if (!this.isIAEnableByMainBook(financeOrgs[i], financeOrgtoBook,
//          booktoIAEnable)) {
//       ExceptionUtils.wrappException(
//           new BusinessException(ResBase.getMainBookNotUsed()));
//      }
//      retList.add(iabills.get(i));
//    }
//
//    return retList;
//  }
//
//  private boolean isIAEnableByMainBook(String pk_financeOrg,
//      Map<String, String> financeOrgtoBook,
//      Map<String, UFBoolean> booktoIAEnable) {
//    String mainbook = financeOrgtoBook.get(pk_financeOrg);
//    return booktoIAEnable.get(mainbook) == null ? false : booktoIAEnable.get(
//        mainbook).booleanValue();
//  }
//  
//  
//  private Map<String, UFBoolean> getIAEnable(String[] mainbooks){
//    Map<String,UFBoolean> retMap= new HashMap<String,UFBoolean>();
//    for(String book:mainbooks){
//      boolean enable = AccountingBookPubService
//          .isAccountingBookAUsedMaterialBook(book);
//      retMap.put(book, UFBoolean.valueOf(enable));
//    }
//    return retMap;
//  }
//  
//  private List<String> getPk_calbodys(List<E> iabills){
//    List<String> retList= new ArrayList<String>();
//    for(E iabill:iabills){
//      retList.add(iabill.getHead().getPk_org());
//    }
//    return retList;
//  }

  @SuppressWarnings("unchecked")
  private E[] cloneBills(E[] bills) {

    E[] tempArrays = (E[]) Array.newInstance(bills[0].getClass(), bills.length);
    for (int i = 0; i < bills.length; i++) {
      tempArrays[i] = (E) bills[i].clone();

    }
    return tempArrays;
  }

  /**
   * 根据仓库是否传存货过滤单据
   * 
   * @param bills
   * @return
   */
  private E[] filterBillByWarehouse(E[] bills) {
    List<E> retBills = new ArrayList<E>();
    if (ValueCheckUtil.isNullORZeroLength(bills)) {
      return CollectionUtils.listToArray(retBills);
    }
    final String isIAWhKey = StordocVO.ISCALCULATEDINVCOST;
    ICBillHeadVO[] heads = VOEntityUtil.getHeadVOs(bills);
    String[] cwarehouseids =
      VOEntityUtil.getVOsValues(heads, ICPubMetaNameConst.CWAREHOUSEID,
          String.class);
    ICBSContext context = new ICBSContext();
    WhVO[] whvos = context.getWarehouseInfo().getWhVO(cwarehouseids);

    for (int i = 0; i < bills.length; i++) {
      String warehouseid = bills[i].getHead().getCwarehouseid();
      AssertUtils.assertValue(
          StringUtil.isStringEqual(warehouseid, cwarehouseids[i]),
          RuleRes.getUnSupportedErr());
      if (whvos[i].getAttributeValue(isIAWhKey) != null
          && UFBoolean.FALSE.equals(whvos[i].getAttributeValue(isIAWhKey))) {
        continue;
      }
      retBills.add(bills[i]);
    }
    return CollectionUtils.listToArray(retBills);
  }

  /**
   * 根据价值管理模式过滤单据
   * 
   * @return
   */
  @SuppressWarnings("unchecked")
  private E[] filterBillsByValueMode(E[] bills) {
    List<E> retBills = new ArrayList<E>();
    if (ValueCheckUtil.isNullORZeroLength(bills)) {
      return CollectionUtils.listToArray(retBills);
    }
    Class<E> billclazz = (Class<E>) bills[0].getClass();
    int[] valmodes = this.getValueModeList(bills);
    if(ArrayUtils.isEmpty(valmodes)){
      return bills;
    }
    int count = 0;
    for (E bill : bills) {
      List<T> bodys = new ArrayList<T>();
      if(ArrayUtils.isEmpty(bill.getBodys())){
    	continue;
      }
      for(ICBillBodyVO body:bill.getBodys()){
    	int valmode = valmodes[count];
      	count++;
    	if(IMaterialEnumConst.MATERIALVALUEMGT_INVCOSTING!=valmode){
    	  continue;
    	}
    	bodys.add((T)body);
      }
      if(ValueCheckUtil.isNullORZeroLength(bodys)){
    	  continue;
      }
      E tempbill = Constructor.construct(billclazz);
      tempbill.setChildrenVO(CollectionUtils.listToArray(bodys));
      tempbill.setParentVO(bill.getHead());
      retBills.add(tempbill);
    }
    return CollectionUtils.listToArray(retBills);
  }
  
  private int[] getValueModeList(E[] bills){
	  List<String> orgs = new ArrayList<String>();
	  List<String> marvids = new ArrayList<String>();
	  for(E bill:bills){
		  String org = bill.getHead().getPk_org();
		  if(ArrayUtils.isEmpty(bill.getBodys())){
			  continue;
		  }
		  for(ICBillBodyVO body:bill.getBodys()){
			  orgs.add(org);
			  marvids.add(body.getCmaterialvid());
		  }
	  }
	  if(orgs.size() == 0){
		  return new int[0];
	  }
	  int[] valmodes = new ICBSContext().getInvInfo().getInvMaterialvaluemgt(
			orgs.toArray(new String[orgs.size()]),
			marvids.toArray(new String[marvids.size()]));
	  if(ArrayUtils.isEmpty(valmodes)||orgs.size()!=valmodes.length){
		  throw new CarrierRuntimeException(RuleRes.getMatNoFianaceOrgErr());
	  }
	  return valmodes;
	  
  }

  private void getBilltoIAandTO(E[] bills, List<E> lias, List<E> ltos) {
    if (this.getFinanceContext() == null
        || this.getFinanceContext().isCrossFinanceSupport()) {
      this.splitTOAndIA(bills, lias, ltos);
    }
    if (this.getFinanceContext() != null
        && !this.getFinanceContext().isCrossFinanceSupport()) {
      for (E bill : bills) {
        lias.add(bill);
      }
    }
  }

  /**
   * 将库存单据交换为存货核算单据
   */
  protected AbstractBaseBill[] changIABills(E[] icbills, String iabilltype) {
    // 数据交换
    return PfServiceScmUtil.executeVOChange(icbills[0].getHead().getBillType()
        .getCode(), iabilltype, icbills);
  }

  /**
   * 设置存货单据的主成本域
   */
  protected void fillIABillCostOrg(AbstractBaseBill[] iabills, String[] costorgs) {
    // AssertUtils
    // .assertValue(costorgs != null, nc.vo.ml.NCLangRes4VoTransl
    // .getNCLangRes().getStrByID("4008001_0", "04008001-0213")/*@res
    // "未设置库存对应成本域"*/);
    if (costorgs == null) {
      ExceptionUtils
          .wrappBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
              .getStrByID("4008001_0", "04008001-0213")/*@res "未设置库存对应成本域"*/);
    }
    for (int i = 0; i < iabills.length; i++) {
      if (costorgs == null || costorgs[i] == null) {
        ExceptionUtils
            .wrappBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
                .getStrByID("4008001_0", "04008001-0213")/*@res "未设置库存对应成本域"*/);
        // throw new CarrierRuntimeException(nc.vo.ml.NCLangRes4VoTransl
        // .getNCLangRes().getStrByID("4008001_0", "04008001-0213")/*@res
        // "未设置库存对应成本域"*/);
      }
      iabills[i].getParentVO().setPk_org(costorgs[i]);
    }
  }

  /**
   * 按库存交易类型是否传存货过滤传存货的单据
   */
  protected E[] filterBillForIAByTransType(E[] bills) {
    List<E> lvos = new ArrayList<E>();
    if (ValueCheckUtil.isNullORZeroLength(bills)) {
      return CollectionUtils.listToArray(lvos);
    }
    for (E bill : bills) {
      if (!this.isCostAffected(bill.getHead().getCtrantypeid())) {
        continue;
      }
      lvos.add(bill);
    }
    return CollectionUtils.listToArray(lvos);
  }

  /**
   * 主数量为0，不传存货核算
   */
  @SuppressWarnings("unchecked")
  protected E[] filterBillForIAByMainNum(E[] bills) {
    if (ValueCheckUtil.isNullORZeroLength(bills)) {
      return null;
    }
    List<E> lvos = new ArrayList<E>();
    for (E bill : bills) {
      List<T> bvos = new ArrayList<T>();
      for (int i = 0; i < bill.getBodys().length; i++) {
        if (ValueCheckUtil.isNullORZeroLength(bill.getBodys())) {
          continue;
        }
        T body = (T) bill.getBodys()[i];
        if (NCBaseTypeUtils.isNullOrZero(body.getNnum())) {
          continue;
        }
        bvos.add(body);
      }
      if (ValueCheckUtil.isNullORZeroLength(bvos)) {
        continue;
      }

      // 不用Constructor，用原始单据即可，前面方法已经构造过新实例
      bill.setChildrenVO(CollectionUtils.listToArray(bvos));
      lvos.add(bill);
    }

    return CollectionUtils.listToArray(lvos);
  }

  /**
   * 根据交易类型+物料价值模式+仓库过滤单据
   * 
   * @return
   */
  protected E[] filterBillsToIA(E[] bills) {
    E[] billWithIAWh = this.filterBillByWarehouse(bills);
    E[] billWithIATran = this.filterBillForIAByTransType(billWithIAWh);
    E[] billWithValueMode = this.filterBillsByValueMode(billWithIATran);
    E[] toIABills = this.filterBillForIAByMainNum(billWithValueMode);
    return toIABills;

  }

  /**
   * 该交易类型是否传存货核算。
   */
  protected boolean isCostAffected(String trantypeid) {
    if (this.toTransTypeToIA.containsKey(trantypeid)) {
      return ValueCheckUtil.isTrue(this.toTransTypeToIA.get(trantypeid));
    }
    try {
      boolean b =
        NCLocator.getInstance().lookup(IInOutTransTypeQueryService.class)
        .isAffectCostById(trantypeid);
      this.toTransTypeToIA
      .put(trantypeid, b ? UFBoolean.TRUE : UFBoolean.FALSE);
      return b;
    }
    catch (Exception e) {
      ExceptionUtils.wrappException(e);
    }
    return false;
  }

  /**
   * 存货核算是否启用。
   */
  protected boolean isEnableIA() {

    return SysInitGroupQuery.isIAEnabled();
  }

  /**
   * 内部交易是否启用。
   */
  protected boolean isEnableTO() {
    try {
      return SysInitGroupQuery.isTOEnabled();
    }
    catch (Exception e) {
      ExceptionUtils.wrappException(e);
    }
    return false;
  }

  /**
   * 查询库存组织仓库对应的成本域
   */
  protected String[] loadCostInfoForCalbody(ICBillHeadVO[] heads) {
    String[] pk_orgs = new String[heads.length];
    String[] cwarehouseids = new String[heads.length];
    for (int i = 0; i < heads.length; i++) {
      pk_orgs[i] = heads[i].getPk_org();
      cwarehouseids[i] = heads[i].getCwarehouseid();
    }
    return this.loadCostInfoForCalbody(pk_orgs, cwarehouseids);
  }

  /**
   * 查询库存组织仓库对应的成本域
   */
  protected String[] loadCostInfoForCalbody(String[] pk_orgs,
      String[] cwarehouseids) {
    if (ValueCheckUtil.isNullORZeroLength(pk_orgs)) {
      return null;
    }
    OrgInfoQuery query = new ICBSContext().getOrgInfo();
    return query.getCostRegionPKsBy(pk_orgs, cwarehouseids);

  }

  /**
   * 查询库存组织对应的财务组织
   */
  protected String[] loadFinanceForCalbody(ICBillHeadVO[] heads) {
    return this.loadFinanceForCalbody(VOEntityUtil.getVOsValues(heads,
        ICPubMetaNameConst.PK_ORG, String.class));
  }

  /**
   * 查询库存组织对应的财务组织
   */
  protected String[] loadFinanceForCalbody(String[] pk_orgs) {
    if (ValueCheckUtil.isNullORZeroLength(pk_orgs)) {
      return null;
    }
    OrgInfoQuery query = new ICBSContext().getOrgInfo();
    return query.getFinanceOrgIDsByCalBodyID(pk_orgs);
  }

  /**
   * 传存货，不跨财务组织的
   */

  protected void saveIABill(E[] bills) throws BusinessException {

    E[] toIABills = this.filterBillsToIA(bills);
    this.ic2IAbills = toIABills;
    if (ValueCheckUtil.isNullORZeroLength(toIABills)) {
      return;
    }
    if (!this.getFinanceContext().isCorssDomainSupport()) {
      this.saveOneDomainIABills(this.getFinanceContext().getIaBillType(),
          Arrays.asList(toIABills));
    }
    if (this.getFinanceContext() != null
        && !this.getFinanceContext().isCorssDomainSupport()) {
      return;
    }
    List<E> oneDomainBills = new ArrayList<E>();
    List<E> crossDomainBills = new ArrayList<E>();
    this.loadBillsCostDomain(toIABills);
    this.splitBillByCrossDomain(toIABills, oneDomainBills, crossDomainBills);
    this.saveCrossDomainIABill(this.getFinanceContext().getIaBillType(),
        crossDomainBills);
    this.saveOneDomainIABills(this.getFinanceContext().getIaBillType(),
        oneDomainBills);

  }

  private void saveCrossDomainIABill(IABillType iaBillType,
      List<E> crossDomainBills) {
    try {
      if (ValueCheckUtil.isNullORZeroLength(crossDomainBills)) {
        return;
      }
      this.saveIAInAndOut(crossDomainBills);
      E[] icbills = CollectionUtils.listToArray(crossDomainBills);
      AbstractBaseBill[] iaBills =
        this.changIABills(icbills, iaBillType.getCode());
      LogInfo log = new LogInfo();
      UFDateTime beignTime =
        log.logIABillBeginTime(icbills, iaBillType.getCode());
      this.insertCrossDomainIAByService(iaBills, icbills);
      log.logIABillEndTime(icbills, iaBillType.getCode(), beignTime);
    }
    catch (BusinessException e) {
      ExceptionUtils.wrappException(e);
    }
  }

  protected void insertCrossDomainIAByService(AbstractBaseBill[] iaBills,
      E[] icbills) throws BusinessException {
    if (ValueCheckUtil.isNullORZeroLength(iaBills)
        || ValueCheckUtil.isNullORZeroLength(icbills)) {
      return;
    }
    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 加载支持跨成本域场景的传IA单据的成本域 ，
   * 支持的单据：材料出库和产成品入库
   * 包括主组织的成本域和非主组织的成本域（例如：材料出库的领料组织对应的成本域）
   * TO be abstract
   * 
   * @param toIABills
   */
  protected void loadBillsCostDomain(E[] toIABills) {
    return;
  }

  /**
   * 传内部交易结算，跨财务组织的通过内部交易解决
   */
  protected void saveTOSettle(E[] bills) throws BusinessException {
    if (bills == null) {
      return;
      // 子类需要覆盖实现
    }
    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 将单据分为需要内部交易及传存货的两类单据, 即：跨财务组织的通过内部交易解决 其余的需要库存考虑传存货核算。
   */
  protected void splitTOAndIA(E[] bills, List<E> lias, List<E> ltos) {
    if (bills == null || lias == null || ltos == null) {
      return;
      // 子类实现
    }
    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 将传IA的单据，划分为跨成本域和非跨成本域
   * 
   * @param bills
   * @param oneDomainBills
   * @param crossDomainBills
   */
  protected void splitBillByCrossDomain(E[] bills, List<E> oneDomainBills,
      List<E> crossDomainBills) {

    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 保存库存单据的IA对应单据，需要根据库存单据设置存货单据主组织
   * （例：库存材料出库-》存货材料出库；库存产成品入库-》存货产成品入库）
   * 
   * @param iaBills
   * @param icbills
   * @throws BusinessException
   */
  protected void insertOneDomainIAByService(AbstractBaseBill[] iaBills,
      E[] icbills) throws BusinessException {

    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 保存存货调拨入库，需要根据库存单据设置存货单据主组织
   * 
   * @param inIABills
   * @param bills
   */
  protected void insertIAInByService(AbstractBaseBill[] inIABills, E[] bills)
  throws BusinessException {
    throw new BusinessException(RuleRes.getUnSupportedErr());
  }

  /**
   * 保存存货调拨出库，需要根据库存单据设置存货单据主组织
   * 
   * @param outIABills
   * @param bills
   */
  protected void insertIAOutByService(AbstractBaseBill[] outIABills, E[] bills)
  throws BusinessException {
    throw new BusinessException(RuleRes.getUnSupportedErr());
  }

  /**
   * 传存货，删除存货单据
   */
  protected void deleteIABill(String[] billids) throws BusinessException {
    if (billids == null) {
      return;
      // 子类需要覆盖实现
    }
    ExceptionUtils.wrappException(new BusinessException(RuleRes
        .getUnSupportedErr()));
  }

  /**
   * 删除内部交易结算
   */
  protected void deleteTOStelle(E[] bills) throws BusinessException {
    if (bills == null) {
      return;
      // 子类需要覆盖实现
    }

  }

  private void saveOneDomainIABills(IABillType iaBillType, List<E> bills) {
    try {
      if (ValueCheckUtil.isNullORZeroLength(bills)) {
        return;
      }
      E[] icbills = CollectionUtils.listToArray(bills);
      AbstractBaseBill[] iaBills =
        this.changIABills(icbills, iaBillType.getCode());
      LogInfo log = new LogInfo();
      UFDateTime beignTime =
        log.logIABillBeginTime(icbills, IABillType.DBRK.getCode());
      this.insertOneDomainIAByService(iaBills, icbills);
      log.logIABillEndTime(icbills, IABillType.DBRK.getCode(), beignTime);
    }
    catch (BusinessException ex) {
      ExceptionUtils.wrappException(ex);
    }
  }

  private void saveIAInAndOut(List<E> crossDomainBills)
  throws BusinessException {
    if (ValueCheckUtil.isNullORZeroLength(crossDomainBills)) {
      return;
    }
    E[] icBills = CollectionUtils.listToArray(crossDomainBills);
    AbstractBaseBill[] iaInBills =
      this.changIABills(icBills, IABillType.DBRK.getCode());
    AbstractBaseBill[] iaOutBills =
      this.changIABills(icBills, IABillType.DBCK.getCode());
    LogInfo log = new LogInfo();
    UFDateTime beignTime =
      log.logIABillBeginTime(icBills, IABillType.DBRK.getCode());
    this.insertIAInByService(iaInBills, icBills);
    log.logIABillEndTime(icBills, IABillType.DBRK.getCode(), beignTime);

    UFDateTime begin =
      log.logIABillBeginTime(icBills, IABillType.DBCK.getCode());
    this.insertIAOutByService(iaOutBills, icBills);
    log.logIABillEndTime(icBills, IABillType.DBCK.getCode(), begin);

  }

  public void setFinanceContext(GeneralFinanceContext financeContext) {
    this.financeContext = financeContext;
  }

  public GeneralFinanceContext getFinanceContext() {
    return this.financeContext;
  }

  class LogInfo {
    /**
     * 请在存货接口前调用 传存货的日志
     */
    public void logIABillBegin(E[] bills, String iabilltype) {
      Log.info(">>>>>开始执行" + bills[0].getHead().getBillType().getCode()/*-=notranslate=-*/
          + "传存货核算" + iabilltype + "<<<<<");/*-=notranslate=-*/
      Log.info(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID(
          "4008001_0", "04008001-0216")/*@res ">>>>>库存单据为："*/
          + StringUtil.mergeString(VOEntityUtil.getVOsValues(
              VOEntityUtil.getHeadVOs(bills), MetaNameConst.CGENERALHID,
              String.class), null, ",") + "<<<<<");
      this.logDetailMessage(bills);
    }

    public UFDateTime logIABillBeginTime(E[] bills, String iabilltype) {
      this.logIABillBegin(bills, iabilltype);
      return this.logTime();
    }

    public void logIABillEndTime(E[] bills, String iabilltype,
        UFDateTime beginTime) {
      UFDateTime endTime = this.logTime();
      this.logIABillEnd(bills, iabilltype);
      if (beginTime == null) {
        return;
      }
      Log.info(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID(
          "4008001_0", "04008001-0217")/*@res ">>>>>操作耗时(秒)："*/
          + UFDateTime.getSecondsBetween(beginTime, endTime) + ">>>>>");
    }

    /**
     * 请在存货接口后调用 传存货的日志
     */
    public void logIABillEnd(E[] bills, String iabilltype) {
      Log.info(">>>>>执行" + bills[0].getHead().getBillType().getCode() + "传存货核算"/*-=notranslate=-*/
          + iabilltype + "结束<<<<<");/*-=notranslate=-*/
      Log.info(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID(
          "4008001_0", "04008001-0220")/*@res ">>>>>传递的库存单据为："*/
          + StringUtil.mergeString(VOEntityUtil.getVOsValues(
              VOEntityUtil.getHeadVOs(bills), MetaNameConst.CGENERALHID,
              String.class), null, ",") + "<<<<<");
      this.logDetailMessage(bills);
    }

    private void logDetailMessage(E[] bills) {
      for (E bill : bills) {
        StringBuilder msg = new StringBuilder();
        for (ICBillBodyVO body : bill.getBodys()) {
          msg.append(">>>>>库存单据表体ID：").append(body.getCgeneralbid())/*-=notranslate=-*/
          .append(", 对应表头ID：").append(body.getCgeneralhid())/*-=notranslate=-*/
          .append("<<<<<");
          Log.info(msg.toString());
        }
      }
    }

    private UFDateTime logTime() {
      UFDateTime dateTime = new UFDateTime(System.currentTimeMillis());
      Log.info(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID(
          "4008001_0", "04008001-0223")/*@res ">>>>>现在时间为："*/
          + dateTime + "<<<<<");
      return dateTime;
    }
  }

  /**
   * 传内部交易前过滤表体寄存供应商的行,适用单据采购入库
   */
  protected E[] filterVmiRow(E[] bills) {
    List<E> retBills = new ArrayList<E>();
    @SuppressWarnings("unchecked")
    Class<E> billclazz = (Class<E>) bills[0].getClass();
    for (E bill : bills) {
      List<T> tempbodys = new ArrayList<T>();
      for (int i = 0; i < bill.getBodys().length; i++) {
        @SuppressWarnings("unchecked")
        T body = (T) bill.getBodys()[i];
        if (!StringUtil.isSEmptyOrNull((String) body
            .getAttributeValue(ICPubMetaNameConst.CVMIVENDERID))) {
          continue;
        }
        tempbodys.add(body);

      }
      if (ValueCheckUtil.isNullORZeroLength(tempbodys)) {
        continue;
      }
      E tempbill = Constructor.construct(billclazz);
      tempbill.setParentVO(bill.getHead());
      tempbill.setChildrenVO(CollectionUtils.listToArray(tempbodys));
      retBills.add(tempbill);
    }
    return CollectionUtils.listToArray(retBills);

  }

  /**
   * 返回每行对应的调拨类型
   * 
   * @param bills
   * @return
   */
  private Map<String, Integer> fetchTransMode(E[] bills) {
    Map<String, Integer> retMap = new HashMap<String, Integer>();
    try {

      String[] frtTranstypes = new String[0];
      for (E bill : bills) {
        frtTranstypes =
          CollectionUtils.combineArrs(VOEntityUtil.getVOsNotRepeatValue(
              bill.getBodys(), MetaNameConst.CFIRSTTRANSTYPE), frtTranstypes);
      }
      M5xTranTypeVO[] m5xTranstypes =
        NCLocator.getInstance().lookup(IM5xTranTypeForIC.class)
        .queryTranTypeForIC(frtTranstypes);
      Map<String, M5xTranTypeVO> tranMap =
        CollectionUtils.hashVOArray(M5xTranTypeVO.CTRANTYPEID, m5xTranstypes);
      for (E bill : bills) {
        for (int i = 0; i < bill.getBodys().length; i++) {
          @SuppressWarnings("unchecked")
          T icBody = (T) bill.getBodys()[i];
          retMap.put(icBody.getCgeneralbid(),
              tranMap.get(icBody.getCfirsttranstype()).getFmodetype());
        }
      }
      return retMap;

    }
    catch (BusinessException e) {
      ExceptionUtils.wrappException(e);
    }
    return retMap;
  }

  private Map<String, UFBoolean> isVmiTransfer(E[] bills) {
    Map<String, Integer> transmode = this.fetchTransMode(bills);
    Map<String, UFBoolean> retMap = new HashMap<String, UFBoolean>();
    for (Entry<String, Integer> entry : transmode.entrySet()) {
      if (FModyType.JC_MODETYPE.value().equals(entry.getValue())) {
        retMap.put(entry.getKey(), UFBoolean.TRUE);
      }
      else {
        retMap.put(entry.getKey(), UFBoolean.FALSE);
      }
    }
    return retMap;
  }

  protected E[] filterVmiRowFormTO(E[] bills) {
    List<E> retBills = new ArrayList<E>();
    @SuppressWarnings("unchecked")
    Class<E> billclazz = (Class<E>) bills[0].getClass();
    Map<String, UFBoolean> modeMap = this.isVmiTransfer(bills);
    for (E bill : bills) {
      List<T> tempbodys = new ArrayList<T>();
      for (int i = 0; i < bill.getBodys().length; i++) {
        @SuppressWarnings("unchecked")
        T body = (T) bill.getBodys()[i];
        // 寄存调拨的不传内部交易
        if (modeMap.get(body.getCgeneralbid()).booleanValue()) {
          continue;
        }
        tempbodys.add(body);

      }
      if (ValueCheckUtil.isNullORZeroLength(tempbodys)) {
        continue;
      }
      E tempbill = Constructor.construct(billclazz);
      tempbill.setParentVO(bill.getHead());
      tempbill.setChildrenVO(CollectionUtils.listToArray(tempbodys));
      retBills.add(tempbill);
    }
    return CollectionUtils.listToArray(retBills);

  }

  /**
   * 传IA前处理IA的VO，
   * 根据库存组织处理是否传批次信息，
   * 适用于材料出库传存货调入和材料出库
   * 产成品入库传
   * 
   * @param pk_orgs
   * @param iabills
   */
  protected void processIABills(String[] pk_calbodys, AbstractBaseBill[] iabills) {
    for (int i = 0; i < iabills.length; i++) {
      if (StringUtil.isSEmptyOrNull(pk_calbodys[i])) {
        continue;
      }
      String[] cmaterialvids =
        VOEntityUtil.getVOsValues(iabills[i].getChildrenVO(),
            AbstractBaseItemVO.CINVENTORYVID, String.class);
      InvCalBodyVO[] calbodyvos =
        new ICBSContext().getInvInfo().getInvCalBodyVO(pk_calbodys[i],
            cmaterialvids);
      Map<String, InvCalBodyVO> invMap =
        CollectionUtils.hashVOArrayByMultiKeys(new String[] {
            MaterialStockVO.PK_ORG, MaterialStockVO.PK_MATERIAL
        }, calbodyvos, null);
      for (AbstractBaseItemVO itemvo : iabills[i].getChildrenVO()) {
        InvCalBodyVO calbodyvo =
          invMap.get(StringUtil.mergeTowString(pk_calbodys[i],
              itemvo.getCinventoryvid()));
        AssertUtils.assertValue(calbodyvo != null, RuleRes.getUnSupportedErr());
        if (calbodyvo != null
            && ValueCheckUtil.isTrue(calbodyvo.getWholemanaflag())) {
          continue;
        }
        itemvo.setVbatchcode(null);
      }

    }
  }

  /**
   * 传存货之前要设置赠品行的成本单价金额为0,有需要的单据子类在传存货之前调用
   * 
   * @param bills
   */
  protected void setFlargessCostPriceMnyZeroForIA(E[] bills) {
    for (E e : bills) {
      for (ICBillBodyVO body : e.getBodys()) {
        UFBoolean flargess =
          (UFBoolean) body.getAttributeValue(MetaNameConst.FLARGESS);
        if (ValueCheckUtil.isTrue(flargess)) {
          body.setNcostprice(UFDouble.ZERO_DBL);
          body.setNcostmny(UFDouble.ZERO_DBL);
        }
      }
    }
  }

}
