package nc.impl.fa.reduce;

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

import nc.bs.am.framework.action.ActionContext;
import nc.bs.am.framework.action.IActionTemplate;
import nc.bs.am.framework.action.IRule;
import nc.bs.am.framework.action.approve.ApproveActionTemplate;
import nc.bs.am.framework.action.approve.UnApproveActionTemplate;
import nc.bs.am.framework.common.rule.ValidateServiceRule;
import nc.bs.dao.DAOException;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.uif2.validation.Validator;
import nc.impl.am.bill.BillBaseDAO;
import nc.impl.am.bill.rule.BillCodeDeleteRule;
import nc.impl.am.bill.rule.BillCodeInsertAfterRule;
import nc.impl.am.bill.rule.BillCodeInsertBeforeRule;
import nc.impl.am.bill.rule.BillCodeUpdateRule;
import nc.impl.am.bill.rule.VOSagasStatusCheckBeforeRule;
import nc.impl.am.common.InSqlManager;
import nc.impl.am.db.DBAccessUtil;
import nc.impl.am.db.QueryUtil;
import nc.impl.am.db.processor.BooleanProcessor;
import nc.impl.am.db.processor.ColumnSetProcessor;
import nc.impl.fa.common.rule.CreateBillLogAfterRule;
import nc.impl.fa.common.rule.DeleteBillLogAfterRule;
import nc.impl.fa.common.rule.UpdateCardLogAfterRule;
import nc.impl.fa.common.validator.CheckCardLegalValidator;
import nc.impl.fa.common.validator.CheckMinUnClosedBookPeriodValidator;
import nc.impl.fa.reduce.rule.CreateReduceBodyInfoAfterRule;
import nc.impl.fa.reduce.rule.DeleteReduceBodyInfoAfterRule;
import nc.impl.fa.reduce.rule.ReduceSendFipAfterRule;
import nc.impl.fa.reduce.rule.ReduceUpdateDepLogAfterRule;
import nc.impl.fa.reduce.rule.ReduceWriteBackToCardhistoryAfterRule;
import nc.impl.fa.reduce.rule.SendARCReduceRule;
import nc.impl.fa.reduce.rule.UpdateBillStatusOfWorkFlowAfterRule;
import nc.impl.fa.reduce.rule.WriteBackEquipCardAfterRule;
import nc.impl.fa.rule.FAAccbookCheckSealRule;
import nc.impl.fa.rule.PKAccbookBeforeRule;
import nc.impl.fa.rule.ReceiptScanBeforeRule;
import nc.impl.fa.rule.UpdateSrcMsgState4BillAfterRule;
import nc.itf.fa.prv.IReduce;
import nc.itf.fa.service.IAccbookInfoService;
import nc.itf.fa.service.IAssetService;
import nc.itf.fa.service.ICloseBookService;
import nc.itf.fa.service.IDepmethodService;
import nc.itf.fa.service.ILogService;
import nc.jdbc.framework.SQLParameter;
import nc.jdbc.framework.processor.ColumnListProcessor;
import nc.pub.fa.card.AssetFieldConst;
import nc.pub.fa.card.AssetUseConst;
import nc.pub.fa.common.checker.DepChecker;
import nc.pub.fa.common.consts.BillTypeConst;
import nc.pub.fa.common.consts.FlagConsts;
import nc.pub.fa.common.consts.LogTypeConst;
import nc.pub.fa.common.manager.DeptScaleManager;
import nc.pub.fa.common.manager.FipManager;
import nc.pub.fa.common.manager.VOManager;
import nc.pub.fa.dep.ReduceSimulateManager;
import nc.pub.fa.msg.MsgManager;
import nc.pub.fa.msg.MsgVO;
import nc.vo.am.common.AMSuperVO;
import nc.vo.am.common.BaseLockData;
import nc.vo.am.common.MappedBaseVO;
import nc.vo.am.common.TransportBillVO;
import nc.vo.am.common.util.ArrayUtils;
import nc.vo.am.common.util.BillTransportTool;
import nc.vo.am.common.util.CollectionUtils;
import nc.vo.am.common.util.ExceptionUtils;
import nc.vo.am.common.util.MapUtils;
import nc.vo.am.common.util.OrgUtils;
import nc.vo.am.common.util.StringUtils;
import nc.vo.am.common.util.UFDoubleUtils;
import nc.vo.am.constant.BillTypeConst_FA;
import nc.vo.am.constant.CommonKeyConst;
import nc.vo.am.manager.AccbookManager;
import nc.vo.am.manager.AccperiodVO;
import nc.vo.am.manager.CurrencyManager;
import nc.vo.am.manager.CurrencyRateManager;
import nc.vo.am.manager.LockManager;
import nc.vo.am.proxy.AMProxy;
import nc.vo.am.pub.uap.ModuleInfoQuery;
import nc.vo.fa.asset.AssetVO;
import nc.vo.fa.depmethod.DepmethodVO;
import nc.vo.fa.deptscale.DeptScaleVO;
import nc.vo.fa.reduce.ReduceBodyVO;
import nc.vo.fa.reduce.ReduceHeadVO;
import nc.vo.fa.reduce.ReduceMsgConst;
import nc.vo.fa.reduce.ReduceVO;
import nc.vo.fa.reduce.helper.ReduceVOGenerator;
import nc.vo.fa.reduce.validator.ReduceCheckHasVoucherForAccbooks;
import nc.vo.pub.BusinessException;
import nc.vo.pub.SuperVO;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDate;
import nc.vo.pub.lang.UFDouble;
import nc.vo.uif2.LoginContext;

/**
 * 资产减少后台操作类
 *
 * @author weizq
 * @date 2009-12-31
 *
 */
public class ReduceImpl extends BillBaseDAO<ReduceVO> implements IReduce {

	/**
	 * 对界面的上的VO进行删除操作，前台调用的接口
	 *
	 * @param context
	 * @param vo
	 * @throws BusinessException
	 * @see nc.itf.fa.prv.IReduce#delete(nc.vo.uif2.LoginContext,
	 *      nc.vo.fa.reduce.ReduceVO)
	 */
	@Override
	public void delete(LoginContext context, ReduceVO billVO) throws BusinessException {

		// 重新获取数据，界面传送过来的仅为pk 和ts
		billVO = reloadReduceVO(billVO.getPrimaryKey());

		// 对全VO进行删除
		deleteReduceVO(context, billVO);
	}

	/*
	 * 影像功能适配
	 */
	@Override
	protected void initCommitActionRules(IActionTemplate<ReduceVO> template) {
		super.initCommitActionRules(template);
		// saga状态校验
		template.addBeforeRule(new VOSagasStatusCheckBeforeRule<ReduceVO>());
		// 影像规则
		template.addBeforeRule(new ReceiptScanBeforeRule<ReduceVO>());
	}

	/**
	 * 对全VO进行删除操作，后台调用请调用这个。不进行流量处理
	 *
	 * @param context
	 * @param vo
	 * @throws BusinessException
	 * @date 2010-5-26
	 */
	protected void deleteReduceVO(LoginContext context, ReduceVO billVO) throws BusinessException {
		deleteBillVO(billVO);
	}

	/**
	 * 新增单据
	 *
	 * @param context
	 * @param billVO
	 * @return
	 * @throws BusinessException
	 */
	@Override
	public TransportBillVO insert(LoginContext context, ReduceVO billVO) throws BusinessException {
		// 调用不进行流量处理的方法
		billVO = insertReduceVO(context, billVO);

		// 流量处理。转化VO，只传送界面不存在的字段,pk,ts,creator...
		TransportBillVO transportBillVO = BillTransportTool.createTransportBill(billVO, BillTransportTool.MODE_INSERT);

		return transportBillVO;
	}

	/**
	 * 不进行流量处理的保存方法
	 *
	 * @param context
	 * @param billVO
	 * @return
	 * @throws BusinessException
	 * @date 2010-5-26
	 */
	protected ReduceVO insertReduceVO(LoginContext context, ReduceVO billVO) throws BusinessException {
		// 判断折旧制单后做减少
		checkAfterDapMade(billVO);

		// 处理多币种
		dealMultiCurrency(billVO);

		// 处理表体账簿
		dealBodyAccbook(billVO);

		ReduceVO[] retReduceVOs = insertBillVO(billVO);
		// 只取第一个
		ReduceVO reduceVO = ArrayUtils.getFirstElem(retReduceVOs);

		// 不能使用finally块进行:因为保存报错了，前台依然处于编辑状态，应该由取消按钮来解锁 或者 保存成功了解锁。
		releasePKCardLock(billVO);

		return reduceVO;
	}

	/**
	 *
	 * 如果是表头账簿是空， 则表体账簿也是~
	 * <p>
	 * <b>参数说明</b>
	 * 
	 * @param billVO
	 *               <p>
	 * @author weizq
	 * @time 2010-12-30 下午03:44:48
	 */
	private void dealBodyAccbook(ReduceVO billVO) {
		ReduceHeadVO headVO = (ReduceHeadVO) billVO.getParent();

		ReduceBodyVO[] bodyVOs = (ReduceBodyVO[]) billVO.getChildrenVO();

		String pk_accbook = headVO.getPk_accbook();
		if (AccbookManager.isBizAccbook(pk_accbook)) {
			VOManager.setAttributeValue(bodyVOs, AssetFieldConst.PK_ACCBOOK, FlagConsts.FLAG_NULL);
		}
	}

	/**
	 * 插入前规则类
	 * 
	 * @param insertAction
	 * @see nc.impl.am.bill.BillBaseDAO#initInsertActionRules(nc.impl.am.bill.action.BillInsertAction)
	 */
	@Override
	protected void initInsertActionRules(IActionTemplate<ReduceVO> insertAction) {
		super.initInsertActionRules(insertAction);
		// 保存校验类
		List<Validator> validators = new ArrayList<Validator>();
		// 添加是否启用账簿的校验
		validators.add(new FAAccbookCheckSealRule());
		// 校验卡片合法性
		validators.add(new CheckCardLegalValidator());
		// 校验最小未结账月
		validators.add(new CheckMinUnClosedBookPeriodValidator());
		// 添加判断财务是否已经制成折旧清单的校验。
		validators.add(new ReduceCheckHasVoucherForAccbooks());

		insertAction.addBeforeRule(new ValidateServiceRule<ReduceVO>(validators));

		// 固定资产账簿处理
		insertAction.addBeforeRule(new PKAccbookBeforeRule<ReduceVO>());
		// 单据号处理
		insertAction.addBeforeRule(new BillCodeInsertBeforeRule<ReduceVO>());
		// 单据号处理
		insertAction.addAfterRule(new BillCodeInsertAfterRule<ReduceVO>());
		// 增加更新卡片日志规则，更新【bill_code，bill_type，bill_status】
		insertAction.addAfterRule(new UpdateCardLogAfterRule<ReduceVO>(false));
		// 更新消息来源状态
		insertAction.addAfterRule(new UpdateSrcMsgState4BillAfterRule<ReduceVO>());
	}

	@Override
	protected void initPushSaveActionRules(IActionTemplate<ReduceVO> insertAction) {
		super.initPushSaveActionRules(insertAction);
		// 保存校验类
		List<Validator> validators = new ArrayList<Validator>();
		// 校验卡片合法性
		validators.add(new CheckCardLegalValidator());
		// 校验最小未结账月
		validators.add(new CheckMinUnClosedBookPeriodValidator());
		insertAction.addBeforeRule(new ValidateServiceRule<ReduceVO>(validators));

		// 增加更新卡片日志规则，更新【bill_code，bill_type，bill_status】
		insertAction.addBeforeRule(new UpdateCardLogAfterRule<ReduceVO>(false));
		// 固定资产账簿处理
		insertAction.addBeforeRule(new PKAccbookBeforeRule<ReduceVO>());
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void initApproveRule(ApproveActionTemplate<ReduceVO> template) {
		// 初始化审批校验类列表.
		List<Validator> validators = new ArrayList<Validator>();
		// 添加是否启用账簿的校验
		validators.add(new FAAccbookCheckSealRule());
		// 添加判断财务是否已经制成折旧清单的校验。
		// validators.add(new CheckHasFipMakeVoucherForAccbooks());
		validators.add(new ReduceCheckHasVoucherForAccbooks());
		// 校验最小未结账月
		validators.add(new CheckMinUnClosedBookPeriodValidator());
		// saga状态校验
		template.addApprovedBeforeRule(new VOSagasStatusCheckBeforeRule<ReduceVO>());
		template.addApprovedBeforeRule(new ValidateServiceRule<ReduceVO>(validators));

		// 配置业务规则
		// 审核通过后规则
		List<IRule<ReduceVO>> approvePassAfterRules = new ArrayList<IRule<ReduceVO>>();

		// 工作流处理结束,更新单据状态
		approvePassAfterRules.add(new UpdateBillStatusOfWorkFlowAfterRule<ReduceVO>());

		// reduce接口
		// 审核后要更新fa_log表中的卡片日志
		approvePassAfterRules.add(new UpdateCardLogAfterRule<ReduceVO>(true));

		// 1. 生成表体------其中有一部分逻辑处理是否重新计提
		approvePassAfterRules.add(new CreateReduceBodyInfoAfterRule<ReduceVO>());

		// 2. 重新计提标志
		approvePassAfterRules.add(new ReduceUpdateDepLogAfterRule());

		// 3. 清空设备卡片财务数据
		approvePassAfterRules.add(new WriteBackEquipCardAfterRule<ReduceVO>(true));

		// 4. 回写卡片历史表
		approvePassAfterRules.add(new ReduceWriteBackToCardhistoryAfterRule(true));

		// 5. 发送会计平台
		approvePassAfterRules.add(new ReduceSendFipAfterRule(true));

		// 6. 添加单据的fa_log记录
		approvePassAfterRules.add(new CreateBillLogAfterRule<ReduceVO>(LogTypeConst.reduce));
		
		// 7. 推送给ARC
//		approvePassAfterRules.add(new SendARCReduceRule());

		// 审批通过后规则
		template.addApprovePassAfterRule(approvePassAfterRules);
	}

	@Override
	protected void initUnApproveRule(UnApproveActionTemplate<ReduceVO> template) {
		// 保存校验类
		List<Validator> validators = new ArrayList<Validator>();
		// 添加是否启用账簿的校验
		validators.add(new FAAccbookCheckSealRule());
		// 最小未结账月
		validators.add(new CheckMinUnClosedBookPeriodValidator());
		// 添加判断财务是否已经制成折旧清单的校验。
		validators.add(new ReduceCheckHasVoucherForAccbooks());

		// 弃审前业务规则
		List<IRule<ReduceVO>> unApprovedBeforeRules = new ArrayList<IRule<ReduceVO>>();
		// saga状态校验
		unApprovedBeforeRules.add(new VOSagasStatusCheckBeforeRule<ReduceVO>());
		unApprovedBeforeRules.add(new ValidateServiceRule<ReduceVO>(validators));
		// 添加取消审核前业务规则
		template.addUnApprovedBeforeRules(unApprovedBeforeRules);

		// 最后取消审核人取消审核业务规则
		List<IRule<ReduceVO>> lastUnApprovedAfterRules = new ArrayList<IRule<ReduceVO>>();
		// cancelReduce接口
		// 1. 删除表体账簿不是"N/A"数据
		lastUnApprovedAfterRules.add(new DeleteReduceBodyInfoAfterRule<ReduceVO>());
		// 2. 回写卡片历史表
		lastUnApprovedAfterRules.add(new ReduceWriteBackToCardhistoryAfterRule(false));
		// 3. 重新计提标志
		lastUnApprovedAfterRules.add(new ReduceUpdateDepLogAfterRule());
		// 4. 发送会计平台(删除)
		lastUnApprovedAfterRules.add(new ReduceSendFipAfterRule(false));

		// 5. 删除单据日志
		lastUnApprovedAfterRules.add(new DeleteBillLogAfterRule<ReduceVO>());

		// 6. 更新卡片日志
		lastUnApprovedAfterRules.add(new UpdateCardLogAfterRule<ReduceVO>(false));

		// 7. 还原设备卡片的财务数据
		lastUnApprovedAfterRules.add(new WriteBackEquipCardAfterRule<ReduceVO>(false));

		template.addLastUnApprovedAfterRules(lastUnApprovedAfterRules);
	}

	/**
	 * 初始化收回业务规则。
	 * 
	 * @author zhangypm
	 * @time 2020年1月10日
	 */
	@Override
	protected void initUnSaveActionRules(IActionTemplate<ReduceVO> template) {
		super.initUnSaveActionRules(template);
		// saga状态校验
		template.addBeforeRule(new VOSagasStatusCheckBeforeRule<ReduceVO>());
	}

	@Override
	protected void initDeleteActionRules(IActionTemplate<ReduceVO> deleteAction) {
		super.initDeleteActionRules(deleteAction);
		// saga状态校验
		deleteAction.addBeforeRule(new VOSagasStatusCheckBeforeRule<ReduceVO>());
		// 增加更新卡片日志规则，清空【bill_code，bill_type，bill_status】
		deleteAction.addBeforeRule(new UpdateCardLogAfterRule<ReduceVO>(false, true));

		deleteAction.addAfterRule(new BillCodeDeleteRule<ReduceVO>());
	}

	@Override
	protected void initUpdateActionRules(IActionTemplate<ReduceVO> updateAction) {
		super.initUpdateActionRules(updateAction);
		// 更新校验类
		List<Validator> validators = new ArrayList<Validator>();
		// 校验卡片合法性
		validators.add(new CheckCardLegalValidator());
		// 校验最小未结账月
		validators.add(new CheckMinUnClosedBookPeriodValidator());
		updateAction.addBeforeRule(new ValidateServiceRule<ReduceVO>(validators));

		updateAction.addBeforeRule(new BillCodeUpdateRule<ReduceVO>());
		updateAction.addBeforeRule(new PKAccbookBeforeRule<ReduceVO>());
	}

	/*
	 * 前台的保存方法，经过流量处理
	 * 
	 * @date 2010-5-26
	 */
	@Override
	public TransportBillVO update(LoginContext context, ReduceVO billVO) throws BusinessException {

		// 调用保存方法
		billVO = updateReduceVO(context, billVO);

		// 流量处理
		TransportBillVO transportBillVO = BillTransportTool.createTransportBill(billVO, BillTransportTool.MODE_UPDATE);
		return transportBillVO;
	}

	/**
	 * 不进行流量处理的更新保存方法
	 *
	 * @param context
	 * @param billVO
	 * @return
	 * @throws BusinessException
	 * @date 2010-5-26
	 */
	protected ReduceVO updateReduceVO(LoginContext context, ReduceVO billVO) throws BusinessException {

		// 折旧制单后判断
		checkAfterDapMade(billVO);

		// 加单据锁
		LockManager.lock(new BaseLockData<ReduceVO>(billVO));

		// 更新单据时，需要先删除旧表体卡片的日志，再新增新表体卡片的日志
		ReduceHeadVO headVO = (ReduceHeadVO) billVO.getParentVO();
		String[] pk_cards = queryBodyCardId(headVO.getPrimaryKey(), headVO.getPk_accbook());
		String bill_code = headVO.getBill_code();
		String bill_type = headVO.getBill_type();
		Integer bill_status = headVO.getBill_status();
		String pk_accbook = headVO.getPk_accbook();

		// 先清空原先的卡片日志中bill_code
		updateCardLog(pk_accbook, null, null, null, pk_cards);

		// 处理多币种
		dealMultiCurrency(billVO);

		// 更新操作的持久化
		ReduceVO reduceVO = ArrayUtils.getFirstElem(updateBillVO(new ReduceVO[] { billVO }, null));

		// 再新增新表体卡片的日志
		pk_cards = queryBodyCardId(headVO.getPrimaryKey(), headVO.getPk_accbook());

		// 再重新更新卡片日志中的bill_code
		updateCardLog(pk_accbook, bill_code, bill_type, bill_status, pk_cards);

		// 对卡片解锁:
		// 不能使用finally块进行:因为保存报错了，前台依然处于编辑状态，应该由取消按钮来解锁 或者 保存成功了解锁。
		releasePKCardLock(billVO);

		return reduceVO;
	}

	/*
	 * 前台调用的提交方法，处理流量
	 * 
	 * @date 2010-5-26
	 */
	@Override
	public ReduceVO commit(LoginContext context, ReduceVO billVO) throws BusinessException {
		// 审核
		billVO = commitReduceVO(context, billVO);

		return billVO;

	}

	/**
	 * 收回
	 * 
	 * @param vos
	 * @return
	 * @throws BusinessException
	 */
	@Override
	public Object unSave(ReduceVO[] vos) throws BusinessException {
		return super.unSaveBillVOs(vos);
	}

	/**
	 * 后台调用的提交方法，不处理流量
	 *
	 * @param context
	 * @param billVO
	 * @return
	 * @throws BusinessException
	 * @date 2010-5-26
	 */
	protected ReduceVO commitReduceVO(LoginContext context, ReduceVO billVO) throws BusinessException {
		billVO = ArrayUtils.getFirstElem(commitBillVO(billVO));
		return billVO;
	}

	/**
	 * 后台解卡片锁: 注意：解锁不一定都需要放在finally代码块中，得根据具体的业务流程进行考虑，带用本方法时必须要注释说明是否需要finally块。
	 *
	 * @param billVO
	 * @param context
	 * @throws BusinessException
	 * @date 2010-1-5
	 */
	private void releasePKCardLock(ReduceVO billVO) throws BusinessException {
		if (billVO == null || ArrayUtils.isEmpty(billVO.getChildrenVO()))
			return;

		ReduceBodyVO[] bodyVOs = (ReduceBodyVO[]) billVO.getChildrenVO();
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < bodyVOs.length; i++) {
			if (StringUtils.isNotEmpty(bodyVOs[i].getPk_card())) {
				list.add(bodyVOs[i].getPk_card());
			}
		}
		String[] pk_cardsAry = CollectionUtils.convertToArray(list, new String[0]);
		if (ArrayUtils.isNotEmpty(pk_cardsAry)) {
			LockManager.releasePKLocks(pk_cardsAry, InvocationInfoProxy.getInstance().getUserId());
		}
	}

	/**
	 * 取消减少:和减少接口正好相反<br>
	 * 1：判断最小未结账月<br>
	 * 2：删除减少表体（减少单则不需要）<br>
	 * 3：回写被减少的卡片数据<br>
	 * 4：如影响折旧则删除折旧标志<br>
	 * 5：删除会计平台的减少数据<br>
	 * 6：
	 *
	 * @param pk_bill 表体的pk_bill字段，比如：该字段值为对应调出单表头的ID
	 * @date 2010-1-5
	 */
	protected void cancelReduce(SuperVO headVO) throws BusinessException {
		ReduceVO billVO = new ReduceVO();
		billVO.setParent(headVO);
		billVO.setChildrenVO(new AMSuperVO[0]);

		List<IRule<ReduceVO>> lastUnApprovedAfterRules = new ArrayList<IRule<ReduceVO>>();

		// 1. 删除表体账簿不是"N/A"数据
		lastUnApprovedAfterRules.add(new DeleteReduceBodyInfoAfterRule<ReduceVO>());
		// 2. 回写卡片历史表
		lastUnApprovedAfterRules.add(new ReduceWriteBackToCardhistoryAfterRule(false));
		// 3. 重新计提标志
		lastUnApprovedAfterRules.add(new ReduceUpdateDepLogAfterRule());
		// 4. 发送会计平台(删除)
		lastUnApprovedAfterRules.add(new ReduceSendFipAfterRule(false));

		ActionContext<ReduceVO> actionContext = new ActionContext<ReduceVO>("InsertAction");
		for (IRule<ReduceVO> rule : lastUnApprovedAfterRules) {
			rule.process(actionContext, billVO);
		}
	}

	protected ReduceVO approveReduceVO(LoginContext context, ReduceVO billVO) throws BusinessException {
		// 审批通过后规则
		ActionContext<ReduceVO> actionContext = new ActionContext<ReduceVO>("InsertAction");

		// 保存校验类
		List<Validator> validators = new ArrayList<Validator>();
		// 添加判断财务是否已经制成折旧清单的校验。
		validators.add(new ReduceCheckHasVoucherForAccbooks());
		// 审批前业务规则
		List<IRule<ReduceVO>> approvedBeforeRules = new ArrayList<IRule<ReduceVO>>();
		approvedBeforeRules.add(new ValidateServiceRule<ReduceVO>(validators));
		for (IRule<ReduceVO> rule : approvedBeforeRules) {
			rule.process(actionContext, billVO);
		}

		// 配置业务规则
		// 审核通过后规则
		List<IRule<ReduceVO>> approvePassAfterRules = new ArrayList<IRule<ReduceVO>>();

		// 审核后要更新fa_log表中的卡片日志
		approvePassAfterRules.add(new UpdateCardLogAfterRule<ReduceVO>(true));
		// reduce接口

		// 1. 生成表体
		approvePassAfterRules.add(new CreateReduceBodyInfoAfterRule<ReduceVO>());

		// 2. 重新计提标志
		approvePassAfterRules.add(new ReduceUpdateDepLogAfterRule());

		// 3. 回写卡片历史表
		approvePassAfterRules.add(new ReduceWriteBackToCardhistoryAfterRule(true));

		// 4. 发送会计平台
		approvePassAfterRules.add(new ReduceSendFipAfterRule(true));

		// 添加单据的fa_log记录
		approvePassAfterRules.add(new CreateBillLogAfterRule<ReduceVO>(LogTypeConst.reduce));

		for (IRule<ReduceVO> rule : approvePassAfterRules) {
			rule.process(actionContext, billVO);
		}

		return billVO;
	}

	/**
	 * 減少服務接口.<br>
	 * 接口操作说明：<br>
	 * 1：保存减少表体数据<br>
	 * 2：回写被减少的卡片数据<br>
	 * 3：如影响折旧则删除折旧标志<br>
	 * 4：填充数据并发送到会计平台<br>
	 * 6：
	 *
	 * @param bodyVO 通过VO对照得到的减少单表体VO.<br>
	 *               ReduceBodyVO中的数据说明：<br>
	 *               1:pk_bill：原单据的表头ID，比如：调出的表头ID<br>
	 *               2:bill_type: 原单据的单据类型，比如：调出：HC<br>
	 *               3:pk_card : 被减少的卡片主键<br>
	 *               4:...<br>
	 * @date 2010-1-5
	 */
	public void reduce(ReduceVO reduceVO) throws BusinessException {
		List<IRule<ReduceVO>> approvePassAfterRules = new ArrayList<IRule<ReduceVO>>();

		approvePassAfterRules.add(new CreateReduceBodyInfoAfterRule<ReduceVO>());

		// 2. 更新重新计提标志
		approvePassAfterRules.add(new ReduceUpdateDepLogAfterRule());

		// 3. 回写卡片历史表
		approvePassAfterRules.add(new ReduceWriteBackToCardhistoryAfterRule(true));

		// 4. 发送会计平台
		approvePassAfterRules.add(new ReduceSendFipAfterRule(true));

		ActionContext<ReduceVO> actionContext = new ActionContext<ReduceVO>("InsertAction");
		for (IRule<ReduceVO> rule : approvePassAfterRules) {
			rule.process(actionContext, reduceVO);
		}
	}

	@Override
	public ReduceVO[] queryReduceVOByPKs(String[] pks) throws BusinessException {
		return queryReduceVOByPKs(pks, false);
	}

	/**
	 * 根据where条件查询减少单统一的接口的，
	 *
	 * @param whereSql     whereSql
	 * @param orderColumns 排序的字段
	 * @param isQueryBody  是否查询表体
	 * @return
	 * @throws BusinessException
	 * @author yanjq
	 * @date 2010-4-13
	 */
	private ReduceVO[] queryReduceVOByHeadCond(String whereSql, boolean isQueryBody) throws BusinessException {

		// 目前只查询表头VO，不查表体VO
		// 由于固定资产减少单账簿有N/A和具体账簿PK的表体记录，
		ReduceVO[] billVOs = QueryUtil.queryBillVOByHeadCond(ReduceVO.class, whereSql, null, false);

		return billVOs;
	}

	/**
	 * 根据PKs查询减少单统一的接口的，
	 *
	 * @param whereSql     whereSql
	 * @param orderColumns 排序的字段
	 * @param isQueryBody  是否查询表体
	 * @return
	 * @throws BusinessException
	 * @author yanjq
	 * @date 2010-4-13
	 */
	private ReduceVO[] queryReduceVOByPKs(String[] pks, boolean isQueryBody) throws BusinessException {
		String sql = " pk_reduce in " + InSqlManager.getInSQLValue(pks);
		return queryReduceVOByHeadCond(sql, isQueryBody);

	}

	@SuppressWarnings("unchecked")
	@Override
	public String[] queryReducePksByWhereSql(LoginContext context, String whereSql) throws BusinessException {

		StringBuffer sql = new StringBuffer("select h.pk_reduce from fa_reduce as h  where h.dr = 0 and bill_type = '"
				+ BillTypeConst.REDUCE + "' and pk_group = '" + InvocationInfoProxy.getInstance().getGroupId() + "'");
		if (whereSql != null && whereSql.trim().length() > 0)
			sql.append(" and ").append(whereSql);

		DBAccessUtil dbAcc = new DBAccessUtil();
		List<String> list = (List<String>) dbAcc.getBaseDAO().executeQuery(sql.toString(), new ColumnListProcessor());

		// 返回的PK数组值
		String[] pks = null;
		if (list != null && list.size() > 0) {
			pks = list.toArray(new String[list.size()]);
		}

		return pks;
	}

	/**
	 * 
	 * 功能描述：提出公共方法，查询减少表体
	 * 
	 * @param pk_accbook
	 * @param whereSql
	 * @return
	 * @throws BusinessException
	 * @return
	 */
	private ReduceBodyVO[] getReduceBodyVOs(String pk_accbook, String whereSql) throws BusinessException {

		// 查询需要的表体
		ReduceBodyVO[] bodyVOs = QueryUtil.queryVOByCond(ReduceBodyVO.class, whereSql, null);

		if (ArrayUtils.isEmpty(bodyVOs))
			return null;

		// 表体卡片数组
		String[] pk_cards = VOManager.getAttributeValueArray(bodyVOs, ReduceBodyVO.PK_CARD);

		// 批量查询多使用部门
		Map<String, List<DeptScaleVO>> deptMap = AMProxy.lookup(IAssetService.class).queryUseDeptBatch(pk_cards);

		// 设置查询卡片的账簿
		String queryPKAccbook = "";
		// 判断是否是业务账簿
		if (FlagConsts.FLAG_NULL.equals(pk_accbook)) {
			// 取表体PK_ORG
			String[] pk_orgs = VOManager.getAttributeValueArray(bodyVOs, ReduceBodyVO.PK_ORG);
			if (null != pk_orgs && pk_orgs.length > 0 && StringUtils.isNotEmpty(pk_orgs[0])) {
				// 如果没有选择账簿，则取主账簿
				queryPKAccbook = AccbookManager.queryMainAccbookIDByOrg(pk_orgs[0]);
			}
		} else {
			// 取表头账簿
			queryPKAccbook = pk_accbook;
		}

		// 查询卡片信息
		MappedBaseVO[] mapBaseVOs = AMProxy.lookup(IAssetService.class).queryAssetFieldValues(pk_cards, queryPKAccbook,
				new String[] { "fa_card." + AssetFieldConst.PK_CARD, AssetFieldConst.ORIGINVALUE,
						AssetFieldConst.ASSET_STATE });

		for (ReduceBodyVO bodyVO : bodyVOs) {
			// 卡片主键
			String pk_card = bodyVO.getPk_card();

			String name = DeptScaleManager.getShowName(CollectionUtils.toArray(deptMap.get(pk_card)));
			bodyVO.setAttributeValue(DeptScaleManager.USEDEPT, name);

			// 取卡片最新卡片状态，区分账簿。
			String asset_state = "";

			if (ArrayUtils.isNotEmpty(mapBaseVOs)) {
				for (MappedBaseVO baseVO : mapBaseVOs) {
					// 获得卡片PK
					String mbPk_card = (String) baseVO.getAttributeValue(CommonKeyConst.pk_card);
					if (StringUtils.isNotEmpty(mbPk_card) && StringUtils.isNotEmpty(pk_card)
							&& pk_card.equals(mbPk_card)) {

						// 获得卡片最新状态
						String mbAssetState = (String) baseVO.getAttributeValue(AssetFieldConst.ASSET_STATE);

						// 取状态
						asset_state = mbAssetState;
					}
				}
			}

			// 设置最新状态
			bodyVO.setAttributeValue(AssetFieldConst.ASSET_STATE, asset_state);
		}

		return bodyVOs;
	}

	/**
	 * 查询表体数据
	 *
	 * @param pk
	 * @param pk_accbook
	 * @return
	 * @throws BusinessException
	 * @see nc.itf.fa.prv.IReduce#queryReduceBodyVOs(java.lang.String,
	 *      java.lang.String)
	 */
	@Override
	public ReduceBodyVO[] queryReduceBodyVOs(String pk, String pk_accbook) throws BusinessException {

		if (AccbookManager.isBizAccbook(pk_accbook)) {
			pk_accbook = FlagConsts.FLAG_NULL;
		}
		// 根据PK查询表体数据，仅仅查询N/A的数据
		String whereSql = " pk_reduce = '" + pk + "' and pk_accbook = '" + pk_accbook + "' and dr =0 ";

		ReduceBodyVO[] bodyVOs = getReduceBodyVOs(pk_accbook, whereSql);

		return bodyVOs;
	}

	/**
	 * 处理多币种
	 *
	 * @param billVO
	 * @throws BusinessException
	 * @author yanjq
	 * @date 2010-4-12
	 */
	private void dealMultiCurrency(ReduceVO billVO) throws BusinessException {
		ReduceHeadVO headVO = (ReduceHeadVO) billVO.getParentVO();
		ReduceBodyVO[] bodyVOs = (ReduceBodyVO[]) billVO.getChildrenVO();

		// 表体无数据时，不处理多币种
		if (ArrayUtils.isEmpty(bodyVOs)) {
			return;
		}
		// 默认组织的主账簿
		String pk_accbook = headVO.getPk_accbook();
		if (AccbookManager.isBizAccbook(pk_accbook)) {
			pk_accbook = AccbookManager.queryMainAccbookIDByOrg(headVO.getPk_org());
		}
		for (ReduceBodyVO bodyVO : bodyVOs) {
			bodyVO.setPk_group(headVO.getPk_group());
			CurrencyRateManager.setLocalMoneyByAccbook(bodyVO, ReduceVOGenerator.MULTI_CURRENCY_ATTRS, pk_accbook,
					headVO.getBusiness_date());
		}
	}

	/**
	 * 生成实物推FA的减少单
	 *
	 * @param msgVO
	 * @return
	 * @throws BusinessException
	 * @author yanjq
	 * @see nc.itf.fa.prv.IReduce#queryReduceVOFromPAM(nc.pub.fa.msg.MsgVO)
	 */
	@Override
	public ReduceVO queryReduceVOFromPAM(MsgVO msgVO) throws BusinessException {
		if (isExistByMsg(msgVO)) {
			ExceptionUtils.asBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("reduce_0",
					"02012015-0013")/* @res "该消息已经生成了减少单！\n" */);
		}
		ReduceVO reduceVO = null;
		if (null != msgVO.getService_class_name()) {
			// 根据传送的VO得到减少VO
			reduceVO = MsgManager.queryPamByFA(msgVO);
		} else {
			if (null != msgVO.getSrc_billid()) {
				ReduceVO[] reduceVOs = queryReduceVOByPk(msgVO.getSrc_billid());
				if (ArrayUtils.isNotEmpty(reduceVOs)) {
					reduceVO = reduceVOs[0];
				}
			}
		}
		return reduceVO;
	}

	/**
	 * 根据来源单据ID，判断该单据生成的变动单是否存在
	 *
	 * @param pk_bill_src
	 * @return
	 * @throws DAOException
	 * @date 2010-5-19
	 */
	protected boolean isExistBySrcBillPk(String pk_bill_src) throws BusinessException {
		String sql = " select top 1 '1' from fa_reduce where dr = 0 and pk_bill_src = ? ";
		SQLParameter params = new SQLParameter();
		params.addParam(pk_bill_src);
		return ((Boolean) new DBAccessUtil().executeQuery(sql, params, new BooleanProcessor())).booleanValue();
	}

	/**
	 * 根据来源单据ID，判断该单据生成的变动单是否存在
	 *
	 * @param pk_bill_src
	 * @return
	 * @throws DAOException
	 * @date 2010-5-19
	 */
	protected boolean isExistByMsg(MsgVO msgVO) throws BusinessException {
		String pk_bill_src = msgVO.getSrc_billid();
		String pk_org = msgVO.getPk_org();
		String sql = " select top 1 '1' from fa_reduce where dr = 0 and pk_bill_src = ? and pk_org = ? ";
		SQLParameter params = new SQLParameter();
		params.addParam(pk_bill_src);
		params.addParam(pk_org);

		return ((Boolean) new DBAccessUtil().executeQuery(sql, params, new BooleanProcessor())).booleanValue();
	}

	/**
	 * 由于现在在单账簿下减少后，还能在业务账簿下减少其他账簿的数据，因此需要按未被减少的账簿进行分批更新卡片日志。
	 *
	 * @param billVO
	 * @param isDelete 是否删除单据
	 * @throws BusinessException
	 * @date 2010-5-17
	 */
	private void updateCardLog(String pk_accbook, String bill_code, String bill_type, Integer bill_status,
			String[] pk_cards) throws BusinessException {

		// Map<pk_accbook,List<pk_card>>将相同账簿的卡片弄在一起。目的是为了按账簿进行分批更新
		Map<String, List<String>> accbookToCardMap = new HashMap<String, List<String>>();
		// 表体所有的卡片
		// 如果是业务账簿
		if (AccbookManager.isBizAccbook(pk_accbook)) {
			// Map<pk_accbook,List<pk_card>>卡片没有被减少的账簿
			accbookToCardMap = AMProxy.lookup(IAssetService.class).queryNotReducedAccbookToCardMap(pk_cards);
		} else {
			// 只需对一个财务账簿进行更新
			accbookToCardMap.put(pk_accbook, Arrays.asList(pk_cards));
		}

		// 分账簿对卡片单据号进行更新
		for (Map.Entry<String, List<String>> entry : accbookToCardMap.entrySet()) {
			getLogService().updateLog(entry.getKey(), CollectionUtils.toArray(entry.getValue()), bill_code, bill_type,
					bill_status);
		}
	}

	/**
	 * 查询表体中的所有卡片PK
	 *
	 * @param parentPK
	 * @param pk_accbook
	 * @return
	 * @throws DAOException
	 * @date 2010-5-17
	 */
	@SuppressWarnings("unchecked")
	private String[] queryBodyCardId(String parentPK, String pk_accbook) throws DAOException {
		String sql = "select pk_card from fa_reduce_b where dr= 0 and pk_reduce = '" + parentPK + "' and pk_accbook = '"
				+ AccbookManager.getValidAccbook(pk_accbook) + "'";
		Set<String> set = (Set<String>) new DBAccessUtil().executeQuery(sql, new ColumnSetProcessor<String>());
		return CollectionUtils.toArray(set);
	}

	/**
	 * 重新加载单据VO，流量修改后，很多时候需要在处理前进行重新加载
	 *
	 * @param reduceVO
	 * @return
	 * @throws BusinessException
	 * @date 2010-5-26
	 */
	protected ReduceVO reloadReduceVO(String pk_reduce) throws BusinessException {
		// 先查询表头
		ReduceVO billVO = ArrayUtils.getFirstElem(queryReduceVOByPKs(new String[] { pk_reduce }));

		if (billVO == null) {
			return null;
		}

		// 表头账簿
		String pk_accbook = (String) billVO.getParentVO().getAttributeValue(CommonKeyConst.pk_accbook);

		// 再加载表体
		billVO.setChildrenVO(queryReduceBodyVOs(pk_reduce, pk_accbook));

		return billVO;
	}

	/**
	 * 批量重新加载减少单 方法功能描述：简要描述本方法的功能。
	 * <p>
	 * <b>参数说明</b>
	 * 
	 * @param primaryKeys 减少单主键
	 * @return
	 * @throws BusinessException
	 *                           <p>
	 * @author weizq
	 * @time 2010-12-23 下午04:51:03
	 */
	public ReduceVO[] reloadReduceVOs(String[] primaryKeys) throws BusinessException {
		List<ReduceVO> reduceVOs = new ArrayList<ReduceVO>();
		for (String pk : primaryKeys) {
			ReduceVO billVO = reloadReduceVO(pk);
			if (billVO == null) {
				continue;
			}
			reduceVOs.add(billVO);
		}

		return reduceVOs.toArray(new ReduceVO[0]);
	}

	protected ReduceVO pushSaveReduceVO(ReduceVO billVO) throws BusinessException {
		// 处理多币种
		dealMultiCurrency(billVO);

		ReduceVO[] retReduceVOs = pushSaveBillVO(billVO);
		// 只取第一个
		ReduceVO reduceVO = ArrayUtils.getFirstElem(retReduceVOs);

		// 不能使用finally块进行:因为保存报错了，前台依然处于编辑状态，应该由取消按钮来解锁 或者 保存成功了解锁。
		releasePKCardLock(billVO);

		return reduceVO;
	}

	/**
	 * 判断折旧制单的情况
	 *
	 * @param billVO
	 * @throws BusinessException
	 * @date 2010-7-5
	 */
	private void checkAfterDapMade(ReduceVO billVO) throws BusinessException {
		ReduceHeadVO headVO = (ReduceHeadVO) billVO.getParentVO();
		ReduceBodyVO[] bodyVOs = (ReduceBodyVO[]) billVO.getChildrenVO();

		if (ArrayUtils.isEmpty(bodyVOs))
			return;

		String pk_org = headVO.getPk_org();
		String pk_accbook = headVO.getPk_accbook();
		String pk_group = headVO.getPk_group();
		String[] pk_cards = VOManager.getAttributeValueArray(bodyVOs, CommonKeyConst.pk_card);

		checkAfterDapMade(pk_group, pk_org, pk_accbook, pk_cards);
	}

	/**
	 * 验证折旧制单后减少是否合法
	 *
	 * @param pk_org
	 * @param pk_accbook
	 * @param pk_cards
	 * @throws BusinessException
	 * @date 2010-7-13
	 */
	private void checkAfterDapMade(String pk_group, String pk_org, String pk_accbook, String... pk_cards)
			throws BusinessException {

		Map<String, List<String>> accbookCardIdsMap = null;

		if (AccbookManager.isBizAccbook(pk_accbook)) {
			accbookCardIdsMap = AMProxy.lookup(IAssetService.class).queryNotReducedAccbookToCardMap(pk_cards);
		} else {
			accbookCardIdsMap = new HashMap<String, List<String>>();
			accbookCardIdsMap.put(pk_accbook, Arrays.asList(pk_cards));
		}

		DepmethodVO[] methodVOs = AMProxy.lookup(IDepmethodService.class).queryAllDepmethods(pk_group);
		// 折旧方法的Map<pk_depmethod,DepmethodVO>
		Map<String, DepmethodVO> depmethodMap = MapUtils.toMap(methodVOs);

		// 是否财务已经启用
		if (!ModuleInfoQuery.isFIPEnabled()) {
			return;
		}

		for (Map.Entry<String, List<String>> entry : accbookCardIdsMap.entrySet()) {
			StringBuilder errbuilder = new StringBuilder();

			String tempAccbook = entry.getKey();
			AccperiodVO periodVO = AMProxy.lookup(ICloseBookService.class).queryMinUnClosebookPeriod(pk_org,
					tempAccbook);
			// 如果已经制单
			if (FipManager.isFipMakeVoucher(pk_org, periodVO.getAccyear(), periodVO.getPeriod(), tempAccbook)
					.booleanValue()) {

				List<String> pk_cardList = entry.getValue();
				if (null == pk_cardList || pk_cardList.size() == 0) {
					return;
				}
				String[] tempCardIds = CollectionUtils.toArray(pk_cardList);

				Map<String, AssetVO> assetMap = AMProxy.lookup(IAssetService.class).queryAssetVO(pk_accbook,
						tempCardIds);
				// 资产类别PK数组
				String[] pk_categorys = VOManager.getAttributeValueArray(CollectionUtils.toArray(assetMap.values()),
						AssetVO.PK_CATEGORY);

				if (StringUtils.isEmpty(pk_accbook) || null == pk_categorys || pk_categorys.length == 0) {
					return;
				}
				// 资产类别在某个账簿中的折旧属性Map<pk_category,Integer>
				Map<String, Integer> accbookDepMap = AMProxy.lookup(IAccbookInfoService.class)
						.queryDepmethodattr(pk_org, pk_categorys, pk_accbook);

				for (Map.Entry<String, AssetVO> assetEntry : assetMap.entrySet()) {
					AssetVO assetVO = assetEntry.getValue();
					String pk_category = assetVO.getPk_category();
					DepmethodVO depmethodVO = depmethodMap.get(assetVO.getPk_depmethod());

					String card_code = assetVO.getCard_code();
					boolean isNewAsset = assetVO.getNewasset_flag().intValue() == AssetUseConst.isNew;
					String depmethod_code = depmethodVO.getDepmethod_code();
					Integer depAttr = accbookDepMap.get(pk_category);
					String errMsg = DepChecker.checkCardWhenReduceAfterDAPMake(card_code, isNewAsset, depmethod_code,
							depAttr);
					errbuilder.append(errMsg);
				}
			}

			if (errbuilder.length() > 0) {
				ExceptionUtils.asBusinessException(errbuilder.toString());
			}
		}

	}

	/**
	 * 日志服务接口
	 *
	 * @return
	 */
	private ILogService getLogService() {
		return AMProxy.lookup(ILogService.class);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nc.itf.fa.prv.IReduce#queryReduceVOByPk(java.lang.String)
	 */
	@Override
	public ReduceVO[] queryReduceVOByPk(String pk) throws BusinessException {
		ReduceVO billVO = reloadReduceVO(pk);
		return new ReduceVO[] { billVO };
	}

	/**
	 * 
	 * 功能描述：为了减少连接次数，用此方法从后台获得数据
	 * 
	 * @return
	 * @throws BusinessException
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Map fetchMsgDate(LoginContext context, String trans_type, String bill_code, UFDate businessDate, MsgVO msgVO)
			throws BusinessException {

		// 定义存储数据MAP
		Map dataMap = new HashMap();

		msgVO.setDest_bill_type(BillTypeConst_FA.REDUCE);

		// 获得消息的财务组织
		String pk_org = msgVO.getPk_org();

		// 根据MsgVO得到减少单的VO
		// 异常捕获，因为有可能在主账簿下作减少并发送消息，但是没处理。然后在报告账簿下再作减少，然后再打开消息，导致界面空白。
		// 在此给出提示
		ReduceVO reduceVO = this.queryReduceVOFromPAM(msgVO);

		ReduceHeadVO headVO = (ReduceHeadVO) reduceVO.getParentVO();

		ReduceBodyVO[] bodyVOs = (ReduceBodyVO[]) reduceVO.getChildrenVO();

		String pk_org_v = OrgUtils.getCurVidByPkOrg(pk_org);

		// 取资产处置表头的财务组织
		// 根据VO交换获得
		String sourcePk_org = headVO.getPk_org();

		// 组织本位币
		// 此处的组织应当对应资产处置单据表头的财务组织
		if (StringUtils.isEmpty(sourcePk_org)) {
			// 如果没有在VO交换中获得，则取消息中的组织
			sourcePk_org = msgVO.getPk_org();
		}

		// 获得表体卡片主键
		String[] pk_cards = VOManager.getAttributeValueArray(bodyVOs, ReduceBodyVO.PK_CARD);
		pk_cards = ArrayUtils.removeNullElements(pk_cards);
		if (ArrayUtils.isEmpty(pk_cards)) {
			return dataMap;
			// ExceptionUtils.asBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("reduce_0","02012015-0004")/*@res
			// "没有满足条件的卡片，请检查数据权限 或 卡片已删除!"*/);
		}

		// 注入表头数据
		// 业务账簿
		// 说明是由实物传过来的
		if (StringUtils.isNotEmpty(msgVO.getSrc_bill_type())) {
			headVO.setPk_accbook(FlagConsts.FLAG_NULL);
		}
		// 交易类型
		headVO.setTransi_type(trans_type);
		// 原单据号，实物的单据号
		headVO.setPk_bill_src(msgVO.getSrc_billid());
		// 集团PK
		headVO.setPk_group(context.getPk_group());
		// 财务组织
		headVO.setPk_org(msgVO.getPk_org());
		// 财务组织多版本
		headVO.setPk_org_v(pk_org_v);
		// 业务日期==== 取前台业务日期
		headVO.setBusiness_date(businessDate);
		// 新增的VO
		headVO.setStatus(2);
		// 单据类型
		headVO.setBill_type(BillTypeConst.REDUCE);
		// 单据状态
		if (StringUtils.isNotEmpty(msgVO.getSrc_bill_type())) {
			headVO.setBill_status(0);
		}
		// 来源单据类型
		headVO.setBill_type_src(msgVO.getSrc_bill_type());
		// 组织主账簿
		String pk_accbook_main = AccbookManager.queryMainAccbookIDByOrg(pk_org);
		// 设置表体的币种
		String bodyoppcurrtype = CurrencyManager.getLocalCurrencyPK(pk_org);
		// 组织本位币
		// 此处的组织应当对应资产处置单据表头的财务组织
		String oppcurrtype = CurrencyManager.getLocalCurrencyPK(sourcePk_org);

		// initData不能向后台传送，没有序列化
		context.setInitData(null);
		// 财务组织
		context.setPk_org(msgVO.getPk_org());

		// 查询主账簿的卡片数据
		AssetVO[] assetVOs = AMProxy.lookup(IAssetService.class).queryMainAssetVOByPks(context, pk_cards);
		if (ArrayUtils.isEmpty(assetVOs)) {
			return dataMap;
			// ExceptionUtils.asBusinessException(nc.vo.ml.NCLangRes4VoTransl.getNCLangRes().getStrByID("reduce_0","02012015-0005")/*@res
			// "没有满足条件的卡片，请检查数据权限 或 卡片已删除 !"*/);
		}

		// 生成减少单表体
		ReduceBodyVO[] itemVOs = ReduceVOGenerator.generateReduceBodyVOByAssetVO(assetVOs, true,
				headVO.getBusiness_date());
		// 将卡片生成的减少单表体数组，转化为Map<pk_card, ReduceBodyVO>的形式
		Map<String, ReduceBodyVO> bodyMap = MapUtils.toMap(itemVOs, ReduceBodyVO.PK_CARD);

		// 用卡片生成的减少表体 来填充 VO交换的表体，只有null，或者为0的值才填充。

		for (ReduceBodyVO bvo : bodyVOs) {
			for (String attr : bvo.getAttributeNames()) {
				Object value = bvo.getAttributeValue(attr);

				if (value == null) {
					// 如果是null值，则用卡片生成的减少单的值来填充
					bvo.setAttributeValue(attr, bodyMap.get(bvo.getPk_card()).getAttributeValue(attr));
				} else {
					// 如果是0值，则用卡片生成的减少单的值来填充
					if (value instanceof UFDouble && UFDoubleUtils.isEqual((UFDouble) value, UFDouble.ZERO_DBL)) {
						bvo.setAttributeValue(attr, bodyMap.get(bvo.getPk_card()).getAttributeValue(attr));
					}
				}
			}
			// 币种
			bvo.setPk_currency(bodyoppcurrtype);
			// 主账簿--设置精度
			bvo.setPk_accbook_scale(pk_accbook_main);

			bvo.setStatus(2);
		}

		headVO.setPk_accbook_scale(pk_accbook_main);

		headVO.setPk_currency(bodyoppcurrtype);

		// 处理模拟折旧(只处理界面显示主账簿信息， 其他账簿在审核处理)
		dealWithSimulatedep(context, headVO, bodyVOs, assetVOs);

		// 处理币种折算, 多币种
		dealCurrencyRate(headVO, bodyVOs, assetVOs, oppcurrtype);

		// 设置单据编码
		// if(StringUtils.isEmpty(bill_code)){
		//
		// // 获取单据号管理服务
		// IBillcodeManage billCodeManager =
		// AMProxy.lookup(IBillcodeManage.class);
		//
		// // 获取单据号管理服务
		// // 取得新的单据号
		// bill_code =
		// billCodeManager.getPreBillCode_RequiresNew(BillTypeConst.REDUCE,
		// context.getPk_group(), msgVO.getPk_org());
		//
		// }

		// reduceVO.getParentVO().setAttributeValue(ReduceHeadVO.BILL_CODE,
		// bill_code);

		// 载入数据，传前台
		dataMap.put(ReduceMsgConst.reduceVOObj, reduceVO);
		dataMap.put(ReduceMsgConst.pk_cards, pk_cards);
		dataMap.put(ReduceMsgConst.assetVOs, assetVOs);

		return dataMap;
	}

	/**
	 * 处理币种折算, 多币种
	 * <p>
	 * <b>examples:</b>
	 * <p>
	 * 使用示例
	 * <p>
	 * <b>参数说明</b>
	 * 
	 * @param headVO
	 * @param bodyVOs
	 *                <p>
	 * @author weizq
	 * @param assetVOs
	 * @throws BusinessException
	 * @time 2011-7-19 下午03:41:13
	 */
	private void dealCurrencyRate(ReduceHeadVO headVO, ReduceBodyVO[] bodyVOs, AssetVO[] assetVOs, String oppcurrtype)
			throws BusinessException {
		// <pk_card, AssetVO> 以map的形式存储
		Map<String, AssetVO> cardMaps = new HashMap<String, AssetVO>();
		for (AssetVO vo : assetVOs) {
			cardMaps.put(vo.getPk_card(), vo);
		}

		for (ReduceBodyVO bodyVO : bodyVOs) {
			// 清理费用
			UFDouble purgefee = bodyVO.getPurgefee();
			// 清理收入
			UFDouble purgerevenue = bodyVO.getPurgerevenue();

			// 主账簿
			String pk_accbook = cardMaps.get(bodyVO.getPk_card()).getPk_accbook();
			// 币种
			String pk_currency = oppcurrtype;// cardMaps.get(bodyVO.getPk_card()).getPk_currency();
			// 业务日期
			UFDate business_date = headVO.getBusiness_date();
			// 币种折算
			// 清理费用折算
			UFDouble purgefeeByCurrnecy = CurrencyRateManager.getAccbookAmountByOpp(pk_accbook, pk_currency, purgefee,
					business_date);
			// 清理收入折算
			UFDouble purgerevenueByCurrnecy = CurrencyRateManager.getAccbookAmountByOpp(pk_accbook, pk_currency,
					purgerevenue, business_date);

			// 将折算后的值赋值
			bodyVO.setPurgefee(purgefeeByCurrnecy);
			bodyVO.setPurgerevenue(purgerevenueByCurrnecy);

			// 多币种处理
			CurrencyRateManager.setLocalMoneyByAccbook(bodyVO,
					new String[] { ReduceBodyVO.PURGEFEE, ReduceBodyVO.PURGEREVENUE }, pk_accbook,
					headVO.getBusiness_date());
		}
	}

	/**
	 *
	 * 方法功能描述：处理模拟折旧(只处理主账簿信息)
	 * <p>
	 * <b>examples:</b> <b>参数说明</b>
	 * 
	 * @param context
	 * @param itemVOs
	 * @param assetVOs
	 *                 <p>
	 * @author weizq
	 * @throws BusinessException
	 * @time 2010-12-14 下午07:18:19
	 */
	private void dealWithSimulatedep(LoginContext context, ReduceHeadVO headVO, ReduceBodyVO[] reduceBodyVOs,
			AssetVO[] mainAssetVOs) throws BusinessException {
		ReduceSimulateManager simulateDepFactory = new ReduceSimulateManager();

		String pk_org = context.getPk_org();
		String pk_group = context.getPk_group();
		// 主账簿PK
		String pk_accbook = (String) ArrayUtils
				.getFirstElem(VOManager.getAttributeValueArray(mainAssetVOs, AssetFieldConst.PK_ACCBOOK));

		// 减少卡片主键
		String[] pk_cards = VOManager.getAttributeValueArray(reduceBodyVOs, AssetFieldConst.PK_CARD);

		// 折旧
		simulateDepFactory.procSimulateDep(pk_cards, pk_group, pk_org, pk_accbook, headVO.getBusiness_date(),
				UFBoolean.FALSE);

		// 设置模拟折旧
		for (ReduceBodyVO bodyVO : reduceBodyVOs) {
			String reducePk_card = bodyVO.getPk_card();

			// 累计折旧
			UFDouble accddep = simulateDepFactory.getSimulateAccudepMap().get(reducePk_card);

			// 模拟折旧
			// UFDouble simulateDep =
			// simulateDepFactory.getSimulateAccudepMap().get(reducePk_card);
			UFDouble simulateDep = simulateDepFactory.getSimulateDepamoutMap().get(reducePk_card);

			// 减少累计折旧 = 累计折旧 + 模拟折旧
			// UFDouble red_accddep = UFDoubleUtils.add(accddep, simulateDep);
			// 减少累计折旧 = 累计折旧 + 模拟折旧
			UFDouble red_accddep = accddep;

			// 净值
			UFDouble netvalue = UFDoubleUtils.sub(bodyVO.getRed_localoriginvalue(), accddep);

			// 净额
			UFDouble netrating = UFDoubleUtils.sub(netvalue, bodyVO.getRed_predevaluate());

			// 注入值
			bodyVO.setAccudep(accddep);
			bodyVO.setSimulatedep(simulateDep);
			bodyVO.setRed_accudep(red_accddep);
			bodyVO.setRed_netvalue(netvalue);
			bodyVO.setRed_netrating(netrating);
		}

	}

	/**
	 * 
	 * 功能描述：根据来源单据类型和来源单据PK查询减少单据
	 * 
	 * @param bill_type_src
	 * @param pk_bill_src
	 * @throws BusinessException
	 * @return ReduceVO
	 */
	public ReduceVO queryReduceVOByOtherBill(String bill_type_src, String pk_bill_src, String pk_accbook)
			throws BusinessException {

		// 定义根据来源单据类型和来源单据PK查询SQL
		String sql = "bill_type_src = '" + bill_type_src + "' and pk_bill_src = '" + pk_bill_src + "' and dr = 0";

		// 查询实际生成的减少单据
		ReduceVO[] reduceVO = queryReduceVOByHeadCond(sql, false);

		// 获得第一个对象
		ReduceVO billVO = ArrayUtils.getFirstElem(reduceVO);

		if (billVO == null) {
			return null;
		}

		// 表头账簿
		// String pk_accbook = (String)
		// billVO.getParentVO().getAttributeValue(CommonKeyConst.pk_accbook);
		// 主键
		String pk_reduce = billVO.getPrimaryKey();

		if (AccbookManager.isBizAccbook(pk_accbook)) {
			pk_accbook = FlagConsts.FLAG_NULL;
		}

		// 根据PK查询表体数据，仅仅查询N/A的数据
		String whereSql = " dr = 0 and pk_reduce = '" + pk_reduce + "' ";

		// 需要查询其他单据推的减少单，需要判断
		if (StringUtils.isNotEmpty(pk_accbook)) {
			whereSql += " and pk_accbook = '" + pk_accbook + "'";
		}

		// 查询减少表体
		ReduceBodyVO[] bodyVOs = getReduceBodyVOs(pk_accbook, whereSql);

		// 再加载表体
		billVO.setChildrenVO(bodyVOs);

		return billVO;
	}

}