package nc.bs.ic.general.rule.before;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.ic.general.util.RuleUtils;
import nc.bs.ic.pub.base.ICRule;
import nc.bs.ic.pub.env.ICBSContext;
import nc.bs.ic.pub.util.BillNameQueryUtil;
import nc.bs.ic.pub.util.DebugUtils;
import nc.itf.scmpub.reference.uap.bd.material.MaterialPubService;
import nc.vo.bd.material.MaterialVO;
import nc.vo.bd.material.stock.MaterialStockVO;
import nc.vo.ic.general.check.ICBillEntityCheck;
import nc.vo.ic.general.define.ICBillBodyVO;
import nc.vo.ic.general.define.ICBillVO;
import nc.vo.ic.general.define.InOutFlag;
import nc.vo.ic.general.define.MetaNameConst;
import nc.vo.ic.general.util.InOutHelp;
import nc.vo.ic.location.ICLocationVO;
import nc.vo.ic.material.define.InvBasVO;
import nc.vo.ic.material.define.InvCalBodyVO;
import nc.vo.ic.material.query.InvInfoQuery;
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.ml.MultiLangContext;
import nc.vo.pub.BusinessException;
import nc.vo.pub.VOStatus;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pubapp.pattern.exception.ExceptionUtils;
import nc.vo.pubapp.pattern.pub.AssertUtils;
import nc.vo.scmpub.res.billtype.ICBillType;

/**
 * 
 * @description 物料相关属性检查 单据业务单位不为空(不用检查了，v6业务单位可以为空) 换算率记结存，换算率非空
 *              如果是出库跟踪入库，则对应入库单号非空 如果是毛重管理存货，则毛重非空 V60 需要物料固定含义的辅助属性辅助属性 包括固定
 *              自由 库存状态:
 *              如果物料有“库存状态管理”标志且物料为非批次管理物料，则出入库单实收发保存时控制表体“库存状态”不能为空(未实现)
 * @scene 单据保存和修改保存
 * @param 无
 * 
 * @since 6.0
 * @version 2014-10-28 上午10:01:02
 * @author chenn
 */
public class InvAttributeCheck<E extends ICBillVO> extends ICRule<E> {

	@Override
	public void process(E[] vos) {
		long time1 = System.currentTimeMillis();
		try {
			for (E vo : vos) {
				this.checkInvAttribute(vo);
			}
		} catch (BusinessException ex) {
			ExceptionUtils.wrappBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4008001_0",
					"04008001-0174")/*
									 * @res "物料相关属性检查错误:"
									 */
					+ ex.getMessage());
		}
		long time2 = System.currentTimeMillis();
		DebugUtils.debug(" before rule: InvAttributeCheck", time2 - time1);

	}

	/**
	 * 方法功能描述：检查固定辅助属性
	 */
	protected void checkFixedAssistArr(E bill) {
		try {
			List<String> errVendorRows = new ArrayList<String>();// 供应商
			List<String> errProductRows = new ArrayList<String>();// 生产厂商
			List<String> errProjectRows = new ArrayList<String>();// 项目
			List<String> errStateRows = new ArrayList<String>();// 库存状态
			List<String> errCustRows = new ArrayList<String>();// 客户
			List<String> errCffileRows = new ArrayList<String>();// 特征码

			List<String> errVendorSrcBids = new ArrayList<String>();// 供应商来源
			List<String> errProductSrcBids = new ArrayList<String>();// 生产厂商
			List<String> errProjectSrcBids = new ArrayList<String>();// 项目
			List<String> errStateSrcBids = new ArrayList<String>();// 库存状态
			List<String> errCustSrcBids = new ArrayList<String>();// 客户
			List<String> errCffileSrcBids = new ArrayList<String>();// 特征码

			Map<String, InvBasVO> invMap = this.getInvBasMap(bill);
			String billName = new BillNameQueryUtil().getBillName(Arrays.asList(bill.getBodys()));
			// 提示信息加上具体物料
			Set<String> matSet = new HashSet<String>();

			for (ICBillBodyVO bodyVO : bill.getBodys()) {
				matSet.add(bodyVO.getCmaterialvid());
			}
			MaterialVO[] matVOs = MaterialPubService.queryMaterialBaseInfoByPks(
					matSet.toArray(new String[matSet.size()]), new String[] { MaterialVO.NAME, MaterialVO.CODE,
							MaterialVO.NAME2, MaterialVO.NAME3, MaterialVO.NAME4, MaterialVO.NAME5, MaterialVO.NAME6 });
			Map<String, String> matMap = new HashMap<String, String>();
			int langIndex = MultiLangContext.getInstance().getCurrentLangSeq();
			for (ICBillBodyVO bodyVO : bill.getBodys()) {
				for (MaterialVO matVO : matVOs) {
					if (bodyVO.getCmaterialvid().equals(matVO.getPk_material())) {
						// 根据当前登录环境的多语信息判断显示物料的哪个名称
						String showName = "";
						switch (langIndex) {
						case 2:
							showName = matVO.getName2();
							break;
						case 3:
							showName = matVO.getName3();
							break;
						case 4:
							showName = matVO.getName4();
							break;
						case 5:
							showName = matVO.getName5();
							break;
						case 6:
							showName = matVO.getName6();
							break;
						default:
							showName = matVO.getName();
							break;
						}
						if (null == showName) {
							showName = matVO.getName();
						}
						matMap.put(bodyVO.getCrowno(), matVO.getCode() + " " + showName);
					}
				}
			}
			for (ICBillBodyVO body : bill.getBodys()) {
				if (body.getStatus() == VOStatus.DELETED) {
					continue;
				}
				// fengping说 应发态单据不检查
				if (NCBaseTypeUtils.isNull(body.getNnum())) {
					continue;
				}
				String cmaterialvid = body.getCmaterialvid();
				String cpickmbid = (String) body.getAttributeValue(MetaNameConst.CPICKMBID);
				if (this.isVendorChosen(cmaterialvid, invMap) && StringUtil.isSEmptyOrNull(body.getCvendorid())) {
					errVendorRows.add(body.getCrowno());
					errVendorSrcBids.add(cpickmbid);
				}
				// 泰开项目客开
				// 项目字段根据库存信息页签-可用量按辅助属性检查是否勾选进行判断
				String pk_org = bill.getHead().getPk_org();
				Boolean needCheckProd = checkProdect(cmaterialvid, pk_org);
				if (needCheckProd && this.isProductorChosen(cmaterialvid, invMap)
						&& StringUtil.isSEmptyOrNull(body.getCproductorid())) {
					errProductRows.add(body.getCrowno());
					errProductSrcBids.add(cpickmbid);
				}
				// 项目字段根据库存信息页签-可用量按辅助属性检查是否勾选进行判断
				Boolean needCheckProject = checkProject(cmaterialvid, pk_org);
				if (needCheckProject && this.isProjectChosen(cmaterialvid, invMap)
						&& StringUtil.isSEmptyOrNull(body.getCprojectid())) {
					errProjectRows.add(body.getCrowno());
					errProjectSrcBids.add(cpickmbid);
				}
				if (this.isCustomerChosen(cmaterialvid, invMap) && StringUtil.isSEmptyOrNull(body.getCasscustid())) {
					errCustRows.add(body.getCrowno());
					errCustSrcBids.add(cpickmbid);
				}
				if (this.checkInvStateManage() && this.isInvStateManage(cmaterialvid, invMap)
						&& StringUtil.isSEmptyOrNull(body.getCstateid())) {
					errStateRows.add(body.getCrowno());
					errStateSrcBids.add(cpickmbid);
				}
				if (this.isCffileChosen(cmaterialvid, invMap) && StringUtil.isSEmptyOrNull(body.getCffileid())) {
					errCffileRows.add(body.getCrowno());
					errCffileSrcBids.add(cpickmbid);
				}
			}
			RuleUtils util = new RuleUtils();
			util.showErrByRowsWithBillInfo(errVendorRows, errVendorSrcBids, RuleRes.getVendorNullErr(), billName,
					matMap);
			util.showErrByRowsWithBillInfo(errProductRows, errProductSrcBids, RuleRes.getProductorNullErr(), billName,
					matMap);
			util.showErrByRowsWithBillInfo(errProjectRows, errProjectSrcBids, RuleRes.getProjectNullErr(), billName,
					matMap);
			util.showErrByRowsWithBillInfo(errStateRows, errStateSrcBids, RuleRes.getStateNullErr(), billName, matMap);
			util.showErrByRowsWithBillInfo(errCustRows, errCustSrcBids, RuleRes.getCustNullErr(), billName, matMap);
			util.showErrByRowsWithBillInfo(errCffileRows, errCffileSrcBids, RuleRes.getCffileNullErr(), billName,
					matMap);
		} catch (BusinessException ex) {
			ExceptionUtils.wrappException(ex);
		}

	}

	/**
	 * 根据物料查询物料库存信息页签-可用量按辅助属性检查是否勾选进行判断
	 * 
	 * @param cmaterialvid
	 * @param pk_org
	 * @return
	 */
	private Boolean checkProdect(String cmaterialvid, String pk_org) {
		String whereSql = " pk_material='" + cmaterialvid + "' and pk_org='" + pk_org + "'  and nvl(dr,0)=0 ";
		List<MaterialStockVO> list = null;
		try {
			list = (List<MaterialStockVO>) new BaseDAO().retrieveByClause(MaterialStockVO.class, whereSql);
		} catch (DAOException e) {
			e.printStackTrace();
		}
		if (list != null && list.size() > 0) {
			return list.get(0).getFixasst4()==null?false:list.get(0).getFixasst4().booleanValue();
		}
		return true;
	}

	/**
	 * 根据物料查询物料库存信息页签-可用量按辅助属性检查是否勾选进行判断
	 * 
	 * @param cmaterialvid
	 * @param pk_org
	 * @return
	 */
	private Boolean checkProject(String cmaterialvid, String pk_org) {
		String whereSql = " pk_material='" + cmaterialvid + "' and pk_org='" + pk_org + "'  and nvl(dr,0)=0 ";
		List<MaterialStockVO> list = null;
		try {
			list = (List<MaterialStockVO>) new BaseDAO().retrieveByClause(MaterialStockVO.class, whereSql);
		} catch (DAOException e) {
			e.printStackTrace();
		}
		if (list != null && list.size() > 0) {
			return list.get(0).getFixasst2()==null?false:list.get(0).getFixasst2().booleanValue();
		}
		return true;
	}

	/**
	 * 方法功能描述：毛重检查。
	 */
	protected void checkGrossWgt(InvCalBodyVO invVO, ICBillBodyVO body) throws BusinessException {
		if (!ValueCheckUtil.isTrue(invVO.getIsmngstockbygrswt()) || NCBaseTypeUtils.isNullOrZero(body.getNnum())) {
			return;
		}
		if (body.getNgrossnum() == null) {
			throw new BusinessException(RuleRes.getGrossWgtErr(body.getCrowno()));
		}
		ICLocationVO[] locvos = body.getLocationVOs();
		if (locvos != null && locvos.length > 0) {
			for (ICLocationVO locvo : locvos) {
				if (locvo.getNgrossnum() == null && !ValueCheckUtil.isTrue(invVO.getSerialmanaflag())
						&& locvo.getStatus() != VOStatus.DELETED) {
					throw new BusinessException(RuleRes.getGrossWgtErr(body.getCrowno()));
				}
			}
			ICBillEntityCheck.checkBodyLocationNum(body, new String[] { ICLocationVO.NGROSSNUM });
		}
	}

	/**
	 * 是否启用库存状态管理检 出库申请单无库存状态，不需要检查询库存状态，因此开放一个方法，方便重写
	 * 
	 * @author jinjya 2011-6-9
	 * @return
	 */
	protected boolean checkInvStateManage() {
		return true;
	}

	/**
	 * 方法功能描述：出库跟踪入库检查。
	 */
	protected void checkOutTraceIn(ICBillType billType, InvCalBodyVO invVO, ICBillBodyVO body)
			throws BusinessException {
		if (invVO.getOuttrackin() == null || !invVO.getOuttrackin().booleanValue()) {
			return;
		}
		// 出库跟踪入库,应发保存,不校验
		if (NCBaseTypeUtils.isNull(body.getNnum())) {
			return;
		}
		// 提示出物料
		String cmaterialvid = body.getCmaterialvid();
		MaterialVO matVO = MaterialPubService.queryMaterialBaseInfoByPk(cmaterialvid,
				new String[] { MaterialVO.NAME, MaterialVO.CODE });
		String matInfo = matVO.getCode() + " " + matVO.getName();

		if (InOutHelp.getRealInOutFlag(billType, body) == InOutFlag.Out
				&& (StringUtil.isSEmptyOrNull(body.getCcorrespondcode())
						|| StringUtil.isSEmptyOrNull(body.getCcorrespondhid())
						|| StringUtil.isSEmptyOrNull(body.getCcorrespondbid())
						|| StringUtil.isSEmptyOrNull(body.getCcorrespondtype()))) {
			throw new BusinessException(RuleRes.getInTraceOutErr(body.getCrowno() + " " + matInfo));
		}
	}

	/**
	 * 方法功能描述：检查换算率。
	 */
	protected void checkVchangerate(InvCalBodyVO invVO, ICBillBodyVO body) throws BusinessException {
		if (invVO.getIsstorebyconvert() == null || !invVO.getIsstorebyconvert().booleanValue()) {
			return;
		}
		if (StringUtil.isSEmptyOrNull(body.getVchangerate()) || StringUtil.isSEmptyOrNull(body.getCastunitid())) {
			throw new BusinessException(RuleRes.getVchangerateErr(body.getCrowno()));
		}
	}

	private void checkInvAttribute(E bill) throws BusinessException {
		String pk_calbody = bill.getHead().getPk_org();
		Map<String, InvCalBodyVO> invMap = new RuleUtils().fetchInvCalBodyVOs(pk_calbody, bill.getBodys());
		AssertUtils.assertValue(invMap != null && invMap.size() > 0, nc.vo.ml.NCLangRes4VoTransl.getNCLangRes()
				.getStrByID("4008001_0", "04008001-0175")/* @res "物料没有分配到库存组织!" */);
		for (ICBillBodyVO body : bill.getBodys()) {
			if (body.getStatus() == VOStatus.DELETED) {
				continue;
			}
			String pk_material = body.getCmaterialvid();
			if (invMap == null) {
				ExceptionUtils.wrappBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4008001_0",
						"04008001-0175")/* @res "物料没有分配到库存组织!" */);
				return;
			}
			InvCalBodyVO invVO = invMap.get(pk_material);
			if (invVO == null) {
				ExceptionUtils.wrappBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("4008001_0",
						"04008001-0175")/* @res "物料没有分配到库存组织!" */);
			}
			this.checkOutTraceIn(bill.getHead().getBillType(), invVO, body);
			this.checkGrossWgt(invVO, body);
			this.checkVchangerate(invVO, body);

		}
		this.checkFixedAssistArr(bill);
	}

	private Map<String, InvBasVO> getInvBasMap(E bill) {
		ICBSContext context = new ICBSContext();
		String[] cmaterialvids = VOEntityUtil.getVOsValues(bill.getBodys(), ICPubMetaNameConst.CMATERIALVID,
				String.class);
		InvInfoQuery query = context.getInvInfo();
		InvBasVO[] invvos = query.getInvBasVO(cmaterialvids);
		Map<String, InvBasVO> vidtoBasVO = CollectionUtils.hashVOArray(MaterialVO.PK_MATERIAL, invvos);
		return vidtoBasVO;
	}

	/**
	 * 
	 * 方法功能描述：
	 * <p>
	 * 物料是否勾选客户辅助属性 <b>参数说明</b>
	 * 
	 * @param cmateiralvid
	 * @param invMap
	 * @return
	 *         <p>
	 * @since 6.0
	 * @author chennn
	 * @time 2011-12-8 上午10:58:09
	 */
	private boolean isCustomerChosen(String cmateiralvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix5 = invMap.get(cmateiralvid).getFix5();
		return fix5 == null ? false : fix5.booleanValue();
	}

	/***
	 * 方法功能描述：
	 * <p>
	 * 物料是否库存状态管理 <b>参数说明</b>
	 * 
	 * @param cmaterialvid
	 */
	private boolean isInvStateManage(String cmaterialvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix1 = invMap.get(cmaterialvid).getFix1();
		return fix1 == null ? false : fix1.booleanValue();

	}

	/**
	 * 方法功能描述：
	 * <p>
	 * 物料是否勾选生产厂商辅助属性 <b>参数说明</b>
	 * 
	 * @param cmaterialvid
	 * @return
	 *         <p>
	 * @since 6.0
	 * @author chennn
	 * @time 2010-6-1 下午02:24:11
	 */
	private boolean isProductorChosen(String cmaterialvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix4 = invMap.get(cmaterialvid).getFix4();
		return fix4 == null ? false : fix4.booleanValue();
	}

	/**
	 * 方法功能描述：
	 * <p>
	 * 物料是否勾选项目辅助属性 <b>参数说明</b>
	 * 
	 * @param cmaterialvid
	 */
	private boolean isProjectChosen(String cmaterialvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix2 = invMap.get(cmaterialvid).getFix2();
		return fix2 == null ? false : fix2.booleanValue();
	}

	/*
	 * 物料是否勾选特征码辅助属性
	 */
	private boolean isCffileChosen(String cmaterialvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix100 = invMap.get(cmaterialvid).getFix100();
		return fix100 == null ? false : fix100.booleanValue();
	}

	/**
	 * 方法功能描述：
	 * <p>
	 * 物料是否勾选供应商辅助属性 <b>参数说明</b>
	 * 
	 * @param cmaterialvid
	 */
	private boolean isVendorChosen(String cmaterialvid, Map<String, InvBasVO> invMap) {
		UFBoolean fix3 = invMap.get(cmaterialvid).getFix3();
		return fix3 == null ? false : fix3.booleanValue();
	}

}
