package com.dev.swms.server.service.receiving.pojo;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.hibernate.type.LocaleType;

import com.dev.swms.server.model.base.BillType;
import com.dev.swms.server.model.base.LPFormat;
import com.dev.swms.server.model.base.Location;
import com.dev.swms.server.model.base.LocationType;
import com.dev.swms.server.model.base.PackageUnit;

import com.dev.swms.server.model.inventory.Inventory;
import com.dev.swms.server.model.inventory.ItemKeyInventory;
import com.dev.swms.server.model.inventory.Stock;
import com.dev.swms.server.model.inventory.StockType;
import com.dev.swms.server.model.inventory.Task;
import com.dev.swms.server.model.inventory.TaskType;
import com.dev.swms.server.model.receiving.MoveDoc;
import com.dev.swms.server.model.receiving.MoveDocDetail;
import com.dev.swms.server.model.receiving.MoveDocStatus;
import com.dev.swms.server.model.rules.ItemSupplyRule;
import com.dev.swms.server.model.rules.PutMethod;
import com.dev.swms.server.model.rules.PutRackRule;
import com.dev.swms.server.model.shipping.PickTicketDetail;
import com.dev.swms.server.service.inventory.InventoryManager;
import com.dev.swms.server.service.receiving.MoveDocManager;
import com.dev.swms.server.service.receiving.PutawayAllocator;
import com.dev.swms.server.service.sequence.BussinessCodeManager;
import com.dev.swms.server.utils.DoubleUtils;
import com.dev.swms.server.web.filter.WarehouseHolder;
import com.dev.energy.server.dao.CommonDao;
import com.dev.energy.server.exception.BusinessException;
import com.dev.energy.server.model.EntityFactory;
import com.dev.energy.server.util.LocalizedMessage;


public class DefaultPutawayAllocator implements PutawayAllocator {
	protected final CommonDao commonDao;
	protected final BussinessCodeManager codeManager;
	protected final InventoryManager inventoryManager;
	private String allocateErrMsg="";
	private Long warehouseId;
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
	
	public DefaultPutawayAllocator(CommonDao commonDao,BussinessCodeManager codeManager,
			InventoryManager manager2) {
		super();
		this.commonDao = commonDao;
		this.codeManager = codeManager;
		this.inventoryManager = manager2;
	}

	public String allocate(Long moveDocId) {
		warehouseId = WarehouseHolder.getWarehouse().getId().longValue();
		synchronized (warehouseId) {
			String errorMsg = "";
			//for (Long id : moveDocIds) {
				MoveDoc moveDoc = commonDao.load(MoveDoc.class, moveDocId);
				//try {
					String allocateInfo = allocate(moveDoc);
					if (moveDoc.getPlanQuantity() == moveDoc.getAllocateQuantity()) {
						// 整单分配成功
						errorMsg += moveDoc.getCode() + "整单分配成功;";
					} else if (moveDoc.getAllocateQuantity() > 0) {
						// 部分分配成功, 去掉errMsg后面的;字符
						errorMsg += moveDoc.getCode() + "部分分配成功[" + allocateInfo + "];";
					} else {
						// 分配失败, 去掉errMsg后面的;字符
						errorMsg += moveDoc.getCode() + "分配失败[" + allocateInfo + "];";
					}
				//}catch(Exception e){
				//	errorMsg += moveDoc.getCode() + "分配失败[" + e.getMessage() + "];";
				//}
			//}
			//LocalizedMessage.addLocalizedMessage(errorMsg);
			return errorMsg;
		}
	}

	private String allocate(MoveDoc moveDoc) {
		//a、按照库位剩余可存放SKU的数量大小按从小到大排序，以放满一个库位再放下一库位为原则，依次往库位上分配库存
		//b、如果库位全部分配后仍然有剩余，则不再分配;
		//c、上架分配时不允许对包装进行拆分
		allocateErrMsg="";
		moveDoc = commonDao.load(MoveDoc.class, moveDoc.getId());
		if (!( MoveDocStatus.CANCEL.equals(moveDoc.getStatus()) 
				|| MoveDocStatus.CLOSE.equals(moveDoc.getStatus())))  {
			for (MoveDocDetail detail : moveDoc.getDetails()) {
				if(detail.getUnallocateQuantity()<=0){
					continue;
				}
				//越库分配
				allocateCrossStocking(detail);
				
				if(detail.getUnallocateQuantity()<=0){
					continue;
				}
				//只对指定源库位的明细进行分配
				if (detail.getSrcLoc() != null) {
					if (detail.getPackageUnit().isAllowLP() || detail.getSrcLP()!=null){//如果货品包装允许码托或者源托盘已经生成，则走托盘分配流程
						//托盘上架流程
						allocateDetailToLP(moveDoc, detail);
					} else {
						//散货上架流程
						allocateDetail(moveDoc, detail);
					}
				}
			}
			commonDao.store(moveDoc);
			return allocateErrMsg;
		} else {
			throw new BusinessException("moveDoc.status.error");
		}
	}

	/**
	 * 越库操作，直接上架到发货单的发货月台
	 * @param detail
	 */
	@SuppressWarnings("unchecked")
	private void allocateCrossStocking(MoveDocDetail detail){
		List<PickTicketDetail> pickTicketDetails = this.commonDao.findByNamedQuery("getPickticketDetailsByItemId",
				new String[]{"warehouseId","itemId"},
				new Object[]{detail.getMoveDoc().getWarehouse().getId(),detail.getItem().getId()});
		if(pickTicketDetails==null||pickTicketDetails.size()==0){
			return;
		}
		
		for(PickTicketDetail pickTicketDetail:pickTicketDetails){
			double unallocateQty = pickTicketDetail.getOrderQtyOfMasterUnit()-pickTicketDetail.getAllocatedQuantity();
			double allocateQty = unallocateQty > detail.getUnallocateQuantity() ? detail.getUnallocateQuantity() : unallocateQty;
			Task task = getTaskByTypeAndUK(detail, TaskType.MV_CROSS,
					detail.getSrcLoc(), detail.getSrcLP(), pickTicketDetail.getPickTicket().getDock(),
					null, detail.getPackageUnit(), allocateQty);
			detail.allocate(allocateQty);
			allocateInventory(task);
			task.setPickTicketDetail(pickTicketDetail);
			pickTicketDetail.allocateCross(allocateQty);
			commonDao.store(task);
			if(detail.getUnallocateQuantity()==0){
				continue;
			}
		}
	}
	
	private void allocateDetail(MoveDoc moveDoc, MoveDocDetail detail) {
		//根据移位单类型决定生成的移位Task类型
		String taskType = getTaskTypeByBillType(detail.getMoveDoc().getBillType());
		
		//当明细的源库位和目标库位均已确定，则不自动分配
		if (detail.getSrcLoc() != null && detail.getDstLoc() != null) {
			hardAllocate(detail, taskType);
			return;
		}
		
		// 如果一个规则下面的所有库位的所有能上架的库存小于明细中产品上架的数量（没有装完），则使用下一个规则的库位装，
		// 直到装完或者没有可用库位为止
		double toBeMovedQty = detail.getPlanQuantityOfMU()-detail.getAllocateQuantity();
		List<PutRackRule> putRackRules = getPutRackRules(detail, Boolean.FALSE);
		if (putRackRules.size() <1) {
			allocateErrMsg+= "货物 "+detail.getItem().getCode()+" 未找到上架策略;";
			return;
		}
		for (PutRackRule putRackRule : putRackRules) {
			toBeMovedQty -= allocateDetailByPutawayRule(detail, putRackRule, taskType);
			if (toBeMovedQty <= 0) {
				break;
			}
			
		}
	}
	
	
	private double allocateDetailByPutawayRule(MoveDocDetail detail,
			PutRackRule putRackRule, String taskType) {
		
		List<Location> wraps = new ArrayList<Location>();
		if(putRackRule.getVerticalAutoMove()){
			wraps = verticalAutoMove(putRackRule, detail);
		}
		else{
			wraps = getSortedLocationsByPutRackRule(detail, putRackRule, Boolean.FALSE);
		}

		double toBeMovedQty = getUnAllocateQtyByDetail(detail);  //基本单位数量
		double allocatedQty = 0;
		
		if(wraps.size()<1){
			allocateErrMsg+= "货物 "+detail.getItem().getCode()+" 未找到合适库位(散货上架);";
			return allocatedQty;
		}
		
		Map<String,Double> map = putawayItemByLocations(wraps,detail,toBeMovedQty,allocatedQty,taskType);
		toBeMovedQty = (Double)map.get("toBeMovedQty");
		allocatedQty = (Double)map.get("allocatedQty");

		//如果在垂直补货情况下，一列库位放完还有剩余数量，则找相邻列的其他空库位放置
		if(toBeMovedQty>0&&putRackRule.getVerticalAutoMove()){
			allocatedQty = getNeighborLocations(putRackRule,wraps,detail,toBeMovedQty,allocatedQty,taskType,0);
		}
		return allocatedQty;
	}
	
	
	
	private double getUnAllocateQtyByDetail(MoveDocDetail detail) {
		return detail.getPlanQuantityOfMU()	- detail.getAllocateQuantity();
	}

	private String getTaskTypeByBillType(BillType billType) {
		String taskType = "";
		if (billType.getCode().equals(MoveDoc.MV_ASN)) {
			//ASN收货上架
			taskType = TaskType.MV_ASN;
		} else if (billType.getCode().equals(MoveDoc.MV_MOVE)) {
			//移位
			taskType = TaskType.MV_MOVE;
		} else if (billType.getCode().equals(MoveDoc.MV_PROCESS)) {
			//加工上架
			taskType = TaskType.MV_PROCESS;
		} else if (billType.getCode().equals(MoveDoc.MV_SUPPLY)) {
			//补货
			taskType = TaskType.MV_SUPPLY;
		}
		return taskType;
	}

	private void allocateDetailToLP(MoveDoc moveDoc, MoveDocDetail detail) {
		//根据移位单类型决定生成的移位Task类型
		String taskType = getTaskTypeByBillType(detail.getMoveDoc().getBillType());
		
		//当明细的源库位和目标库位均已确定，则不自动分配
		if (detail.getSrcLoc() != null && detail.getDstLoc() != null) {
			hardAllocate(detail, taskType);
			return;
		}

		// 如果一个规则下面的所有库位的所有能上架的库存小于明细中产品上架的数量（没有装完），则使用下一个规则的库位装，
		// 直到装完或者没有可用库位为止
		double toBeMovedQty = detail.getPlanQuantityOfMU() - detail.getAllocateQuantity();
		int atLeastLPQty = detail.getPackageUnit().getAtLeastLPQty()*detail.getPackageUnit().getConvertFigure();
		if (atLeastLPQty > 0) {
			List<PutRackRule> putRackRules = getPutRackRules(detail, Boolean.TRUE);
			
			if (putRackRules.size() < 1) {
				allocateErrMsg+= "货物 "+detail.getItem().getCode() + " 未找到上架策略;";
				return;
			}
			for (PutRackRule putRackRule : putRackRules) {
				toBeMovedQty -= allocateDetailByPutawayRuleAndLP(detail, putRackRule, taskType);
				if (toBeMovedQty <= 0) {
					break;
				}
				if(atLeastLPQty > 0 && toBeMovedQty < atLeastLPQty) {
					this.allocateDetail(moveDoc, detail);
					return;
				}
			}
		}
		
		//码托分配后剩下的待上架数量，直接走散货上架分配流程
		if(toBeMovedQty > 0) {
			this.allocateDetail(moveDoc, detail);
		}					
	}
	
	@SuppressWarnings("unchecked")
	private double allocateDetailByPutawayRuleAndLP(MoveDocDetail detail,
			PutRackRule putRackRule, String taskType) {
		List<Location> wraps = new ArrayList<Location>();
		
		//如果策略里勾选了垂直补货，则通过垂直补货流程，选取待上架的库位
		if(putRackRule.getVerticalAutoMove()){
			wraps = verticalAutoMove(putRackRule, detail);
		}
		else{
			wraps = getSortedLocationsByPutRackRule(detail, putRackRule, Boolean.TRUE);
		}
		//起码数量（基本单位数量）
		int atLeastLPQty = detail.getPackageUnit().getAtLeastLPQty() * detail.getPackageUnit().getConvertFigure();
		//未分配数量（基本单位数量）
		double toBeMovedQty = getUnAllocateQtyByDetail(detail);			
		double allocatedQty = 0;

		if (wraps.size() < 1) {
			allocateErrMsg += "货物 " + detail.getItem().getCode() + " 未找到合适库位(托盘上架);";
			return allocatedQty;
		}
		//在指定的库位中执行码托上架操作
		Map<String,Double> map =putawayItemByLocationsAndLP(wraps,detail,toBeMovedQty,allocatedQty,taskType,atLeastLPQty);
		
		toBeMovedQty = (Double)map.get("toBeMovedQty");
		allocatedQty = (Double)map.get("allocatedQty");

		//如果在垂直补货情况下，一列库位放完还有剩余数量，
		//则在库位定位分类里随便找一个排序号最小的库位作为参考库位来取得其同列的待上架库位再次上架
		if(toBeMovedQty>0&&putRackRule.getVerticalAutoMove()){
			allocatedQty = getNeighborLocations(putRackRule,wraps,detail,toBeMovedQty,allocatedQty,taskType,atLeastLPQty);
		}
		return allocatedQty;
	}
	
	private void hardAllocate(MoveDocDetail detail, String taskType) {
		double toBeAllocate = detail.getPlanQuantityOfMU()-detail.getAllocateQuantity();
		//如果分配的目标库位与上架明细之前的目标库位一致，则直接增加分配数量
		//否则新增一条移位计划明细，并扣减原移位明细的计划数量
		Task task = getTaskByTypeAndUK(detail, taskType, detail.getSrcLoc(), detail.getSrcLP(), 
				detail.getDstLoc(),detail.getDstLP(), detail.getPackageUnit(), toBeAllocate);
		detail.allocate(toBeAllocate);
		
		allocateInventory(task);
		
		commonDao.store(task);
	}

	@SuppressWarnings("unchecked")
	private List<PutRackRule> getPutRackRules(MoveDocDetail detail, boolean isLP){
		String	itemPutawayType = null;
		if(detail.getItemKey().getItem().getWarehouseItemStorageRule()!=null 
				&& detail.getItemKey().getItem().getWarehouseItemStorageRule().getItemPutawayType()!=null) {
		      itemPutawayType = detail.getItemKey().getItem().getWarehouseItemStorageRule().getItemPutawayType();
		}
		String abcType = null;
		if (detail.getItem().getWarehouseItemStorageRule() != null 
				&& detail.getItem().getWarehouseItemStorageRule().getAbcType() != null) {
			abcType = detail.getItem().getWarehouseItemStorageRule().getAbcType().getAbcType();
		}
		List<PutRackRule> putRackRules  = this.commonDao.findByNamedQuery("getPutRackRuleForPutAway",
				new String[]{"warehouseId","companyId","billTypeId","itemPutawayType","itemId","packageLineNo",
					"LP","propC1","propC2","propC3","propC4","propC5","propC6","propC7","propC8","propC9",
					"propN1","propN2","propN3","propD1","propD2","propD3","propL1","propL2","propL3","qualityCode",
					"itemBigType","itemMiddleType","itemLittleType","pickQuantityMinPU","pickQuantity","abcType"},
				new Object[]{WarehouseHolder.getWarehouse().getId(),
				detail.getItemKey().getItem().getCompany().getId(),
				detail.getMoveDoc().getAsnType()==null?(detail.getMoveDoc().getBillType() == null ? null : detail.getMoveDoc().getBillType().getId()):detail.getMoveDoc().getAsnType().getId(),
				itemPutawayType,
				detail.getItemKey().getItem().getId(),
				detail.getPackageUnit().getLineNO(),
				isLP,
				detail.getItemKey().getLotInfo().getPropC1(),
				detail.getItemKey().getLotInfo().getPropC2(),
				detail.getItemKey().getLotInfo().getPropC3(),
				detail.getItemKey().getLotInfo().getPropC4(),
				detail.getItemKey().getLotInfo().getPropC5(),
				detail.getItemKey().getLotInfo().getPropC6(),
				detail.getItemKey().getLotInfo().getPropC7(),
				detail.getItemKey().getLotInfo().getPropC8(),
				detail.getItemKey().getLotInfo().getPropC9(),
				detail.getItemKey().getLotInfo().getPropN1(),
				detail.getItemKey().getLotInfo().getPropN2(),
				detail.getItemKey().getLotInfo().getPropN3(),
				detail.getItemKey().getLotInfo().getPropD1(),
				detail.getItemKey().getLotInfo().getPropD2(),
				detail.getItemKey().getLotInfo().getPropD3(),
				detail.getItemKey().getLotInfo().getPropL1(),
				detail.getItemKey().getLotInfo().getPropL2(),
				detail.getItemKey().getLotInfo().getPropL3(),
				detail.getInventoryStatus(),
				detail.getItemKey().getItem().getLargeClass(),
				detail.getItemKey().getItem().getCenterClass(),
				detail.getItemKey().getItem().getSmallClass(),
				detail.getPlanQuantityOfMU()-detail.getAllocatedQuantity(),
				Math.ceil((detail.getPlanQuantityOfMU()-detail.getAllocatedQuantity())/detail.getPackageUnit().getConvertFigure()),
				abcType});
		
		return putRackRules;
	}
	
	@SuppressWarnings("unchecked")
	private List<Location> getSortedLocationsByPutRackRule(MoveDocDetail detail, 
			PutRackRule putRackRule, Boolean isLP){
		String lpLimit = "";
		//是否托盘库位
		if  (isLP) {
			lpLimit += " and location.stowage.lpLimit > 0 ";
		} else {
			lpLimit += " and location.stowage.lpLimit = 0 ";
		}
		//指定库位或定位分类
		if (putRackRule.getPosition() != null) {
			lpLimit += " and location.positionIds like '%<" + putRackRule.getPosition().getId() + ">%' ";
		} else {
			lpLimit += " and location.id = " + putRackRule.getDefaultLocation().getId();
		}

		StringBuffer hql = new StringBuffer();
		hql.append(" from Location location ")
			.append(" where location.disabled = false ")
			.append(" and location.type = 'STORAGE' ")
			.append(" and location.usedRate < 100 and location.usedRate<stowage.fullRate ")
			//锁检查
			.append(" and (location.countLock is null or location.countLock = '') ")
			.append(" and (location.inLock is null or location.inLock = '') ")
			//尺寸检查
			.append(" and (location.stowage.length = 0 or location.stowage.length >= " + detail.getPackageUnit().getLength() + ") ")
			.append(" and (location.stowage.width = 0 or location.stowage.width >= " + detail.getPackageUnit().getWidth() + ") ")
			.append(" and (location.stowage.height = 0 or location.stowage.height >= " + detail.getPackageUnit().getHeight() + ") ")
			//混放规则
			.append(" and ( ")
			.append("  location.mixItem = true or (location.queuedQuantity=0 and location.totalQuantity=0)")
			.append("  or (location.mixItem = false and location.mixLot = true and (location.currentItem = " + detail.getItem().getId() +" or location.currentItem is null))")
			.append("  or (location.mixItem = false and location.mixLot = false and ( (location.currentItem = "+ detail.getItem().getId()+" and location.currentLot = "+ detail.getItemKey().getId()+")")
			.append("	                                                            or ( location.currentItem is null and location.currentLot is null )))")
			.append(")")
			//附加条件
			.append(lpLimit);
		
		List<Location> locations = this.commonDao.findByQuery(hql.toString());
//		List<Location> toBeRemoved = new ArrayList<Location>();
//		
//		for (Location loc : locations) {
//			// 混放规则检查
//			if (!checkMixRule(loc, detail.getItemKey().getItem()
//					.getId(), detail.getItemKey().getId())) {
//				toBeRemoved.add(loc);
//			}
//		}
//		locations.removeAll(toBeRemoved);
//		toBeRemoved.clear();
		
		if (locations == null || locations.size() <= 1) {
			return locations;
		}

		List<Location> res = new ArrayList<Location>();
		// 按缺省库位状态排序+库位排序号排序
		ComparatorChain chain =  new ComparatorChain();
		chain.addComparator(new BeanComparator("status"));
		chain.addComparator(new BeanComparator("sortIndex"));
		Collections.sort(locations, chain);
		
		List<Location> emptyWraps = new ArrayList<Location>();
		List<Location> storeWraps = new ArrayList<Location>();
		List<Location> pickWraps = new ArrayList<Location>();
		
		for (Location lw : locations) {
			if (lw.getStatus().equals(Location.PICK)) {
				pickWraps.add(lw);
			} else if (lw.getStatus().equals(Location.STORE)) {
				storeWraps.add(lw);
			} else {
				emptyWraps.add(lw);
			}
		}
		locations.clear();
		
		if (PutMethod.STORE_EMPTY_PICK.equals(putRackRule.getPutMethod())) {
			res.addAll(storeWraps);
			res.addAll(emptyWraps);
			res.addAll(pickWraps);
		} else if (PutMethod.EMPTY_STORE_PICK.equals(putRackRule.getPutMethod())) {
			res.addAll(emptyWraps);
			res.addAll(storeWraps);
			res.addAll(pickWraps);
		} else if (PutMethod.STORE_PICK_EMPTY.equals(putRackRule.getPutMethod())) {
			res.addAll(storeWraps);
			res.addAll(pickWraps);
			res.addAll(emptyWraps);
		} else if (PutMethod.EMPTY_PICK_STORE.equals(putRackRule.getPutMethod())) {
			res.addAll(emptyWraps);
			res.addAll(pickWraps);
			res.addAll(storeWraps);
		} else if (PutMethod.PICK_STORE_EMPTY.equals(putRackRule
				.getPutMethod())) {
			res.addAll(pickWraps);
			res.addAll(storeWraps);
			res.addAll(emptyWraps);
		} else {
			res.addAll(pickWraps);
			res.addAll(emptyWraps);
			res.addAll(storeWraps);
		}
		return res;
	}

	/**
	 * 查找上架明细中是否已经有相同属性的明细存在，如没有则自动新增一条明细
	 * @param moveDocDetail
	 * @param dstLoc
	 * @param quantity 任务数量（基本单位数量）
	 * @return
	 */
	private Task getTaskByTypeAndUK(MoveDocDetail detail , String type, Location srcLoc, Stock srcLP, 
			Location dstLoc, Stock dstLP, PackageUnit packageUnit, double quantity) {
		//生成新的Task
		Task newTask = EntityFactory.getEntity(Task.class);
		newTask.setCode(this.codeManager.getCodeForYm("", 10));//this.codeManager.generateTask());
		newTask.setType(type);
		newTask.setBillCode(detail.getMoveDoc().getCode());
		newTask.setItemKey(detail.getItemKey());
		newTask.setStorageDate(detail.getStorageDate());
		newTask.setSOI(detail.getSOI());
		newTask.setPackageUnit(packageUnit);
		newTask.setInventoryStatus(detail.getInventoryStatus());
		newTask.setSrcLoc(srcLoc);
		newTask.setSrcLP(srcLP);
		newTask.setSrcLPCode(srcLP==null?null:srcLP.getLP());
		newTask.setDstLoc(dstLoc);
		newTask.setDstLP(dstLP);
		newTask.setDstLPCode(dstLP==null?null:dstLP.getLP());
		newTask.setPlanQuantityMU(quantity);
		newTask.setOrderDate(detail.getMoveDoc().getCreateDate());//增加订单日期
		newTask.setMoveDocDetail(detail);
		detail.getTasks().add(newTask);
			
		return newTask;
	}
	
	/**
	 * 计算库位上最多允许放货物的数量、体积或者重量，取3者最小的为存放依据（必须满足最小的数量）
	 * @param location
	 * @param packageUnit
	 * @return
	 */
	public Double calculateLocationCanPutAwayQty(Location location, PackageUnit packageUnit) {
		double maxRate = location.getStowage().getFullRate();
		if (maxRate <= 0) {
			maxRate = 100;
		}
		double usedRate = location.getUsedRate();
		//未设置包装信息时，默认可放无限多的数量
		double allowW = 999999999;
		double allowV = 999999999;
		double allowQ = 999999999;
		double result = 999999999;
		if (packageUnit.getWeight() > 0 && location.getStowage().getWeight()>0) {
			allowW = (((maxRate - usedRate)/100)*location.getStowage().getWeight())/packageUnit.getWeight();
		}
		if (packageUnit.getVolume() > 0 && location.getStowage().getVolume()>0) { 
			allowV = (((maxRate - usedRate)/100)*location.getStowage().getVolume())/packageUnit.getVolume();
		}
		if(packageUnit.getConvertFigure() > 0 && location.getStowage().getQuantity()>0){
			allowQ = ((maxRate - usedRate)/100)*location.getStowage().getQuantity();
		}
		
		//如果库位是托盘库位，则计算剩余可放托盘数量
		if (location.getStowage().getLpLimit() > 0) {
			result = location.getStowage().getLpLimit() - location.getTotalLP() - location.getQueuedLP();
		} else {
			//取最小的可上架数量返回
			if(result>allowQ)
				result = allowQ;
			if(result>allowV)
				result = allowV;
			if(result>allowW)
				result = allowW;
		}
		
		if (packageUnit.getPrecision() == 0) {
			//取下整改为四舍五入
			result = Math.round(result);
//			result = Math.floor(result);
		} else {
			result = DoubleUtils.formatByPrecision(result, packageUnit.getPrecision());
		}
		return Double.valueOf(result);
	}
	
	private boolean checkMixRule(Location loc, Long itemId, Long lot) {
		if (loc.isMixLot() && !loc.isMixItem() 
				&& (loc.getCurrentItem() != null && !itemId.equals(loc.getCurrentItem()))) {
			return false;
		}

		if (!loc.isMixLot()	&& !loc.isMixItem()
				&& ((loc.getCurrentItem() != null && !itemId.equals(loc.getCurrentItem()))
				|| (loc.getCurrentLot() != null && !lot.equals(loc.getCurrentLot())))) {
			return false;
		}
		return true;
	}

	@SuppressWarnings("unchecked")
	public void unallocate(MoveDoc moveDoc) {
		if (MoveDocStatus.ALLOCATE.equals(moveDoc.getStatus())) {
			List<Task> tasks = commonDao.findByQuery("FROM Task task WHERE task.moveDocDetail.moveDoc.id = "
							+ moveDoc.getId() + " AND (task.planQuantityMU-task.movedQuantityMU) > 0");
			for (Task task : tasks) {
				double quantity = task.getPlanQuantityMU()-task.getMovedQuantityMU();
				if (quantity > 0) {
					// 取消源库存的锁定数量
					unAllocateInventory(task);

					if (TaskType.MV_CROSS.equals(task.getType())
							&& task.getPickTicketDetail() != null) {
						task.getPickTicketDetail().unallocateCross(quantity);
					}

					// 修改单据的分配数量
					task.unallocateMove(quantity);
					if (task.getPlanQuantityMU() == .0) {
						task.getMoveDocDetail().getTasks().remove(task);
					}

					if (task.getDstLP() != null) {
						commonDao.delete(task.getDstLP());
					}
				}
			}

			moveDoc.setStatus(MoveDocStatus.OPEN);
			commonDao.store(moveDoc);
		}
	}
	
	
	@SuppressWarnings("unchecked")
	public void unallocateTask(Task task) {
		double quantity = task.getPlanQuantityMU()-task.getMovedQuantityMU();
		if (quantity > 0) {
			// 取消源库存的锁定数量
			unAllocateInventory(task);
			if (TaskType.MV_CROSS.equals(task.getType())
					&& task.getPickTicketDetail() != null) {
				task.getPickTicketDetail().unallocateCross(quantity);
			}
			// 修改单据的分配数量
			task.unallocateMove(quantity);
			if (task.getPlanQuantityMU() == .0) {
				task.getMoveDocDetail().getTasks().remove(task);
			}

			if (task.getDstLP() != null) {
				commonDao.delete(task.getDstLP());
			}
		}

	}
	
	private void allocateInventory(Task task) {
		String barCode=null;
		ItemKeyInventory itemKeyInventory = inventoryManager.getItemKeyInventory(task.getSrcLoc().getWarehouse(), task.getItemKey());
		Inventory srcInv = itemKeyInventory.getInventory(task.getSrcLoc(), task.getPackageUnit(), 
				task.getSrcLP(), barCode,task.getInventoryStatus(), task.getStorageDate(), task.getSOI());
		srcInv.allocate(task.getPlanQuantityMU(), task.getDstLoc(), task.getDstLP());
	}
	
	private void unAllocateInventory(Task task) {
		List<Inventory> invs = new ArrayList<Inventory>();

		ItemKeyInventory iki = inventoryManager.getItemKeyInventory(
				task.getSrcLoc().getWarehouse(), task.getItemKey());
		invs.addAll(iki.getInventories());
		double toBeUnallocateQty = task.getPlanQuantityMU() - task.getMovedQuantityMU();
		for (Inventory inv : invs) {
			//对满足条件库存逐一扣减，直到取消分配完毕
			if (inv.getQueuedQuantity()>0 
					&& (inv.getLocation().getId().equals(task.getSrcLoc().getId()) 
					&& ((task.getSrcLP() == null && inv.getLP() == null) 
							|| (inv.getLP() != null && task.getSrcLP() != null 
									&& inv.getLP().getId().equals(task.getSrcLP().getId())))
					&& sdf.format(inv.getStorageDate()).equals(sdf.format(task.getStorageDate())) 
					&& inv.getSOI().equals(task.getSOI())
					&& inv.getStatus().equals(task.getInventoryStatus()))) {
				// 对源库存取消分配
				if (toBeUnallocateQty <= inv.getQueuedQuantity()) {
					inv.unallocate(toBeUnallocateQty, task.getDstLoc(), task.getDstLP());
					toBeUnallocateQty = 0;
				} else {
					inv.unallocate(inv.getQueuedQuantity(), task.getDstLoc(), task.getDstLP());
					toBeUnallocateQty -= inv.getQueuedQuantity();
				}
			}
		}
	}

	private void createStockByDetail(MoveDocDetail detail, Location location, String taskType, 
			double fullLPQuantity,int lpQuantity, double itemQuantity,double tierQty,double stackHighQty) {
		for (int i = 1; i <= lpQuantity; i++) {
			//创建托盘
			Stock stock = EntityFactory.getEntity(Stock.class);
			if(detail.getSrcLP()!=null){
				stock = detail.getSrcLP();
			}else{
				stock.setWarehouse(location.getWarehouse());
				stock.setLocation(location);
				stock.setLP(codeManager.generateLP(location.getWarehouse().getCode()));
				stock.setType(StockType.PALLET);
				if (itemQuantity <= fullLPQuantity) {
					stock.setItemQty(itemQuantity);
				} else {
					stock.setItemQty(fullLPQuantity);
				}
				stock.setWeight(stock.getItemQty()*detail.getPackageUnit().getWeight());
				stock.setVolume(stock.getItemQty()*detail.getPackageUnit().getVolume());
				stock.setLocationIndex(0);
				stock.setEnabled(true);
				stock.setInventoryQty(0);
				stock.setTierQty(tierQty);
				stock.setStackHighQty(stackHighQty);
				stock.setType(StockType.PALLET);
				commonDao.store(stock);
			}
			
			//创建上架任务
			Task task = getTaskByTypeAndUK(detail, taskType, detail.getSrcLoc(), 
					detail.getSrcLP(), location, stock, detail.getPackageUnit(), stock.getItemQty());
			commonDao.store(task);	
			
			//明细上架分配
			detail.allocate(task.getPlanQuantityMU());
			//库存锁定
			allocateInventory(task);

			itemQuantity -= stock.getItemQty();
		}
	}
	
	public Task manualAllocatebyLocation(MoveDocDetail detail, Location location, PackageUnit packageUnit, 
			double toBeMovedQty,Stock srcLp) {
		// 如果待分配数量为0，则不再上架分配
//		toBeMovedQty = toBeMovedQty*packageUnit.getConvertFigure();
		if (toBeMovedQty <= 0) {
			return null;
		}
		if ((detail.getPlanQuantityOfMU()-detail.getAllocateQuantity())<toBeMovedQty) {
			throw new BusinessException("moveQuantity.is.big");
		}
		Stock dstLp=null;
//		if(srcLp!=null&&location.getType().equals(LocationType.STORAGE)){
//			String hql="from Inventory inv where inv.location.id="+location.getId();
//			if(detail.getSrcLP()==null){
//				hql+=" and inv.LP.id is not null";
//			}else{
//				hql+=" and (inv.LP.id!="+detail.getSrcLP().getId() +" or inv.LP.id is null)";
//			}
//			List<Inventory> invs=this.commonDao.findByQuery(hql);
//			if(invs.size()>0){
//				if(detail.getSrcLP()==null){
//					dstLp=invs.get(0).getLP();
//				}else{
//					throw new BusinessException(location.getCode()+"该库位上已有托盘");
//				}
//			}else{
//				dstLp=detail.getSrcLP();
//			}
//		}else if(location.getType().equals(LocationType.TRANSIT)){
//			dstLp=null;
//		}
		String taskType = getTaskTypeByBillType(detail.getMoveDoc().getBillType());
		Task task = getTaskByTypeAndUK(detail, taskType, detail.getSrcLoc(), detail.getSrcLP(), 
				location, dstLp, packageUnit, toBeMovedQty);
		commonDao.store(task);

		detail.allocate(toBeMovedQty);
		String barCode=null;
		if (task != null) {
			ItemKeyInventory itemKeyInventory = inventoryManager.getItemKeyInventory(
					task.getSrcLoc().getWarehouse(), task.getItemKey());
			//根据明细包装单位找到源库存
			Inventory srcInv = itemKeyInventory.getInventory(task.getSrcLoc(), detail.getPackageUnit(), 
					task.getSrcLP(), barCode,task.getInventoryStatus(), task.getStorageDate(), task.getSOI());
			srcInv.allocate(task.getPlanQuantityMU(), location, task.getDstLP());
		}
		return task;
	}
	
	/**
	 * 补货上架分配
	 * @param detail
	 * @param inventory
	 * @param quantity
	 */
	public String allocateByDetailAndInventory(MoveDocDetail detail, Inventory inventory, double quantity) {
		String taskType = getTaskTypeByBillType(detail.getMoveDoc().getBillType());
		
		List<PutRackRule> putRackRules = getPutRackRules(detail, Boolean.FALSE);
		if (putRackRules.size() < 1) {
			allocateErrMsg += "货物 " + detail.getItem().getCode() + " 未找到上架策略;";
			return allocateErrMsg;
		}
		for (PutRackRule putRackRule : putRackRules) {
			quantity -= allocateDetailByInventory(detail, putRackRule, taskType, inventory, quantity);
			if (quantity <= 0) {
				break;
			}
		}
		
		//如果拣出来的货无处可放，直接报错“补货库位空间不足”
		if (quantity > 0) {
			allocateErrMsg += detail.getItem().getCode()+"货品补货库位空间不足;";
		}
		
		return allocateErrMsg;
	}
	
	private double allocateDetailByInventory(MoveDocDetail detail, PutRackRule putRackRule, String taskType, 
			Inventory inventory, double quantity) {
		List<Location> wraps = getSortedLocationsByPutRackRule(detail, putRackRule, Boolean.FALSE);

		double toBeMovedQty = quantity;  //基本单位数量
		double allocatedQty = 0;
		
		if(wraps.size()<1){
			allocateErrMsg+= "货物 "+detail.getItem().getCode()+" 未找到合适库位(散货上架);";
			return allocatedQty;
		}
		for (Location locationWrap : wraps) {
			//如果待分配数量为0，则不再上架分配
			if (toBeMovedQty <= 0) {
				break;
			}
			//如果目标库位与源库位一致，跳过该库位
			if (detail.getSrcLoc() != null && detail.getSrcLoc().equals(locationWrap)) {
				continue;
			}
			//计算库位可放数量
			double leftQty = calculateLocationCanPutAwayQty(locationWrap, detail.getPackageUnit())
					* detail.getPackageUnit().getConvertFigure();
			
			//只能上架到有空位的非托盘库位
			if (leftQty>0) {
				//如果明细剩余可分配数量小于库位可分配数量，则直接分配
				//带托盘号的记录不能拆开分配
				Task task = null;
				if (leftQty>=toBeMovedQty) {
					//如果分配的目标库位与上架明细之前的目标库位一致，则直接增加分配数量
					//否则新增一条移位计划明细，并扣减原移位明细的计划数量
					task = getTaskByTypeAndUK(detail, taskType, inventory, 
							locationWrap, detail.getDstLP(), toBeMovedQty);
				} else {
					//如果待分配数量大于库位可容纳数量，并且是托盘分配，则该库位不能分配，而是继续寻找下一个库位，因为托盘不能分割。
					task = getTaskByTypeAndUK(detail, taskType, inventory,
							locationWrap, detail.getDstLP(), leftQty);
				}
				allocatedQty += task.getPlanQuantityMU();
				toBeMovedQty -= task.getPlanQuantityMU();
				commonDao.store(task);
				
				if (task != null) {
					detail.allocate(task.getPlanQuantityMU());
					inventory.allocate(task.getPlanQuantityMU(), task.getDstLoc(), task.getDstLP());
				}
			}
		}
		return allocatedQty;
	}
	
	private Task getTaskByTypeAndUK(MoveDocDetail detail , String type, Inventory inventory, 
			Location dstLoc, Stock dstLP, double quantity) {
		//生成新的Task
		Task newTask = EntityFactory.getEntity(Task.class);
		newTask.setCode(this.codeManager.generateTask());
		newTask.setType(type);
		newTask.setBillCode(detail.getMoveDoc().getCode());
		newTask.setMoveDocDetail(detail);
		newTask.setItemKey(inventory.getItemKeyInventory().getItemKey());
		newTask.setStorageDate(inventory.getStorageDate());
		newTask.setSOI(inventory.getSOI());
		newTask.setPackageUnit(inventory.getPackageUnit());
		newTask.setInventoryStatus(detail.getInventoryStatus());
		newTask.setSrcLoc(inventory.getLocation());
		newTask.setSrcLP(inventory.getLP());
		newTask.setDstLoc(dstLoc);
		newTask.setDstLP(dstLP);
		newTask.setPlanQuantityMU(quantity);
		newTask.setOrderDate(detail.getMoveDoc().getCreateDate());//增加订单日期
			
		return newTask;
	}
	
	/**
	 * 托盘上架分配
	 * @param loccations：可托盘上架库位
	 * @param detail：移位单明细
	 * @param toBeMovedQty：待上架数量（移位单明细剩余未上架数量）
	 * @param allocatedQty：已分配数量
	 * @param taskType：Task类型
	 * @param atLeastLPQty：起码数量
	 * @return
	 */
	private Map<String,Double> putawayItemByLocationsAndLP(List<Location> loccations,MoveDocDetail detail,double toBeMovedQty,double allocatedQty,String taskType,double atLeastLPQty){
		
		Map<String,Double> map = new HashMap<String,Double>();
		for (Location locationWrap : loccations) {
			// 如果待分配数量为0，则不再上架分配
			if (toBeMovedQty <= 0) {
				break;
			}
			// 当待上架数量<该货品包装的起码数量时,则直接走散货分配流程
			if (atLeastLPQty > 0 && toBeMovedQty < atLeastLPQty) {
				map.put("allocatedQty", allocatedQty);
				map.put("toBeMovedQty", toBeMovedQty);
				return map;
			}
			LPFormat lpFormat = detail.getPackageUnit().getFitLPFormat(locationWrap.getStowage().getHeight());
			
			//计算库位可放托盘数量
			int allowLPCount = calculateLocationCanPutAwayQty(locationWrap, detail.getPackageUnit()).intValue();
			if (allowLPCount <= 0) {
				continue;
			}
			
			//如果移位单明细上包含托盘，则直接分配，不用计算托盘数量
			if(detail.getSrcLP()!=null){
				createStockByDetail(detail, locationWrap, taskType, detail.getSrcLP().getItemQty(), 1, detail.getSrcLP().getItemQty(),lpFormat.getTierQty(),lpFormat.getStackHighQty());
				//扣减待分配数量
				toBeMovedQty -= detail.getSrcLP().getItemQty();
				//记录已分配数量
				allocatedQty += detail.getSrcLP().getItemQty();
				map.put("allocatedQty", allocatedQty);
				map.put("toBeMovedQty", toBeMovedQty);
				return map;
			}

			//获取满托数量（基本单位数量）
			double lpFullQty = 0;
			if (lpFormat == null || (lpFormat.getTierQty() == 0 && lpFormat.getStackHighQty() == 0  && lpFormat.getHeight() == 0)) {
					continue;
			} else {
				lpFullQty = (lpFormat.getTierQty()* lpFormat.getStackHighQty()) * detail.getPackageUnit().getConvertFigure();
			}
				
			//计算明细可码托数以及托盘中货品数量
			int lpCnt = Double.valueOf(Math.ceil(toBeMovedQty/lpFullQty)).intValue();
			//如果最后一个托盘货品数小于最低码托数，放弃该托盘
			if (toBeMovedQty % lpFullQty > 0 
					&& toBeMovedQty % lpFullQty < atLeastLPQty) {
				lpCnt -= 1;
			}
			if (lpCnt > allowLPCount) {
				lpCnt = allowLPCount;
			}
			if (lpCnt <= 0) {
				continue;
			}
			double itemQty = lpCnt * lpFullQty;
			if (itemQty > toBeMovedQty) {
				itemQty = toBeMovedQty;
			}
			
			// 生成托盘信息并完成码托分配
			createStockByDetail(detail, locationWrap, taskType, lpFullQty, lpCnt, itemQty,lpFormat.getTierQty(),lpFormat.getStackHighQty());
				
			//扣减待分配数量
			toBeMovedQty -= itemQty;
				
			//记录已分配数量
			allocatedQty += itemQty;
		}
		
		map.put("allocatedQty", allocatedQty);
		map.put("toBeMovedQty", toBeMovedQty);
		
		return map;
	}
	
	//散货上架
	private Map<String,Double> putawayItemByLocations(List<Location> loccations,MoveDocDetail detail,double toBeMovedQty,double allocatedQty,String taskType){
		for (Location locationWrap : loccations) {
			//如果待分配数量为0，则不再上架分配
			if (toBeMovedQty <= 0) {
				break;
			}
			//如果目标库位与源库位一致，跳过该库位
			if (detail.getSrcLoc() != null && detail.getSrcLoc().equals(locationWrap)) {
				continue;
			}
			//计算库位可放数量
			double leftQty = calculateLocationCanPutAwayQty(locationWrap, detail.getPackageUnit())
					* detail.getPackageUnit().getConvertFigure();
			
			//只能上架到有空位的非托盘库位
			if (leftQty>0) {
				//如果明细剩余可分配数量小于库位可分配数量，则直接分配
				//带托盘号的记录不能拆开分配
				Task task = null;
				if (leftQty >= toBeMovedQty) {
					//如果分配的目标库位与上架明细之前的目标库位一致，则直接增加分配数量
					//否则新增一条移位计划明细，并扣减原移位明细的计划数量
					task = getTaskByTypeAndUK(detail, taskType, detail.getSrcLoc(), detail.getSrcLP(), 
							locationWrap,detail.getDstLP(), detail.getPackageUnit(), toBeMovedQty);
				} else {
					//如果待分配数量大于库位可容纳数量，并且是托盘分配，则该库位不能分配，而是继续寻找下一个库位，因为托盘不能分割。
					task = getTaskByTypeAndUK(detail, taskType, detail.getSrcLoc(), detail.getSrcLP(), 
							locationWrap, detail.getDstLP(), detail.getPackageUnit(), leftQty);
				}
				detail.allocate(task.getPlanQuantityMU());
				allocatedQty += task.getPlanQuantityMU();
				toBeMovedQty -= task.getPlanQuantityMU();
				commonDao.store(task);
					
				if (task != null) {
					allocateInventory(task);
				}
			}
		}
		Map<String,Double> map = new HashMap<String,Double>();
		map.put("allocatedQty", allocatedQty);
		map.put("toBeMovedQty", toBeMovedQty);
		
		return map;
	}
	
	
	//垂直补货上架
	@SuppressWarnings("unchecked")
	private List<Location> verticalAutoMove(PutRackRule putRackRule,MoveDocDetail detail){
		//查找固定库位补货规则里有无该货品对应的存放规则
		List<ItemSupplyRule> isrs = commonDao.findByNamedQuery("getItemSupplyRule",
				new String[]{"warehouseId","itemId"}, new Object[]{WarehouseHolder.getWarehouse().getId(),detail.getItem().getId()});
		
		//如找到对应库位，则将该库位同一列上的其他库位进行上架操作
		if(isrs.size()>0){
			//取得补货库位
			for(ItemSupplyRule isr:isrs){
				Location srcLoc = isr.getReplenishmentLocation();
				//取得与当前库位同列的其他库位
				List<Location> loccations = commonDao.findByNamedQuery("getOtherLocations",
						new String[]{"warehouseId","zoneId","itemId","aisle","line","col","locId"},
						new Object[]{WarehouseHolder.getWarehouse().getId(),srcLoc.getZone().getId(),detail.getItem().getId(),
								     srcLoc.getAisle(),srcLoc.getLine(),srcLoc.getCol(),srcLoc.getId()});
				
				if(loccations.size()>0){
					//库位排序
					return getSortedLocationsByVertivalAutoMove(loccations,putRackRule);
				}
			}
		}
		
		
		List<Location> tempSuitLocations = new ArrayList<Location>();

		//取得策略内库位定位分类里的库位
		List<Location> tempLoc = commonDao.findByQuery("from Location loc where loc.disabled = false and loc.type='STORAGE'" 
		+" and (loc.countLock is null or loc.countLock = '')"
		+" and (loc.inLock is null or loc.inLock = '')"
		+" and loc.warehouse.id  = "+WarehouseHolder.getWarehouse().getId() 
		+" and loc.positionIds like '%<"+putRackRule.getPosition().getId()+">%'");
		
		//如固定补货规则里未找到对应存放规则，则比较上架策略库位定位分类里包含的库位，是否包含在垂直补货库位分类定义的库位里
		if (putRackRule.getVerticalPosition() != null) {
			//取得垂直补货库位分类里定义的库位集合(必须包含和当前上架货物对应的库存)
			List<Location> tempVerticalLoc = commonDao.findByQuery("from Location loc where loc.disabled = false and loc.type='STORAGE' "
					+" and (loc.countLock is null or loc.countLock = '')"
					+" and (loc.inLock is null or loc.inLock = '')"
					+" and loc.warehouse.id  = "+WarehouseHolder.getWarehouse().getId() 
					+" and (select count(*) from Inventory inv where inv.location.id = loc.id and inv.itemKeyInventory.itemKey.item.id= "+detail.getItem().getId()+")>0"
					+" and loc.positionIds like '%<"+putRackRule.getVerticalPosition().getId()+">%'");
			
			//检查定位分类里的库位是否包含在垂直补货分类的库位里,如是，则将与当前库位同列的其他库位加入待上架库位集合里
			for(Location location:tempLoc){
				if(tempVerticalLoc.contains(location)){
					//查找与该库位同列的其他库位
					String hql = "from Location loc where loc.disabled = false and loc.type='STORAGE' "
						+" and (loc.countLock is null or loc.countLock = '')"
						+" and (loc.inLock is null or loc.inLock = '')"
						+" and loc.warehouse.id  = "+WarehouseHolder.getWarehouse().getId() 
						+" and loc.zone.id = "+location.getZone().getId()
						+" and loc.totalQuantity = 0 and loc.totalLP = 0"
						+" and loc.aisle = "+location.getAisle()
						+" and loc.line = "+location.getLine()
						+" and loc.col = "+location.getCol()
						+" and loc.id != "+location.getId();
					List<Location> loccations = commonDao.findByQuery(hql);
					
					tempSuitLocations.addAll(loccations);
				}
			}
			
			if(tempSuitLocations.size()>0){
				//库位排序
				return  getSortedLocationsByVertivalAutoMove(tempSuitLocations,putRackRule);
			}
		}
		
		//如果以上两种方式都未找到合适待上架库位,则在库位定位分类里随便找一个排序号最小的库位作为参考库位来取得其同列的待上架库位
		tempLoc = getSortedLocationsByVertivalAutoMove(tempLoc,putRackRule);
		
		for(Location location:tempLoc){
			//查找与该库位同列的其他库位
			List<Location> loccations = commonDao.findByNamedQuery("getOtherLocations",
						new String[]{"warehouseId","zoneId","aisle","line","col","locId"},
						new Object[]{WarehouseHolder.getWarehouse().getId(),location.getZone().getId(),detail.getItem().getId(),
									location.getAisle(),location.getLine(),location.getCol(),location.getId()});
			//如找到同列的合适库位，就退出
			if(loccations.size()>0){
				tempSuitLocations.addAll(loccations);
				break;
			}
		}
		if(tempSuitLocations.size()>0){
			//库位排序
			return  getSortedLocationsByVertivalAutoMove(tempSuitLocations,putRackRule);
		}
		
		return null;
	}
	
	//查找相邻的库位上架
	@SuppressWarnings("unchecked")
	private double getNeighborLocations(PutRackRule putRackRule,List<Location>wraps,MoveDocDetail detail,double toBeMovedQty,double allocatedQty,String taskType,double atLeastLPQty ){
		
		StringBuffer sbf = new StringBuffer();
		for(Location loc:wraps){
			//if(loc.getTotalLP()!=0||loc.getTotalQuantity()!=0)
			sbf.append(loc.getId()).append(",");
		}
		String tempChar = sbf.toString();
		tempChar = tempChar.substring(0, tempChar.length()-1);
		
		//取得策略内库位定位分类里的库位,并且要排除当前已经上架了的库位和垂直补货规则里定义的库位
		String hql = "from Location loc where loc.disabled = false and loc.type='STORAGE' "
			+" and (loc.countLock is null or loc.countLock = '')"
			+" and (loc.inLock is null or loc.inLock = '')"
			+" and loc.warehouse.id  = "+WarehouseHolder.getWarehouse().getId() 
			+" and loc.positionIds like '%<"+putRackRule.getPosition().getId()+">%'"
			+(putRackRule.getVerticalPosition()==null?"":" and loc.positionIds not like '%<"+putRackRule.getVerticalPosition().getId()+">%'")
			+" and ((select count(*) from Inventory inv where inv.location.id = loc.id and inv.itemKeyInventory.itemKey.item.id= "+detail.getItem().getId()+")>0" 
			+" or (loc.totalQuantity = 0 and loc.totalLP = 0))"
			+" and loc.id not in (select isr.replenishmentLocation.id from ItemSupplyRule isr where isr.warehouse.id = "+WarehouseHolder.getWarehouse().getId() 
			+" and isr.item.id = "+detail.getItem().getId()
			+" and isr.replenishmentLocation.id is not null)"
			+" and loc.id not in ("+tempChar+")";
		
		List<Location> tempLoc = commonDao.findByQuery(hql);
		
		if(tempLoc.size()>0){
			Map<String,Double> map1 = new HashMap<String,Double>();
			if(detail.getPackageUnit().isAllowLP())
				map1 = putawayItemByLocationsAndLP(tempLoc,detail,toBeMovedQty,allocatedQty,taskType,atLeastLPQty);
			else
				map1 = putawayItemByLocations(tempLoc,detail,toBeMovedQty,allocatedQty,taskType);
				
			allocatedQty = (Double)map1.get("allocatedQty");
		}
		
		return allocatedQty;
	}
	
	//垂直补货分类里定义的库位排序
	@SuppressWarnings("unchecked")
	private List<Location> getSortedLocationsByVertivalAutoMove(List<Location> locations,PutRackRule putRackRule){
		List<Location> res = new ArrayList<Location>();
		// 按缺省库位状态排序+库位排序号排序
		ComparatorChain chain =  new ComparatorChain();
		chain.addComparator(new BeanComparator("status"));
		chain.addComparator(new BeanComparator("sortIndex"));
		Collections.sort(locations, chain);
		
		List<Location> emptyWraps = new ArrayList<Location>();
		List<Location> storeWraps = new ArrayList<Location>();
		List<Location> pickWraps = new ArrayList<Location>();
		
		for (Location lw : locations) {
			if (lw.getStatus().equals(Location.PICK)) {
				pickWraps.add(lw);
			} else if (lw.getStatus().equals(Location.STORE)) {
				storeWraps.add(lw);
			} else {
				emptyWraps.add(lw);
			}
		}
		locations.clear();
		
		if (PutMethod.STORE_EMPTY_PICK.equals(putRackRule.getPutMethod())) {
			res.addAll(storeWraps);
			res.addAll(emptyWraps);
			res.addAll(pickWraps);
		} else if (PutMethod.EMPTY_STORE_PICK.equals(putRackRule.getPutMethod())) {
			res.addAll(emptyWraps);
			res.addAll(storeWraps);
			res.addAll(pickWraps);
		} else if (PutMethod.STORE_PICK_EMPTY.equals(putRackRule.getPutMethod())) {
			res.addAll(storeWraps);
			res.addAll(pickWraps);
			res.addAll(emptyWraps);
		} else if (PutMethod.EMPTY_PICK_STORE.equals(putRackRule.getPutMethod())) {
			res.addAll(emptyWraps);
			res.addAll(pickWraps);
			res.addAll(storeWraps);
		} else if (PutMethod.PICK_STORE_EMPTY.equals(putRackRule
				.getPutMethod())) {
			res.addAll(pickWraps);
			res.addAll(storeWraps);
			res.addAll(emptyWraps);
		} else {
			res.addAll(pickWraps);
			res.addAll(emptyWraps);
			res.addAll(storeWraps);
		}
		return res;
	}
	
}
