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

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

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.lang.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import com.dev.swms.client.ui.constant.CT_PA;
import com.dev.swms.client.ui.javabean.PT_ALLOCATED;
import com.dev.swms.client.ui.javabean.PT_AVAILABLE;
import com.dev.swms.client.ui.javabean.PT_DETAILS;
import com.dev.swms.client.ui.javabean.PT_INFO;
import com.dev.swms.server.model.abstractEntity.AbstractMoveDocDetail;
import com.dev.swms.server.model.base.BoxModel;
import com.dev.swms.server.model.base.BoxModelType;
import com.dev.swms.server.model.base.CompanyCode;
import com.dev.swms.server.model.base.Item;
import com.dev.swms.server.model.base.ItemKey;
import com.dev.swms.server.model.base.Location;
import com.dev.swms.server.model.base.NewLotInfo;
import com.dev.swms.server.model.base.PackageUnit;
import com.dev.swms.server.model.base.Structure;
import com.dev.swms.server.model.base.TurnOverMode;
import com.dev.swms.server.model.base.Warehouse;
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.Task;
import com.dev.swms.server.model.rules.CompanyWorkRule;
import com.dev.swms.server.model.rules.SortRule;
import com.dev.swms.server.model.rules.WaveRule;
import com.dev.swms.server.model.rules.WorkModel;
import com.dev.swms.server.model.shipping.CageInfo;
import com.dev.swms.server.model.shipping.MachiningDetail;
import com.dev.swms.server.model.shipping.PickTicket;
import com.dev.swms.server.model.shipping.PickTicketDetail;
import com.dev.swms.server.model.shipping.PickTicketStatus;
import com.dev.swms.server.model.shipping.Prewave;
import com.dev.swms.server.model.shipping.TempTask;
import com.dev.swms.server.model.shipping.WaveDoc;
import com.dev.swms.server.model.shipping.WaveDocDetail;
import com.dev.swms.server.model.shipping.WaveDocItem;
import com.dev.swms.server.model.shipping.WaveDocStatus;
import com.dev.swms.server.service.base.ItemManager;
import com.dev.swms.server.service.inventory.InventoryManager;
import com.dev.swms.server.service.picking.allocate.AutoAllocator;
import com.dev.swms.server.service.sequence.BussinessCodeManager;
import com.dev.swms.server.service.shipping.PickTicketManager;
import com.dev.swms.server.service.shipping.WaveDocManager;
import com.dev.swms.server.utils.DateUtil;
import com.dev.swms.server.utils.DateUtils;
import com.dev.swms.server.utils.DoubleUtils;
import com.dev.swms.server.utils.NewLotInfoParser;
import com.dev.swms.server.web.filter.WarehouseHolder;
import com.dev.energy.client.ui.page.IPage;
import com.dev.energy.server.exception.BusinessException;
import com.dev.energy.server.model.EntityFactory;
import com.dev.energy.server.service.pojo.DefaultBaseManager;
import com.dev.energy.server.util.LocalizedMessage;
import com.dev.energy.server.web.security.UserHolder;

public class DefaultWaveDocManager extends DefaultBaseManager implements
		WaveDocManager {

	private final PickTicketManager pickTicketManager;
	protected final AutoAllocator autoAllocator;
	protected final InventoryManager inventoryManager;
	protected final ItemManager itemManager;
	protected final BussinessCodeManager bussinessCodeManager;
	
	public DefaultWaveDocManager(PickTicketManager pickTicketManager, AutoAllocator autoAllocator,
			InventoryManager inventoryManager, ItemManager itemManager,BussinessCodeManager bussinessCodeManager) {
		this.pickTicketManager = pickTicketManager;
		this.autoAllocator = autoAllocator;
		this.inventoryManager = inventoryManager;
		this.itemManager=itemManager;
		this.bussinessCodeManager = bussinessCodeManager;
	}
	@SuppressWarnings("unchecked")
	public void createWaveDoc() {
		Long warehouseId=WarehouseHolder.getWarehouse().getId();
		String pts_hql="from PickTicket pt where  pt.company.id=:companyId and (pt.status=:status1 or pt.status=:status2) and pt.warehouse.id=:warehouseId";
		List<Long> companyIds=commonDao.findByQuery("select distinct(pt.company.id) from PickTicket pt where pt.waveDoc.id is null and "
				+ " (pt.status=:status1 or pt.status=:status2) and pt.warehouse.id=:warehouseId",
				new String[]{"status1","status2","warehouseId"},
				new Object[]{PickTicketStatus.ACTIVE,PickTicketStatus.PREALLOCATE,warehouseId});
		for(Long companyId:companyIds){
			WaveDoc waveDoc=createWaveDoc(companyId,warehouseId);
			List<PickTicket> pts=commonDao.findByQuery(pts_hql,
					new String[]{"companyId","status1","status2","warehouseId"},
					new Object[]{companyId,PickTicketStatus.ACTIVE,PickTicketStatus.PREALLOCATE,warehouseId});
			if(pts.size()>0){
//				List<Long> pickTicketIds=commonDao.findByQuery(ptIds_hql,
//						new String[]{"companyId","status1","status2","warehouseId"},
//						new Object[]{companyId,PickTicketStatus.ACTIVE,PickTicketStatus.PREALLOCATE,warehouseId});
				String s="";
				for(PickTicket pt:pts){
					//s=autoAllocator.allocate(pt.getId(),null,null,null);
					waveDoc.setQuantity(waveDoc.getQuantity()+pt.getOrderQuantity());
					waveDoc.setWeight(waveDoc.getWeight()+pt.getWeight());
					waveDoc.setVolumn(waveDoc.getVolumn()+pt.getVolume());
					pt.setWaveCode(waveDoc.getCode());
					pt.setWaveDoc(waveDoc);
					this.commonDao.store(pt);
				}
				waveDoc.addPickTicketsByPickBatchModel(pts);//把明细合并加入到补货单。再判断是否需要补货操作
				Set<WaveDocDetail> wdds = new HashSet<WaveDocDetail>();
				for (WaveDocDetail wdd : waveDoc.getWaveDocDetails()) {
					if(wdd.getPlanBhQty()>0){
						NewLotInfo lotInfo = wdd.getNewLotInfo() == null ? new NewLotInfo() : wdd.getNewLotInfo();
						itemManager.formatNewLotInfo(lotInfo);
					 	StringBuffer sb = new StringBuffer();
					 	String hql =sb.append("SELECT ")
							.append("nvl(SUM(inventory.qtyOfMasterPack),0) ")
							.append("FROM Inventory inventory ")
							.append("WHERE inventory.itemKeyInventory.warehouse.id = :warehouseId ")
							.append(" AND inventory.itemKeyInventory.itemKey.item.id = :itemId")
							.append(" AND inventory.location.id = :locationId")
							.append(" AND (inventory.status is null or inventory.status = '' or inventory.status = '非破损')").toString();
							//.append(" AND (inventory.location.countLock is null or inventory.location.countLock = '')")
							//.append(" AND (inventory.location.outLock is null or inventory.location.outLock = '')").toString(); 
					    	if(lotInfo!=null){
					    		if(!waveDoc.getCompany().getCode().equals(CompanyCode.XBK)){
							    	if(lotInfo.getPropC1()!=null){
							    		hql+=" and inventory.itemKeyInventory.itemKey.lotInfo.propC1='"+lotInfo.getPropC1()+"'";
							    	}
							    	if(lotInfo.getPropD2()!=null){
							    		hql+=" and to_char(inventory.itemKeyInventory.itemKey.lotInfo.propD2,'yyyy-mm-dd')='"+lotInfo.getPropD2()+"'";
							    	}
					    		}
					    		 if(lotInfo.getPropD1()!=null){
							    		hql+=" and to_char(inventory.itemKeyInventory.itemKey.lotInfo.propD1,'yyyy-mm-dd')='"+lotInfo.getPropD1()+"'";
							    	}
					    	}
					    	 Double inventoryQty = (Double)this.commonDao.findByQuery(hql, 
					         		new String[] {"warehouseId", "itemId","locationId"},
					         		new Object[] {waveDoc.getWarehouse().getId(), wdd.getItem().getId(),wdd.getItem().getLocation().getId()}).get(0);;
//					 Double invQty=(Double) this.commonDao.findByQuery("select nvl(sum(inv.qtyOfMasterPack-inv.queuedQuantity),0) from Inventory inv "
//					 		+ "where inv.location.id=:locationId and inv.packageUnit.id=:packageUnitId "
//								+ " and inv.qtyOfMasterPack-inv.queuedQuantity>0",
//								new String[]{"locationId","packageUnitId"},new Object[]{wdd.getItem().getLocation().getId(),wdd.getPackageUnit().getId()}).get(0);
					if(inventoryQty>wdd.getPlanBhQty()){
						inventoryQty=wdd.getPlanBhQty();
					}
					wdd.setOrderQuantity(wdd.getPlanBhQty()-inventoryQty);
					wdd.setOrderQtyOfMasterUnit(wdd.getOrderQuantity());
					waveDoc.setOrderQuantity(waveDoc.getOrderQuantity()-inventoryQty);
					this.commonDao.store(wdd);
					}
//					if (wdd.getOrderQtyOfMasterUnit() <= 0) {
//						wdds.add(wdd);
//					}
				}
				//waveDoc.getWaveDocDetails().removeAll(wdds);
				commonDao.store(waveDoc);
				if(s!=null&&!s.equals("")){
					LocalizedMessage.addLocalizedMessage(s);
				}else{
//					waveDoc=createWaveDoc(warehouseId,companyId);
//					waveDoc.addPickTicketsByPickBatchModel(pts);
//					Set<WaveDocDetail> wdds = new HashSet<WaveDocDetail>();
//					for (WaveDocDetail wdd : waveDoc.getWaveDocDetails()) {
//						if (wdd.getOrderQtyOfMasterUnit() <= 0) {
//							wdds.add(wdd);
//						}
//					}
//					waveDoc.getWaveDocDetails().removeAll(wdds);
//					commonDao.store(waveDoc);
					//pickTicketManager.unPreAllocate(pickTicketIds);
				}
			}
		}
	}
	
	WaveDoc createWaveDoc(Long companyId,Long warehouseId){
		List<WaveDoc> waveDocs=commonDao.findByQuery("from WaveDoc w where to_char(w.createDate,'yyyy-mm-dd')=:createDate and w.status=:status and w.company.id=:companyId",
				new String[]{"createDate","status","companyId"},
				new Object[]{DateUtil.formatDateYMDToStr(new Date()),WaveDocStatus.OPEN,companyId});
		WaveDoc waveDoc=null;
		if(waveDocs.size()>0){
			waveDoc=waveDocs.get(0);
		}else{
			waveDoc=EntityFactory.getEntity(WaveDoc.class);
			waveDoc.setCode(bussinessCodeManager.generateDayCode("BC", 3));
			waveDoc.setStatus(WaveDocStatus.OPEN);
			waveDoc.setWorkModel(WorkModel.PickBatchModel);
			waveDoc.setStartDate(new Date());
			waveDoc.setTurnOverMode(TurnOverMode.FIFO_VALIDDATE);
			waveDoc.setUserTracking();
			waveDoc.setCreateDate(new Date());
			waveDoc.setCompany(this.commonDao.get(Structure.class, companyId));
			waveDoc.setWarehouse(WarehouseHolder.getWarehouse());
			commonDao.store(waveDoc);
		}
		return waveDoc;
	}
	/**
	 * 1、波次创建时参考新的波次规则筛选订单加入波次，波次规则筛选条件中高级规则和其他设置只能选一组作为参考条件； 
	 * 2、点击波次创建菜单时，要在弹出窗口中输入一个"当天时间"，并以当天时间为参考时间基准跟波次规则中查询条件配合使用； 
	 * 3、波次创建时要参考订单对应货主作业规则中的周转方式和作业方式，不同的周转方式和作业方式要强制分成不同波次，并要在波次头上显示对应的周转方式和作业方式； 
	 * 4、指定波次如果已经生效（参考波次号或波次的作业时间范围），如果创建波次时发现该时间范围内还有符合波次条件的订单，则创建一个新的波次，否则将新订单自动加入原有波次； 
	 		例如：波次规则：0001 09:00---18:00 所有订单 
                  工作人员在下午5点创建了波次，里面有50个订单，波次号为0001-20091015 
                  在下午5点到6点又来了10个订单 
                  工作人员在下午6点再次创建波次，此时如果波次未生效，则把10个订单加入原来的波次，即波次有60个订单；反之则产生一个新波次，里面有10个订单，波次号为0001-20091015-2
     * 5、注意翻天问题：例如波次规则起始时间是20:00截止时间是08:00，则截止时间要翻天，表示波次拣货单时间是当天晚上8点到第二天早上8点。   
	 */
	@SuppressWarnings("unchecked")
	public void createWaveDoc(Date baseDate) {
		List<WaveRule> waveRules = this.commonDao.findByNamedQuery(
				"getWaveRulesByWarehouse", new String[] { "warehouseId"},
				new Object[] { WarehouseHolder.getWarehouse().getId()});
		if(waveRules==null||waveRules.size()==0){
			return;
		}
		for(WaveRule waveRule:waveRules){
			List<PickTicket> pickTickets = getPickTicketByWaveRule(waveRule,baseDate);//查询适合波次的拣货单
			if(pickTickets == null || pickTickets.size() == 0){
				continue;
			}
			Map map = groupPickTickets(pickTickets);//对拣货单分组，每个分组一个波次
			
			for (Object obj : map.keySet()) {
				String objs = (String)obj;
				String[] strs = objs.split("#");
				String turnOverMode = strs[0].trim();
				String pickBatchType = strs[1].trim();
				List<PickTicket> pks = (List)map.get(obj);
				createWaveDoc(pks,turnOverMode,pickBatchType,waveRule);
			}
		}
	}
	/**按照周转方式、作业方式、波次规则号对拣货单创建波次
	 * @param pickTickets
	 * @param turnOverMode
	 * @param pickBatchType
	 * @param waveRuleId
	 */
	
	private void createWaveDoc(List<PickTicket> pickTickets,String turnOverMode,String pickBatchType,WaveRule waveRule){
		SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
		
		WaveDoc waveDoc = (WaveDoc)this.commonDao.findByNamedQueryUniqueResult("getWaveDocByCompanyWorkRule",
				new String[]{"turnOverMode","pickBatchType","waveRuleId"},
				new Object[]{turnOverMode,pickBatchType,waveRule.getId()});
		if (waveDoc == null) {// 没有可以加入的波次
			String code = waveRule.getWaveCode() + "_" + df.format(new Date());
			String hql = "select count(*) from WaveDoc waveDoc where waveDoc.code like '"
					+ code + "%'";
			Long nums = (Long) this.commonDao.findByQueryUniqueResult(hql,
					new String[] {}, new Object[] {});
			if (nums != 0)
				code = code + "_" + nums;
			waveDoc = new WaveDoc();
			waveDoc.setCode(code);
			waveDoc.setStatus(WaveDocStatus.OPEN);
		}
		
		if (waveRule.getWorkModel().equals(WorkModel.PickTicketModel)) {
			waveDoc.addPickTicketsByPickTicketModel(pickTickets);
		} else {
			waveDoc.addPickTicketsByPickBatchModel(pickTickets);
		}
		waveDoc.setStartDate(DateUtil.getDateByTime(waveRule.getStartTime()));
		waveDoc.setWaveRules(waveRule.getWaveRules());
		waveDoc.setWorkModel(waveRule.getWorkModel());
		waveDoc.setPickBatchType(pickBatchType);//批拣模式，用于超商项目
		waveDoc.setWaveRuleId(waveRule.getId());
		waveDoc.setTurnOverMode(turnOverMode);
		waveDoc.setUserTracking();
		waveDoc.setCreateDate(new Date());
		
		Set<WaveDocDetail> wdds = new HashSet<WaveDocDetail>();
		for (WaveDocDetail wdd : waveDoc.getWaveDocDetails()) {
			if (wdd.getOrderQtyOfMasterUnit() <= 0) {
				wdds.add(wdd);
			}
		}
		waveDoc.getWaveDocDetails().removeAll(wdds);
		commonDao.store(waveDoc);
	}
	
	/**
	 * 波次创建时要参考订单对应货主作业规则中的周转方式和作业方式，不同的周转方式和作业方式要强制分成不同波次，并要在波次头上显示对应的周转方式和作业方式
	 * 此方法对拣货单按照周转方式和作业方式进行分组
	 * @param pickTickets
	 * @return
	 */
	private Map groupPickTickets(List<PickTicket> pickTickets){
		Map map = new HashMap();
		for(PickTicket pickTicket:pickTickets){
			CompanyWorkRule rule = pickTicket.getCompany().getCompanyWorkRule();
			String mapKey = (rule.getCompanyTurnOverMode()==null?" ":rule.getCompanyTurnOverMode())+"#"+(rule.getPickBatchType()==null?" ":rule.getPickBatchType());
			if(map.get(mapKey)==null){
				List<PickTicket> pks = new ArrayList<PickTicket>();
				pks.add(pickTicket);
				map.put(mapKey,pks);
			}else{
				List<PickTicket> pks = (List)map.get(mapKey);
				pks.add(pickTicket);
			}
		}
		return map;
	}
	
	/**
	 * 依据波次规则获得拣货单列表
	 * @param waveRule
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<PickTicket> getPickTicketByWaveRule(WaveRule waveRule, Date baseDate){
		String hql = getHqlForWave(waveRule,baseDate);
		List<PickTicket> pickTickets = commonDao.findByQuery(hql,
				new String[]{"warehouseId"},
				new Object[]{WarehouseHolder.getWarehouse().getId()});
		List<PickTicket> notPreAllocs = new ArrayList<PickTicket>();
		for(PickTicket pickTicket:pickTickets){
			if(!pickTicketManager.preAllocate(pickTicket.getId())){
				notPreAllocs.add(pickTicket);
			}
		}
		if(notPreAllocs.size()>0){
			pickTickets.removeAll(notPreAllocs);
		}
		return pickTickets;
	}
	
	private String getHqlForWave(WaveRule waveRule, Date baseDate){
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		String hql= "SELECT pickTicket FROM PickTicket pickTicket " +
				"LEFT JOIN pickTicket.company company " +
				"LEFT JOIN pickTicket.billType billType " +
				"LEFT JOIN pickTicket.customer customer " +
				"LEFT JOIN pickTicket.carrier carrier " +
				"WHERE pickTicket.waveCode is null " +
				"AND pickTicket.status in ('ACTIVE','PREALLOCATE','SHIP_PART') " +
				"AND pickTicket.warehouse.id = :warehouseId " ;
		if(StringUtils.isNotEmpty(waveRule.getWaveRules().trim())){//高级设置
			hql += "AND "+waveRule.getWaveRules();
		}else{
			if (StringUtils.isNotEmpty(waveRule.getOrderStartTime().trim())) {// 订单时间开始时间
				if (waveRule.getDateType().equals(WaveRule.DAY_TYPE)) {
					hql += " AND pickTicket.orderDate>='"+sdf.format(baseDate)+" "+waveRule.getOrderStartTime()+"' ";
				}
				if (waveRule.getDateType().equals(WaveRule.WEEK_TYPE)) {
					String dayOfFirstWeek = sdf.format(DateUtils.getFirstDateOfWeek(baseDate));//周第一天
					hql += " AND pickTicket.orderDate>='"+dayOfFirstWeek+" "+waveRule.getOrderStartTime()+"' ";
				}
				if (waveRule.getDateType().equals(WaveRule.MONTH_TYPE)) {
					String dayOfFirstMonth = sdf.format(DateUtils.getFirstDateOfMonth(baseDate));//周第一天
					hql += " AND pickTicket.orderDate>='"+dayOfFirstMonth+" "+waveRule.getOrderStartTime()+"' ";
				}
			}// 订单时间结束时间
			if (StringUtils.isNotEmpty(waveRule.getOrderEndTime().trim())) {// 订单时间截止时间
				boolean isNextDate = false;//是否需要翻天
				if((waveRule.getOrderEndTime().trim()).compareTo(waveRule.getOrderStartTime().trim())<0){
					isNextDate = true;
				}
				if (waveRule.getDateType().equals(WaveRule.DAY_TYPE)) {
					if(isNextDate){//如果要翻天
						hql += " AND pickTicket.orderDate<='"+sdf.format(DateUtils.getNextDay(baseDate))+" "+waveRule.getOrderEndTime()+"' ";
					}else{
						hql += " AND pickTicket.orderDate<='"+sdf.format(baseDate)+" "+waveRule.getOrderEndTime()+"' ";
					}
				}
				if (waveRule.getDateType().equals(WaveRule.WEEK_TYPE)) {
					String dayOfLastWeek = "";
					if(isNextDate){//如果要翻天
						dayOfLastWeek = sdf.format(DateUtils.getNextDay(DateUtils.getLastDateOfWeek(baseDate)));//周最后一天
					}else{
						dayOfLastWeek = sdf.format(DateUtils.getLastDateOfWeek(baseDate));//周最后一天
					}
					hql += " AND pickTicket.orderDate<='"+dayOfLastWeek+" "+waveRule.getOrderEndTime()+"' ";
				}
				if (waveRule.getDateType().equals(WaveRule.MONTH_TYPE)) {
					String dayOfLastMonth = "";
					if(isNextDate){//如果要翻天
						dayOfLastMonth = sdf.format(DateUtils.getNextDay(DateUtils.getLastDateOfMonth(baseDate)));//月最后一天
					}else{
						dayOfLastMonth = sdf.format(DateUtils.getLastDateOfMonth(baseDate));//月最后一天
					}
					hql += " AND pickTicket.orderDate<='"+dayOfLastMonth+" "+waveRule.getOrderEndTime()+"' ";
				}
			}
			if(waveRule.getSortIndex()!=null){
				hql += " AND pickTicket.priority="+waveRule.getSortIndex();
			}
			if(StringUtils.isNotEmpty(waveRule.getShipmentMethod())){
				hql += " AND pickTicket.shipmentMethod='"+waveRule.getShipmentMethod()+"'";
			}
			if(StringUtils.isNotEmpty(waveRule.getCompanyGroupStr().trim())){
				hql += " AND pickTicket.company.id in (select rule.company.id from CompanyWorkRule rule " +
						" where rule.warehouse.id="+waveRule.getWarehouse().getId()+" and rule.groupStr='"+waveRule.getCompanyGroupStr()+"')";
			}
			if(StringUtils.isNotEmpty(waveRule.getCustomerGroupStr().trim())){
				hql += " AND pickTicket.customer.id in (select rule.customer.id from CustomerWorkRule rule " +
						" where rule.warehouse.id="+waveRule.getWarehouse().getId()+" and rule.groupStr='"+waveRule.getCustomerGroupStr()+"')";
			}
		}
		
		return hql;
	}
	

	public void savePrepareGoodsLoc(WaveDoc waveDoc) {
		WaveDoc dbWaveDoc = commonDao.load(WaveDoc.class, waveDoc.getId());
		//指定备货库位
		dbWaveDoc.setPrepareGoodsLoc(waveDoc.getPrepareGoodsLoc());
	}
	

	@SuppressWarnings("unchecked")
	public void activeWaveDoc(List<Long> waveDocIds) {
		List<WaveDoc> waveDocs = this.commonDao.loadAll(WaveDoc.class,waveDocIds.toArray(new Long[]{}));
		String errMsg = "";
		for(WaveDoc waveDoc:waveDocs){
			if(waveDoc.getPrepareGoodsLoc()==null){
				errMsg+=waveDoc.getCode()+" 激活失败;";
				continue;
			}
			List<WaveDoc> waveDocInPrepareGoodsLoc = this.commonDao.findByNamedQuery("getWaveDocByprepareGoodsLocationId",
							new String[] { "locationId" },
							new Object[] { waveDoc.getPrepareGoodsLoc().getId() });
			List<Inventory> inventorys = this.commonDao.findByNamedQuery("getInventoriesByLocationId", 
					new String[] { "warehouseId","locationId" }, 
					new Object[] {waveDoc.getWarehouse().getId(),waveDoc.getPrepareGoodsLoc().getId() });
			/**只要还有使用这个备货库位的波次没有结束,或者还有货物在该波次使用的备货库位上没有移走,则不能生效波次
			 * 如果waveDocInPrepareGoodsLoc不为空,则表示还有使用这个备货库位的波次没有结束
			 * 如果inventorys不为空,则表示还有货物在该波次使用的备货库位上没有移走
			 */
			if ((waveDocInPrepareGoodsLoc != null && waveDocInPrepareGoodsLoc.size() > 0)
					|| (inventorys != null && inventorys.size() > 0)) {
				errMsg+=waveDoc.getCode()+" 激活失败;";
				continue;
			}
			else
				errMsg+=waveDoc.getCode()+" 激活成功;";
			waveDoc.setStatus(WaveDocStatus.ACTIVE);
			waveDoc.setUserTracking();
		}
		LocalizedMessage.addLocalizedMessage(errMsg);
	}

	public void inactiveWaveDoc(List<Long> waveDocIds) {
		List<WaveDoc> waveDocs = this.commonDao.loadAll(WaveDoc.class,waveDocIds.toArray(new Long[]{}));
		for(WaveDoc waveDoc:waveDocs){
			waveDoc.setStatus(WaveDocStatus.OPEN);
			waveDoc.setUserTracking();
		}
	}

	@SuppressWarnings("unchecked")
	public void deleteWaveDoc(List<Long> waveDocIds) {
		List<WaveDoc> waveDocs = this.commonDao.loadAll(WaveDoc.class,waveDocIds.toArray(new Long[]{}));
		for(WaveDoc waveDoc:waveDocs){
			List<PickTicket> pickTickets = this.commonDao.findByNamedQuery("getPickTicketsByWaveDocId",
					new String[]{"waveDocId"},new Object[]{waveDoc.getId()});
			for(PickTicket pickTicket:pickTickets){
				pickTicket.setWaveCode(null);
				pickTicket.setWaveDoc(null);
			}
			this.commonDao.delete(waveDoc);
		}
	}

	public void addPickTicketsToWaveDoc(Long waveDocId, List<Long> pickTicketIds) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		List<PickTicket> pickTickets = this.commonDao.loadAll(PickTicket.class,pickTicketIds.toArray(new Long[]{}));
		if(pickTickets==null||pickTickets.size()==0){
			return;
		}List<PickTicket> notPreAllocs = new ArrayList<PickTicket>();
		for(PickTicket pickTicket:pickTickets){
			if(!pickTicketManager.preAllocate(pickTicket.getId())){
				notPreAllocs.add(pickTicket);
			}
		}
		if(notPreAllocs.size()>0){
			pickTickets.removeAll(notPreAllocs);
		}
		if(WorkModel.PickTicketModel.equals(waveDoc.getWorkModel())){
			waveDoc.addPickTicketsByPickTicketModel(pickTickets);
		}else{
			waveDoc.addPickTicketsByPickBatchModel(pickTickets);
		}
		waveDoc.setUserTracking();
	}

	public void deletePickTicketsFromWaveDoc(Long waveDocId, List<Long> pickTicketIds) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		List<PickTicket> pickTickets = this.commonDao.loadAll(PickTicket.class,pickTicketIds.toArray(new Long[]{}));
		if(pickTickets==null||pickTickets.size()==0){
			return;
		}
		List<WaveDocDetail> waveDocDetails;
		if(WorkModel.PickTicketModel.equals(waveDoc.getWorkModel())){
			waveDocDetails = waveDoc.deletePickTicketsByPickTicketModel(pickTickets);
		}else{
			waveDocDetails = waveDoc.deletePickTicketsByPickBatchModel(pickTickets);
		}
		for(WaveDocDetail waveDocDetail:waveDocDetails){
			waveDoc.getWaveDocDetails().remove(waveDocDetail);
			this.commonDao.delete(waveDocDetail);
		}
		if(waveDoc.getOrderQuantity()==0){
			waveDoc.setWeight(.0);
			waveDoc.setVolumn(.0);
		}
		waveDoc.setUserTracking();
	}
	
	//整单分配 zhuhejing
	@SuppressWarnings("unchecked")	
	public Map auto_WD_Allocate(Map params) {
		Long waveDocId = (Long) params.get("waveDocId");
		List<Long> waveDocDetailIds = (List<Long>) params.get("parentIds");
		
		Map result = new HashMap();
		result.put("waveDocId", waveDocId);
		
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class, waveDocId);
		if (WaveDocStatus.ACTIVE.equals(waveDoc.getStatus()) 
				|| WaveDocStatus.ALLOCATE.equals(waveDoc.getStatus())) {
			List<AbstractMoveDocDetail> details = new ArrayList<AbstractMoveDocDetail>();
			if (waveDocDetailIds == null || waveDocDetailIds.size() == 0) {
				if (waveDoc.getWaveDocDetails() == null || waveDoc.getWaveDocDetails().size() == 0) {
					throw new BusinessException("waveDoc.details.none");
				}
				details.addAll(waveDoc.getAbstractMoveDocDetails());
			} else {
				List<WaveDocDetail> waveDocDetails = this.commonDao.loadAll(WaveDocDetail.class, waveDocDetailIds.toArray(new Long[] {}));
				for(WaveDocDetail detail :waveDocDetails){
					details.add(detail);
				}
			}
			autoAllocator.allocate(null, null, null, details);
		}
		return result;
	}
	
	//部分分配 zhuhejing	
	@SuppressWarnings("unchecked")
	public Map manual_WD_Allocate(Map clientParams) {
		Long waveDocDetailId = (Long) clientParams.get("waveDocDetailId");
		Map inventories = (Map)clientParams.get(IPage.TABLE_INPUT_VALUES);
		Map result = new HashMap();

		result.put("waveDocDetailId", waveDocDetailId);		
		WaveDocDetail waveDocDetail = commonDao.load(WaveDocDetail.class, waveDocDetailId);
		autoAllocator.manualAllocate(waveDocDetail, inventories);
		return result;
	}
	
	public void unallocate(List<Long> waveDocIds) {
		for (Long id : waveDocIds) {
			WaveDoc waveDoc = commonDao.load(WaveDoc.class, id);
			this.deleteCageInfo(waveDoc.getId());
			autoAllocator.unallocate(waveDoc);
		}
	}

	//取消分配 zhuhejing
	@SuppressWarnings("unchecked")
	public Map cancell_WD_Allocate(Map clientParams) {
		String barCode=null;
		Long waveDocId = (Long) clientParams.get("waveDocId");
		Map tasks = (Map)clientParams.get(IPage.TABLE_INPUT_VALUES);
		Map result = new HashMap();
		result.put("waveDocId", waveDocId);
		for (Object obj : tasks.keySet()) {
			Task task = commonDao.load(Task.class, (Long) obj);
			String cValue = String.valueOf(tasks.get(obj));
			double quantity = Double.valueOf(cValue); 
			quantity = quantity*task.getPackageUnit().getConvertFigure();
			if (task.getPackageUnit().getPrecision() == 0) {
				quantity = Math.floor(quantity);
			} else {
				quantity = DoubleUtils.formatByPrecision(quantity, task.getPackageUnit().getPrecision());
			}

			task.unallocateWaveDoc(quantity);

			ItemKeyInventory itemKeyInventory = inventoryManager.getItemKeyInventory(
					task.getSrcLoc().getWarehouse(), task.getItemKey());
			Inventory inventory = itemKeyInventory.getInventoryWithException(task.getSrcLoc(), 
					task.getPackageUnit(), task.getSrcLP(), barCode,task.getInventoryStatus(), 
					task.getStorageDate(), task.getSOI());
			inventory.unallocate(quantity, null, null);
			
			if (task.getPlanQuantityMU() <= 0) {
				commonDao.delete(task);
			}
		}
		return result;
	}
	
	/**
	 * 波次整单拣货
	 */
	@SuppressWarnings("unchecked")
	public void pickConfirmForPickAll(Long waveDocId,Long workerId) {
		String hql = "select task from Task task where task.waveDocDetail.waveDoc.id ="
				+ waveDocId + " and (task.planQuantityMU -task.movedQuantityMU)>0 ";
		List<Task> tasks = commonDao.findByQuery(hql);
		for (Task task : tasks) {
			double qty = task.getPlanQuantityMU() - task.getMovedQuantityMU();
			if (qty > 0) {
				pickConfirm(task.getId(), qty, workerId);
			}
		}
	}
	
	/**
	 * 对波次任务进行逐一拣货确认
	 */
	public void pickConfirm(Long taskId, double pickedQuantity, Long workerId) {
		Task task = commonDao.load(Task.class,taskId);
		if((task.getPlanQuantityMU()-task.getMovedQuantityMU()) < pickedQuantity){
			throw new BusinessException("pick.quantity.error");
		}
		String barCode=null;
		//task.pickConfirm(task.getPackageUnit(), pickedQuantity, w);
		//库存移位处理
		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());
		if (srcInv != null) {
			//对源库存取消分配
	        srcInv.unallocate(pickedQuantity, null, null);
			//移位确认(捡货确认是将货物捡到月台上面,没有托盘)
			inventoryManager.moveInventory(srcInv.getId(), task.getDstLoc().getId(), null, 
					pickedQuantity, task.getPackageUnit().getId(), task.getId(), workerId);
		} else {
			throw new BusinessException("move.inventory.none");
		}
		//afterPickConfirm(task);
	}
	
	public void afterPickConfirm(List<Long> ids) {
		for(Long id:ids ){
			WaveDoc doc=this.commonDao.get(WaveDoc.class, id);
			if(doc.getPickedQuantity()>=doc.getOrderQuantity()){
				List<PickTicket> pts=commonDao.findByQuery("from PickTicket pt where pt.waveDoc.id=:waveDocId",
						new String[]{"waveDocId"},new Object[]{id});
				for (PickTicket pt : pts) {
					String errorMsg = autoAllocator.allocate(pt.getId(), null, null, null);
					//pickTicketManager.pickConfirm(pt.getId(), null);
				}
				doc.setStatus(WaveDocStatus.CLOSE);
				this.commonDao.store(doc);
			}else{
				throw new BusinessException("未全部补货完成");
			}
		}
	}

	/**
	 * 单一任务拣货确认
	 */
	public void pickConfirm(Long taskId, Long srcLocId, String lp, String lot, Long packageUnitId,
			double pickedQuantity, Long workerId ) {
		Task task = commonDao.load(Task.class,taskId);
		PackageUnit packageUnit =  commonDao.load(PackageUnit.class,packageUnitId);
		Location srcLocation = commonDao.load(Location.class, srcLocId);
		pickedQuantity = pickedQuantity * packageUnit.getConvertFigure();
		String barCode=null;
		Stock srcLp = task.getSrcLP();	//源托盘
		Stock dstLp = task.getDstLP();	//目标托盘号
		ItemKey itemKey = task.getItemKey();
		//是否修改了秕次
		if(StringUtils.isNotEmpty(lot) && !lot.equals(task.getItemKey().getLot())){
			itemKey = (ItemKey) commonDao.findByNamedQueryUniqueResult("getItemKeyByLot", 
					new String[] {"lot"}, new Object[] {lot});
			if(itemKey==null){
				throw new BusinessException("input.lot.error");
			}
		}
		
		if((task.getPlanQuantityMU()-task.getMovedQuantityMU()) < pickedQuantity){
			throw new BusinessException("pick.quantity.error");
		}

		//对源库存取消分配
		ItemKeyInventory srcItemKeyInventory = inventoryManager.getItemKeyInventory(
				task.getSrcLoc().getWarehouse(), task.getItemKey());	
		Inventory srcInv = srcItemKeyInventory.getInventory(task.getSrcLoc(), task.getPackageUnit(), 
				task.getSrcLP(), barCode,task.getInventoryStatus(),task.getStorageDate(),task.getSOI());
		if (srcInv == null) {
			throw new BusinessException("move.inventory.none");
		}
        srcInv.unallocate(pickedQuantity, null, null);
        
        if (srcLocation.equals(task.getSrcLoc())
				&& ((srcLp==null&&task.getSrcLP()==null)||srcLp.equals(task.getSrcLP()))
				&& itemKey.equals(task.getItemKey())
				&& packageUnit.equals(task.getPackageUnit())) {
        	inventoryManager.moveInventory(srcInv.getId(), task.getDstLoc().getId(), 
        			dstLp == null ? null : dstLp.getId(), pickedQuantity, task.getPackageUnit().getId(), task.getId(), workerId);
        } else {
            //扣减实际拣货库位库存
            ItemKeyInventory actItemKeyInventory = inventoryManager.getItemKeyInventory(
            		srcLocation.getWarehouse(), itemKey);
            List<Inventory> invs = actItemKeyInventory.getInventories(srcLocation, srcLp, 
            		task.getStorageDate(), task.getSOI(), task.getInventoryStatus());
            double moveQty = 0;
            for (Inventory inv : invs) {
            	if (pickedQuantity > inv.getAvaliableQuantity()) {
                	moveQty = inv.getAvaliableQuantity();
            	} else {
            		moveQty = pickedQuantity;
            	}
            	inventoryManager.moveInventory(inv.getId(), task.getDstLoc().getId(), 
            			dstLp == null ? null : dstLp.getId(), moveQty, packageUnit.getId(), task.getId(), workerId);
            	pickedQuantity -= moveQty;
            	if (pickedQuantity <= 0) {
            		break;
            	}
            }
            
    		if (pickedQuantity > 0) {
    			throw new BusinessException("move.inventory.none");
    		}
        }
		//afterPickConfirm(task);
	}
	
	/**
	 * 拣货确认后调度方法。当波次拣货确认完成自动调用自动分单，自动分单结束如果所有拣货单完全分单后关闭波次
	 * @param task
	 */
	@SuppressWarnings("unchecked")
	private void  afterPickConfirm(Task task){
		//拣货完成后自动分单
		WaveDoc waveDoc = task.getWaveDocDetail().getWaveDoc();
		if(!WaveDocStatus.PICK_ALL.equals(waveDoc.getStatus())){
			return;
		}
		//调用自动分单方法
		autoDisForWave(waveDoc.getId());
		//判断波次作业单的全部发货单是否完全分单成功，确定是否关闭该波次作业单
		List<PickTicket> pickTickets = this.commonDao.findByNamedQuery(
				"getPickTicketsByWaveDocId", new String[] { "waveDocId" },
				new Object[] { waveDoc.getId() });
		for(PickTicket pickTicket:pickTickets){
			if(pickTicket.getOrderQuantity() - pickTicket.getAllocatedQuantity() > 0){
				return;
			}
		}
		List<Long> waveDocIds = new ArrayList<Long>();
		waveDocIds.add(waveDoc.getId());
		closeWaveDocsByIds(waveDocIds);
	}
	
	@SuppressWarnings("unchecked")
	public void closeWaveDocsByIds(List<Long> waveDocIds){
		String barCode=null;
		List<WaveDoc> waveDocs = this.commonDao.loadAll(WaveDoc.class,waveDocIds.toArray(new Long[]{}));
		for(WaveDoc waveDoc:waveDocs){
			if (!(WaveDocStatus.SPLIT.equals(waveDoc.getStatus()))
					&& !(WaveDocStatus.SPLIT_ALL.equals(waveDoc.getStatus()))) {
				continue;
			}
			List<PickTicket> pickTickets = this.commonDao.findByNamedQuery(
					"getPickTicketsByWaveDocId", new String[] { "waveDocId" },
					new Object[] { waveDoc.getId() });
			List<Long> pickTicketIds = new ArrayList<Long>();
			for(PickTicket pickTicket: pickTickets){
				pickTicket.setWaveCode(null);
				if((pickTicket.getAllocatedQuantity()-pickTicket.getPickedQuantity())>0){
					pickTicketIds.add(pickTicket.getId());
					moveConfirm(pickTicket.getId());
				}
			}
			//创建BOL
			if(pickTicketIds.size()>0){
				//this.pickTicketManager.createBOL(pickTicketIds);
			}
			
			//对备货库位未分单的货物处理
			if(waveDoc.getPickedQuantity()>waveDoc.getBillAllocatedQuantity()){
				List<Inventory> inventorys = this.commonDao.findByNamedQuery("getInventoriesByLocationId", 
						new String[] { "warehouseId","locationId" }, 
						new Object[] {waveDoc.getWarehouse().getId(),waveDoc.getPrepareGoodsLoc().getId() });
				if(inventorys==null&&inventorys.size()==0){
					continue;
				}
				if (waveDoc.getPrepareGoodsLoc().getPickBackLoc() != null) {
					// 库存移位处理
					for (Inventory inv : inventorys) {
						inventoryManager.moveInventory(inv.getId(), 
								waveDoc.getPrepareGoodsLoc().getPickBackLoc().getId(),
								inv.getLP() == null ? null : inv.getLP().getId(), 
								inv.getQtyOfMasterPack(), inv.getPackageUnit().getId(), null, null);
					}
				} else {
					throw new BusinessException("waveDoc.pickBack.location.none");
				}
			}
			//对已分配但未全部拣货波次作业Task的处理
			List<Task> tasks = this.commonDao.findByNamedQuery("getNotAllPickTasksByWaveDocId",
					new String[] { "waveDocId" }, new Object[] { waveDoc.getId() });
			List<Task> ts = new ArrayList<Task>();
			for (Task task : tasks) {
				double qty = task.getPlanQuantityMU() - task.getMovedQuantityMU();
				if (qty > 0) {
					task.unallocateWaveDoc(qty);
					ItemKeyInventory itemKeyInventory = inventoryManager.getItemKeyInventory(
							task.getSrcLoc().getWarehouse(), task.getItemKey());
					Inventory inventory = itemKeyInventory.getInventoryWithException(task.getSrcLoc(), 
							task.getPackageUnit(), task.getSrcLP(), barCode,task.getInventoryStatus(), 
							task.getStorageDate(), task.getSOI());
					inventory.unallocate(qty, null, null);
				}
				if (task.getPlanQuantityMU()== 0) {
					ts.add(task);
				}
			}
			for(Task task:ts){
				task.getWaveDocDetail().getTasks().remove(task);
			}
			//关闭波次
			waveDoc.close();
		}
	}
	
	/**
	 * 将备货库位已分单的货物移到发货库位
	 * @param pickTicketId
	 */
	@SuppressWarnings("unchecked")
	private void moveConfirm(Long pickTicketId){
		String barCode=null;
		List<Task> tasks = this.commonDao.findByNamedQuery("getTasksByPickTicketId",new String[]{"pickTicketId"},new Object[]{pickTicketId});
		for(Task task:tasks){
			Double pickedQuantity = task.getPlanQuantityMU() - task.getMovedQuantityMU();
			//task.pickConfirm(task.getPackageUnit(), pickedQuantity, worker);
			//库存移位处理
			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());
			if (srcInv != null) {
				//对源库存取消分配
		        srcInv.unallocate(pickedQuantity, null, null);
				//移位确认(捡货确认是将货物捡到月台上面,没有托盘)
		        //移动货物到发货月台时，要根据发货单明细上的货品包装要求重新包装
				inventoryManager.moveInventory(srcInv.getId(), task.getDstLoc().getId(), 
						null, pickedQuantity, task.getPickTicketDetail().getPackageUnit().getId(), 
						task.getId(), null);
			} else {
				throw new BusinessException("move.inventory.none");
			}
		}
	}
	
	/**
	 * 将库库列表按货主映射
	 * @param inventories
	 * @return
	 */
	private Map<Long,List<Inventory>> getInventoryCompanyMap(List<Inventory> inventories){
		Map<Long,List<Inventory>> map = new HashMap<Long,List<Inventory>>();
		for(Inventory inventory:inventories){
			Long CompanyId = inventory.getItemKeyInventory().getItemKey().getItem().getCompany().getId();
			List<Inventory> tempInventories = map.get(CompanyId);
			if(tempInventories==null){
				tempInventories = new ArrayList<Inventory>();
				tempInventories.add(inventory);
				map.put(CompanyId,tempInventories);
			}else{
				tempInventories.add(inventory);
			}
		}
		return map;
	}
	
	/**
	 * 自动按照波次分单
	 */
	public void autoDisForWave(Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		if(waveDoc.getStatus().equals(WaveDocStatus.SPLIT_ALL))
			return;
		
		splitForWave(waveDoc,false);
	}
	
	/**
	 * 判断taskLog和PickTicketDetail是否匹配
	 * @param log
	 * @param detail
	 * @param isBatch:是否按单捡货
	 * @param isMustSplit:是否强制分单
	 * @return
	 */
	private boolean isPeak(Inventory inventory,PickTicketDetail detail,boolean isMustSplit){
		//如果是强制分单,则不需要批次属性匹配,只要产品匹配就行
		ItemKey itemKey = inventory.getItemKeyInventory().getItemKey();
		
		//发货单未指定收货人，或者收货人未指定作业规则时，默认规则为：严格批次属性发货
		boolean beAllowed2ChangePickLot = false;
		if(detail.getPickTicket().getCustomer()==null){
			beAllowed2ChangePickLot = true;
		}else{
			beAllowed2ChangePickLot = detail.getPickTicket().getCustomer().isBeAllowed2ChangePickLot();
		}
		if(!beAllowed2ChangePickLot && isMustSplit && itemKey.getItem().getId()==detail.getItem().getId()){
			return true;
		}
		
		NewLotInfo newLotInfo = detail.getNewLotInfo() == null ? new NewLotInfo() : detail.getNewLotInfo();
		if(itemKey.getItem().getId()==detail.getItem().getId() 
				&& (newLotInfo.getPropC1()==null || 
						(newLotInfo.getPropC1()!=null && NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC1(),newLotInfo.getPropC1())))
				&& (newLotInfo.getPropC2()==null || 
						(newLotInfo.getPropC2()!=null && NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC2(),newLotInfo.getPropC2())))
				&& (newLotInfo.getPropC3()==null || 
						(newLotInfo.getPropC3()!=null &&  NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC3(),newLotInfo.getPropC3())))
				&& (newLotInfo.getPropC5()==null || 
						(newLotInfo.getPropC5()!=null && NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC5(),newLotInfo.getPropC5())))
				&& (newLotInfo.getPropC6()==null || 
						(newLotInfo.getPropC6()!=null && NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC6(),newLotInfo.getPropC6())))
				&& (newLotInfo.getPropC7()==null || 
						(newLotInfo.getPropC7()!=null &&  NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC7(),newLotInfo.getPropC7())))
				&& (newLotInfo.getPropC8()==null || 
						(newLotInfo.getPropC8()!=null &&  NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC8(),newLotInfo.getPropC8())))
				&& (newLotInfo.getPropC9()==null || 
						(newLotInfo.getPropC9()!=null &&  NewLotInfoParser.compareString(itemKey.getLotInfo().getPropC9(),newLotInfo.getPropC9())))
				&& (newLotInfo.getPropN1()==null || 
						(newLotInfo.getPropN1()!=null && NewLotInfoParser.compareNum(itemKey.getLotInfo().getPropN1(), newLotInfo.getPropN1())))
				&& (newLotInfo.getPropN2()==null || 
						(newLotInfo.getPropN2()!=null && NewLotInfoParser.compareNum(itemKey.getLotInfo().getPropN2(), newLotInfo.getPropN2())))
				&& (newLotInfo.getPropN3()==null || 
						(newLotInfo.getPropN3()!=null &&  NewLotInfoParser.compareNum(itemKey.getLotInfo().getPropN3(), newLotInfo.getPropN3())))
		        && (newLotInfo.getPropD1()==null || 
						(newLotInfo.getPropD1()!=null &&  NewLotInfoParser.compareDate(itemKey.getLotInfo().getPropD1(),newLotInfo.getPropD1())))
				&& (newLotInfo.getPropD2()==null || 
						(newLotInfo.getPropD2()!=null &&  NewLotInfoParser.compareDate(itemKey.getLotInfo().getPropD2(),newLotInfo.getPropD2())))
				&& (newLotInfo.getPropD3()==null || 
						(newLotInfo.getPropD3()!=null &&  NewLotInfoParser.compareDate(itemKey.getLotInfo().getPropD3(),newLotInfo.getPropD3())))
				&& (newLotInfo.getPropL1()==null || 
						(newLotInfo.getPropL1()!=null && NewLotInfoParser.compareLongString(itemKey.getLotInfo().getPropLC1(),newLotInfo.getPropL1())))
				&& (newLotInfo.getPropL2()==null || 
						(newLotInfo.getPropL2()!=null && NewLotInfoParser.compareLongString(itemKey.getLotInfo().getPropLC2(),newLotInfo.getPropL2())))
				&& (newLotInfo.getPropL3()==null || 
						(newLotInfo.getPropL3()!=null && NewLotInfoParser.compareLongString(itemKey.getLotInfo().getPropLC3(),newLotInfo.getPropL3())))
				){
			return true;
		}
		return false;
	}
	
	/**
	 * 总的分单过程(用产品的批次属性匹配分单)
	 * @param pickTickets 波次对应的发货单
	 * @param taskLogs 波次对应的taskLog
	 * @param workModel 波次作业模式
	 */
	private double splitWaveDocByLotInfo(WaveDoc waveDoc,List<PickTicket> pickTickets,List<Inventory> inventorys,boolean isMustSplit){
		double splitQty = .0;// 分单数量
		for (PickTicket pickTicket : pickTickets) {
			if (pickTicket.getStatus().equals(PickTicketStatus.PICK_ALL)) {
				continue;
			}
			boolean shipPart = true;
			if(pickTicket.getCustomer()==null){
				shipPart = true;
			}else{
				shipPart = pickTicket.getCustomer().isShipPart();
			}
			if (shipPart) {// 客户或货主允许自动发货
				for (PickTicketDetail detail : pickTicket.getPickTicketDetails()) {
					if (detail.getPreAllocatedQuantity() == detail.getAllocatedQuantity()) {
						continue;
					}
					for (Inventory inventory : inventorys) {
						if (inventory.getAvaliableQuantity() == 0) {
							continue;
						}
						if (isPeak(inventory, detail,isMustSplit)) {
							double tempQty = 0.0;
							tempQty = detail.split(inventory);
							splitQty += tempQty;
							waveDoc.split(detail, tempQty);
							if (detail.getAllocatedQuantity() == detail.getPreAllocatedQuantity()) {
								break;
							}
						}
					}
				}
			} else {// 不允许部份发货
				double partQty = 0.0;
				List<Inventory> tempInventories = new ArrayList<Inventory>();
				for (PickTicketDetail detail : pickTicket.getPickTicketDetails()) {
					if (detail.getPreAllocatedQuantity() == detail.getAllocatedQuantity()) {
						continue;
					}
					for (Inventory inventory : inventorys) {
						// 只有备货库位中包装为基本包装时才能够分单成功
						if (inventory.getAvaliableQuantity() == 0 || inventory.getPackageUnit().getConvertFigure() != 1) {
							continue;
						}
						if (isPeak(inventory, detail,isMustSplit)) {
							partQty += (detail.getPreAllocatedQuantity() - detail.getAllocatedQuantity()) <= inventory.getAvaliableQuantity() 
								? (detail.getPreAllocatedQuantity() - detail.getAllocatedQuantity()) : inventory.getAvaliableQuantity();
							tempInventories.add(inventory);
							if (partQty == detail.getOrderQtyOfMasterUnit()) {
								break;
							}
						}
					}
				}
				if (partQty < pickTicket.getPreAllocatedQuantity()) {
					continue;
				}
				for (PickTicketDetail detail : pickTicket.getPickTicketDetails()) {
					if (detail.getPreAllocatedQuantity() == detail.getAllocatedQuantity()) {
						continue;
					}
					for (Inventory inventory : tempInventories) {
						if (inventory.getAvaliableQuantity() == 0) {
							continue;
						}
						if (isPeak(inventory, detail,isMustSplit)) {
							double tempQty = 0.0;
							tempQty = detail.split(inventory);
							splitQty += tempQty;
							waveDoc.split(detail, tempQty);
							if (detail.getAllocatedQuantity() == detail.getPreAllocatedQuantity()) {
								break;
							}
						}
					}
				}
			}
		}
		return splitQty;
	}
	
	/**
	 * 批拣分单
	 * @param waveDoc
	 * @param isMustSplit:是否强制分单
	 */
	@SuppressWarnings("unchecked")
	public void splitForWave(WaveDoc waveDoc,boolean isMustSplit){
		List<Inventory> inventorys = this.commonDao.findByNamedQuery("getInventoriesByLocationId", 
				new String[] { "warehouseId","locationId" }, 
				new Object[] {waveDoc.getWarehouse().getId(),waveDoc.getPrepareGoodsLoc().getId() });
		List<PickTicket> pickTickets = this.commonDao.findByNamedQuery("getPickTicketsByWaveDocId",
				new String[]{"waveDocId"},new Object[]{waveDoc.getId()});
		splitWaveDocByLotInfo(waveDoc,pickTickets,inventorys,isMustSplit);
	}
	
	/**
	 * 批拣分单
	 * @param waveDoc
	 * @param pickTickets:选择的捡货单
	 * @param isMustSplit:是否强制分单
	 */
	@SuppressWarnings("unchecked")
	public void splitForWave(WaveDoc waveDoc,List<PickTicket> pickTickets,boolean isMustSplit){
		List<Inventory> inventorys = this.commonDao.findByNamedQuery("getInventoriesByLocationId", 
				new String[] { "warehouseId","locationId" }, 
				new Object[] {waveDoc.getWarehouse().getId(),waveDoc.getPrepareGoodsLoc().getId() });
		splitWaveDocByLotInfo(waveDoc,pickTickets,inventorys,isMustSplit);
	}
	
	/**
	 * 自动批量分单
	 */
	public void autoDisForPickticket(List<Long> pickTicketIds,Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		List<PickTicket> pickTickets = new ArrayList<PickTicket>();
		for(Long id:pickTicketIds){
			pickTickets.add(this.commonDao.load(PickTicket.class,id));
		}
		splitForWave(waveDoc,pickTickets,true);
	}	
	
	/**
	 * 强制波次分单
	 */
	public void mustDisForWave(Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		if(waveDoc.getStatus().equals(WaveDocStatus.SPLIT_ALL))
			return;
		splitForWave(waveDoc,true);
	}
	
	/**
	 * 强制批量分单
	 */
	public void mustDisForPickticket(List<Long> pickTicketIds, Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		List<PickTicket> pickTickets = new ArrayList<PickTicket>();
		for(Long id:pickTicketIds){
			pickTickets.add(this.commonDao.load(PickTicket.class,id));
		}
		
		this.splitForWave(waveDoc,pickTickets,true);
		
	}
	////////////////////////////////////////////////////////////////////////分单
	/**
	 * 取消分单
	 */
	@SuppressWarnings("unchecked")
	public void cancelSplitWaveDoc(Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class, waveDocId);
		List<PickTicket> pickTickets = this.commonDao.findByNamedQuery(
				"getPickTicketsByWaveDocId", new String[] { "waveDocId" },
				new Object[] { waveDocId });
		cancelSplitFun(waveDoc,pickTickets);
	}

	public void cancelSplitForPickticket(List<Long> ids, Long waveDocId) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class, waveDocId);
		List<PickTicket> pickTickets = this.commonDao.loadAll(PickTicket.class,ids.toArray(new Long[]{}));
		
		cancelSplitFun(waveDoc,pickTickets);
	}
	
	private void cancelSplitFun(WaveDoc waveDoc,List<PickTicket> pickTickets){
		double allQty = .0;
		String barCode=null;
		for (PickTicket pk : pickTickets) {
			if(!PickTicketStatus.ALLOCATE.equals(pk.getStatus())){
				continue;
			}
			double allBolQty = .0;
			for (PickTicketDetail detail : pk.getPickTicketDetails()) {
				double bolQty = .0;
				for (Task task : detail.getTasks()) {
					bolQty+=task.getMovedQuantityMU();
					allQty+=task.getPlanQuantityMU()-task.getMovedQuantityMU();
					if (allQty > 0) {
						ItemKeyInventory itemKeyInventory = inventoryManager.getItemKeyInventory(
								task.getSrcLoc().getWarehouse(), task.getItemKey());
						Inventory inventory = itemKeyInventory.getInventoryWithException(task.getSrcLoc(), 
								task.getPackageUnit(), task.getSrcLP(), barCode,task.getInventoryStatus(), 
								task.getStorageDate(), task.getSOI());
						inventory.unallocate(task.getPlanQuantityMU()-task.getMovedQuantityMU(), null, null);
					}
				}
				allBolQty+=bolQty;
				detail.setAllocatedQuantity(bolQty);
				detail.setPickedQuantity(bolQty);
				detail.getTasks().clear();
			}
			pk.setAllocatedQuantity(allBolQty);
			pk.setPickedQuantity(allBolQty);
			pk.setStatus(PickTicketStatus.PREALLOCATE);
		}
		waveDoc.cancelSplit(allQty);
	}

//查询波次信息    
    @SuppressWarnings("unchecked")
	public Map init_WD_INFO(Map params) {
		Long waveDocId = (Long)params.get("waveDocId");
		
		final String hql = "SELECT new com.dev.swms.client.ui.javabean.PT_INFO(waveDoc.id,waveDoc.code,waveDoc.orderQuantity," +
				"0.0,waveDoc.allocatedQuantity,waveDoc.pickedQuantity ) "+
		"From WaveDoc waveDoc " +
		"WHERE waveDoc.id =:waveDocId";
		PT_INFO pt_info = (PT_INFO) commonDao.findByQueryUniqueResult(hql,new String[]{"waveDocId" } , new Object[]{waveDocId});

		
		Map<String ,PT_INFO> result = new HashMap<String,PT_INFO>();
		result.put(CT_PA.PT_RESULT, pt_info);
		return result;

	}
//查询波次明细信息 	
	@SuppressWarnings("unchecked")	
	public Map init_WDD_INFO(Map params) {
		Long waveDocId = (Long)params.get("waveDocId");
		
		final String hql = "SELECT new com.dev.swms.client.ui.javabean.PT_DETAILS(w.id,"+
	  	" w.item.id,"+
	  	" w.packageUnit.id,"+
	  	" w.packageUnit.convertFigure,"+
	  	" w.item.code,"+
	  	" w.item.name,"+
	  	" w.packageUnit.unit," +
	    " w.orderQuantity,"+ 
	    " w.orderQtyOfMasterUnit,"+
	    " 0.0,"+
	    " w.allocatedQuantity,"+
	    " w.pickedQuantity,"+
	    " 0.0," +
	    " w.newLotInfo, "+
	    " w.newLotInfo.propC1,"+
	    " w.newLotInfo.propC2," +
	    " w.newLotInfo.propC3," +
	    " w.newLotInfo.propC4," +
	    " w.newLotInfo.propC5,"+
	    " w.newLotInfo.propC6," +
	    " w.newLotInfo.propC7," +
	    " w.newLotInfo.propC8," +
	    " w.newLotInfo.propC9,"+
	    
	    " w.newLotInfo.propN1, " +
	    " w.newLotInfo.propN2, " +
	    " w.newLotInfo.propN3, " +
	    
	    " w.newLotInfo.propD1," +
	    " w.newLotInfo.propD2," +
	    " w.newLotInfo.propD3," +
	    
	    " w.newLotInfo.propL1," +
	    " w.newLotInfo.propL2," +
	    " w.newLotInfo.propL3)" +
	    
	    " From WaveDocDetail w "+
	    " where w.waveDoc.id = :waveDocId and w.orderQuantity>w.pickedQuantity" +
	    " and w.waveDoc.warehouse.id = :warehouseId";
		List<PT_DETAILS> pt_details = commonDao.findByQuery(hql,new String[]{"waveDocId","warehouseId" },new Object[]{waveDocId, WarehouseHolder.getWarehouse().getId()});

		
		Map<String ,List> result = new HashMap<String,List>();
		result.put(CT_PA.PD_RESULT, pt_details);
		return result;

	}

//查询波次明细对应的库存信息	
	@SuppressWarnings("unchecked")	
	public Map init_WDAL_INFO(Map params) {
		Long itemId = (Long)params.get("itemId");
		Integer convertFigure = (Integer)params.get("convertFigure");
		String propC1 = (String)params.get("propC1");
		String propC2 = (String)params.get("propC2");
		String propC3 = (String)params.get("propC3");
		String propC4 = (String)params.get("propC4");
		String propC5 = (String)params.get("propC5");
		String propC6 = (String)params.get("propC6");
		String propC7 = (String)params.get("propC7");
		String propC8 = (String)params.get("propC8");
		String propC9 = (String)params.get("propC9");
		String propN1 = (String)params.get("propN1");
		String propN2 = (String)params.get("propN2");
		String propN3 = (String)params.get("propN3");
		String propD1 = (String)params.get("propD1");
		String propD2 = (String)params.get("propD2");
		String propD3 = (String)params.get("propD3");
		String propL1 = (String)params.get("propL1");
		String propL2 = (String)params.get("propL2");
		String propL3 = (String)params.get("propL3");
		
		StringBuffer hqlb = new StringBuffer();
		hqlb.append(" select new com.dev.swms.client.ui.javabean.PT_AVAILABLE(inventory.id,inventory.location.code,inventory.itemKeyInventory.itemKey.item.code, ")
		.append("inventory.itemKeyInventory.itemKey.item.name,inventory.packageUnit.unit,")
		.append("inventory.packageUnit.convertFigure,")
		.append("inventory.quantity,")
		.append("inventory.qtyOfMasterPack-inventory.queuedQuantity,0,")
		.append("inventory.itemKeyInventory.itemKey.lot,inventory.itemKeyInventory.itemKey.lotInfo,")
		.append("inventory.storageDate,inventory.SOI)")
		.append(" from Inventory inventory,Item item")
		.append(" where inventory.itemKeyInventory.itemKey.item = item")
		.append(" and item.id = :itemId")
		.append(" and inventory.packageUnit.convertFigure >= :convertFigure")
		.append(" and (inventory.qtyOfMasterPack - inventory.queuedQuantity)>0")
		.append(" and inventory.location.warehouse.id = :warehouseId")
		//.append(" and inventory.location.type in ('STORAGE')")
		.append(" and inventory.location.type in ('STORAGE','BAD','READY','TRANSIT')")
		.append(" and (inventory.location.countLock = '' or inventory.location.countLock is null) ")
		.append(" and (inventory.location.inLock = '' or inventory.location.inLock is null) ")
		.append(" and (inventory.location.outLock = '' or inventory.location.outLock is null) ")
		
		.append(" AND ( 1 = 1 ")
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC1",propC1))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC2",propC2))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC3",propC3))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC4",propC4))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC5",propC5))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC6",propC6))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC7",propC7))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC8",propC8))
		.append(" AND ").append(NewLotInfoParser.decryptStringOfLot("propC9",propC9))
		
		.append(" AND ").append(NewLotInfoParser.decryptDoubleOfLot("propN1",propN1))
		.append(" AND ").append(NewLotInfoParser.decryptDoubleOfLot("propN2",propN2))
		.append(" AND ").append(NewLotInfoParser.decryptDoubleOfLot("propN3",propN3))
		.append(" AND ").append(NewLotInfoParser.decryptDateOfLot("propD1",propD1))
		.append(" AND ").append(NewLotInfoParser.decryptDateOfLot("propD2",propD2))
		.append(" AND ").append(NewLotInfoParser.decryptDateOfLot("propD3",propD3))
		.append(" AND ").append(NewLotInfoParser.decryptLongStringOfLot("propLC1",propL1)) 
		.append(" AND ").append(NewLotInfoParser.decryptLongStringOfLot("propLC2",propL2))
		.append(" AND ").append(NewLotInfoParser.decryptLongStringOfLot("propLC3",propL3))		
		.append(" )");          
		final Map propMap = new HashMap();
		final String hql = hqlb.toString();

		List<PT_AVAILABLE> pt_available = commonDao.findByQuery(hql,new String[]{"itemId","convertFigure","warehouseId"},new Object[]{itemId,convertFigure, WarehouseHolder.getWarehouse().getId()});		
		Map<String ,List> result = new HashMap<String,List>();
		result.put(CT_PA.PTA_RESULT, pt_available);
		return result;
	}

//查询波次已分配信息    
    @SuppressWarnings("unchecked")	
	public Map init_WDALD_INFO(Map params) {
    	Long waveDocId = (Long)params.get("waveDocId");
		
		final String hql = "SELECT new com.dev.swms.client.ui.javabean.PT_ALLOCATED(task.id," +
								   "task.srcLoc.code," +
								   "task.itemKey.item.code," +
								   "task.itemKey.item.name, " +
								   "task.packageUnit.unit," +
								   "task.planQuantity," +
								   "task.planQuantityMU-task.movedQuantityMU," +
								   "0.0, " +
								   "task.itemKey.lotInfo," +
								   "task.storageDate,task.SOI) "+
								   "from Task task where task.waveDocDetail.waveDoc.id = :waveDocId and task.movedQuantityMU = 0 "+
								   "and task.waveDocDetail.waveDoc.warehouse.id = :warehouseId";
		List<PT_ALLOCATED> ptad_info = commonDao.findByQuery(hql,new String[]{"waveDocId","warehouseId" },new Object[]{waveDocId, WarehouseHolder.getWarehouse().getId()});

		
		Map<String ,List> result = new HashMap<String,List>();
		result.put(CT_PA.PTAD_RESULT , ptad_info);
		return result;

	}
/**
 * 1、当波次规则为超商批拣模式且为按单作业时，要对Task中的重货和泡货进行搭配，明确要几个笼车后（按不同收货人、不同发货月台的总容积计算），
 * 		然后对每个笼车先平均放置重货，再按照泡货尽量把一个笼车放满；（不拆分Task）
 * 2、批拣分配产生的结果要单独一个对象放置（批拣流水号、波次ID、箱型ID、发货月台、创建日期、描述），并和Task关联（Task上增加批拣ID外键关联[允许为空]）；
 */
	public void operatingArrangement(List<Long> ids) {
		for(Long id:ids){
			WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
			
			deleteCageInfo(waveDoc.getId());//删除该波次以前生成的笼车信息
			
			Map map = operateForWaveDoc(waveDoc);
			//得到笼车
			List<BoxModel> boxModels = this.commonDao.findByNamedQuery("getBoxModelByWarehouseAndType",
					new String[]{"warehouseId","type"},
					new Object[]{waveDoc.getWarehouse().getId(),BoxModelType.CAGE});
			if(boxModels.size()==0){
				throw new BusinessException("createCageInfo.no.BoxModel");
			}
			BoxModel boxModel = boxModels.get(0);
			
			for (Object obj : map.keySet()) {
				String objs = (String)obj+" ";
				String[] strs = objs.split("#");
				Long dockId = Long.parseLong(strs[0]);//月台ID
				List<Long> taskIds = (List)map.get(obj);
				
				createCageInfo(waveDoc,dockId,taskIds,boxModel,strs[1].trim());
			}
			
			//将不能用笼车装的Task装入非笼车CageInfo，便于打印分组报表（打印报表的时候，没有用笼车装的Task打印在一起）
			List<Task> noCageTasks = getNotCageTask(waveDoc);
			if(noCageTasks.size()>0){
				CageInfo cageInfo = new CageInfo();
				cageInfo.setWarehouse(WarehouseHolder.getWarehouse());
				cageInfo.setCode(this.bussinessCodeManager.generateCageInfoCode(WarehouseHolder.getWarehouse().getCode()));
				cageInfo.setWaveDoc(waveDoc);
				cageInfo.setBoxModel(boxModel);
				cageInfo.setShipToName("");
				cageInfo.setDock(null);
				cageInfo.setCreateDate(new Date());
				cageInfo.setBeCage(false);//是笼车
				cageInfo.setHasBOL(false);
				this.commonDao.store(cageInfo);
				for(Task tk:noCageTasks){
					tk.setCageInfo(cageInfo);
					cageInfo.setVolumn(cageInfo.getVolumn()+tk.getPlanQuantity()*tk.getPackageUnit().getVolume());
					cageInfo.setWeight(cageInfo.getWeight()+tk.getPlanQuantity()*tk.getPackageUnit().getWeight());
					this.commonDao.store(tk);
				}
			}
		}
	}
//	得到非笼车Task
	private List<Task> getNotCageTask(WaveDoc waveDoc){
		List<Task> tempTasks = new ArrayList<Task>();
		String hql = "select task " +
			"from Task task inner join task.waveDocDetail waveDocDetail " +
			"inner join waveDocDetail.waveDoc waveDoc " +
			"inner join task.itemKey itemKey inner join itemKey.item item " +
			",ItemStorageRule rule  inner join rule.abcType abcType " +
			"where waveDoc.code='"+waveDoc.getCode()+"' " +
			"and item.id=rule.item.id and abcType.beCage=false ";
        tempTasks = this.commonDao.findByQuery(hql);
		return tempTasks;
	}
	/**
	 * 删除该波次以前生成的笼车信息
	 * @param waveDocId
	 */
	private void deleteCageInfo(Long waveDocId){
		List<CageInfo> cageInfos = this.commonDao.findByNamedQuery("queryCageInfoByWaveDoc",new String[]{"waveDocId"},new Object[]{waveDocId});
		for(CageInfo cageInfo:cageInfos){
			for(Task task:cageInfo.getTasks()){
				task.setCageInfo(null);
				this.commonDao.store(task);
			}
		}
		this.commonDao.deleteAll(cageInfos);
		this.commonDao.load(CageInfo.class,new Long(0));
	}
	//对每个波次产生的Task按照路向和月台进行分组;
	private Map<String, Long> operateForWaveDoc(WaveDoc waveDoc){
		Map result = new HashMap();
		//查询波次产生的、并且Task中的货品允许笼车装运的Task
		String hql = "select new com.dev.swms.server.model.shipping.TempTask(pickTicket.dock.id,pickTicket.shipToName,task.id) " +
		"from Task task inner join task.waveDocDetail waveDocDetail " +
		"inner join waveDocDetail.waveDoc waveDoc " +
		"inner join waveDocDetail.pickTicket pickTicket " +
		"inner join task.itemKey itemKey inner join itemKey.item item " +
		"where waveDoc.code='"+waveDoc.getCode()+"' " +
		"and item.id in (select rule.item.id from ItemStorageRule rule inner join rule.abcType abcType where abcType.beCage = true " +
		"and abcType.company.id=rule.item.company.id) " +
		"order by pickTicket.dock.id,pickTicket.shipToName";
		List<TempTask> tempTasks = this.commonDao.findByQuery(hql);
		for(TempTask tempTask:tempTasks){
			List<Long> taskIds = new ArrayList<Long>();
			if(result.get(tempTask.getLocId()+"#"+tempTask.getShipToName().trim())==null){
				taskIds.add(tempTask.getTaskId());
				result.put(tempTask.getLocId()+"#"+tempTask.getShipToName().trim(),taskIds);
			}else{
				taskIds = (List)result.get(tempTask.getLocId()+"#"+tempTask.getShipToName().trim());
				taskIds.add(tempTask.getTaskId());
				result.remove(tempTask.getLocId()+"#"+tempTask.getShipToName().trim());
				result.put(tempTask.getLocId()+"#"+tempTask.getShipToName().trim(),taskIds);
			}
		}
		return result;
	}
	/**
	 * 根据波次、月台、任务列生成笼车计划
	 * @param waveDoc
	 * @param dockId
	 * @param taskIds
	 */
	private void createCageInfo(WaveDoc waveDoc,Long dockId,List<Long> taskIds, BoxModel boxModel, String shipToName){
		List<Task> tasks = this.commonDao.loadAll(Task.class,taskIds.toArray(new Long[taskIds.size()]));
		
		int cageQty = getBoxModelQty(boxModel,tasks);//计算笼车数量
		//生成cageQty数量的笼车记录
		List<CageInfo> cageInfos = new ArrayList<CageInfo>();
		for(int i=0;i<cageQty;i++){
			CageInfo cageInfo = new CageInfo();
			cageInfo.setWarehouse(WarehouseHolder.getWarehouse());
			cageInfo.setCode(this.bussinessCodeManager.generateCageInfoCode(WarehouseHolder.getWarehouse().getCode()));
			cageInfo.setWaveDoc(waveDoc);
			cageInfo.setBoxModel(boxModel);
			cageInfo.setDock(this.commonDao.load(Location.class,dockId));
			cageInfo.setShipToName(shipToName);
			cageInfo.setCreateDate(new Date());
			cageInfo.setBeCage(true);//是笼车
			cageInfo.setHasBOL(false);
			cageInfos.add(cageInfo);
		}
		
		//对每个笼车先平均放置重货,不存在一个笼车放不下一个Task货品的情况
		for(Task task:tasks){
			if(task.getItemKey().getItem().isBeWeight()){//如果货品是重货
				cageInfos = sortCageInfos(cageInfos,"weight");//按照装货重量对cageInfos进行升序排序
				
				task.setCageInfo(cageInfos.get(0));
				cageInfos.get(0).getTasks().add(task);
				cageInfos.get(0).setVolumn(cageInfos.get(0).getVolumn()+task.getPlanQuantity()*task.getPackageUnit().getVolume());
				cageInfos.get(0).setWeight(cageInfos.get(0).getWeight()+task.getPlanQuantity()*task.getPackageUnit().getWeight());
				this.commonDao.store(cageInfos.get(0));
			}
		}
		
		//装泡货
		for(Task task:tasks){
			if(!task.getItemKey().getItem().isBeWeight()){//如果货品是泡货
				cageInfos = sortCageInfos(cageInfos,"volumn");//按照装货体积对cageInfos进行升序排序
				
				task.setCageInfo(cageInfos.get(0));
				cageInfos.get(0).getTasks().add(task);
				cageInfos.get(0).setVolumn(cageInfos.get(0).getVolumn()+task.getPlanQuantity()*task.getPackageUnit().getVolume());
				cageInfos.get(0).setWeight(cageInfos.get(0).getWeight()+task.getPlanQuantity()*task.getPackageUnit().getWeight());
				this.commonDao.store(cageInfos.get(0));
			}
			this.commonDao.store(task);
		}
		
	}
	
//	按照笼车已经装货的sortKey进行排序，确保装货sortKey最小的最先装货，避免装不下的问题
	private List<CageInfo> sortCageInfos(List<CageInfo> cageInfos, String sortKey){
		ComparatorChain chain =  new ComparatorChain();
		List<SortRule> sortRules = new ArrayList<SortRule>();
		sortRules.add(new SortRule(1, sortKey, "ASC"));
		for (SortRule sortRule : sortRules) {
			chain.addComparator(new BeanComparator(sortRule.getSortKey()));
        }
		Collections.sort(cageInfos,chain);
		return cageInfos;
	}
	/**
	 * 计算所有task需要的笼车数量
	 * @param boxModel
	 * @param tasks
	 * @return
	 */
	private int getBoxModelQty(BoxModel boxModel,List<Task> tasks){
		int cageQty = 0;
		double volumns = .0;//总体积
		double weights = .0;//总数量
		for(Task task:tasks){
			volumns+=task.getPlanQuantity()*task.getPackageUnit().getVolume();
			weights+=task.getPlanQuantity()*task.getPackageUnit().getWeight();;
		}
		if(boxModel.getVolume()==0 && boxModel.getWeight()==0){
			return 0;
		}
		if(boxModel.getVolume()!=0 && boxModel.getWeight()==0){
			cageQty = (int)Math.ceil(volumns/boxModel.getVolume());
		}
		if(boxModel.getVolume()==0 && boxModel.getWeight()!=0){
			cageQty = (int)Math.ceil(weights/boxModel.getWeight());
		}
		if(boxModel.getVolume()!=0 && boxModel.getWeight()!=0){
			if(Math.ceil(volumns/boxModel.getVolume())>=Math.ceil(weights/boxModel.getWeight())){
				cageQty = (int)Math.ceil(volumns/boxModel.getVolume());
			}else{
				cageQty = (int)Math.ceil(weights/boxModel.getWeight());
			}
		}
		return cageQty;
	}
	@Override
	public void manualcreateWaveDoc(Long sid,WaveDoc waveDoc) {
			
			Structure s =this.load(Structure.class, sid);
			waveDoc.setCompany(s);
			//波次规则
			List<WaveRule> waveRules = this.commonDao.findByNamedQuery(
					"getWaveRulesByWarehouse", new String[] { "warehouseId"},
					new Object[] { WarehouseHolder.getWarehouse().getId()});
			//货主规则
			 List<CompanyWorkRule> objs =  commonDao.findByQuery(" from CompanyWorkRule o where o.company.id=:id ",
						new String[] { "id"},new Object[] {s.getId()});
			 WaveRule wr=waveRules.get(0);
			
			 if(objs.size()>0) { //有对象取周转方式
				 CompanyWorkRule cwr= objs.get(0);
				  waveDoc.setTurnOverMode(cwr.getCompanyTurnOverMode());
			 }else {
				 waveDoc.setTurnOverMode(TurnOverMode.FIFO_VALIDDATE);//默认
			 }
			
			SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
			String code = wr.getWaveCode() +"_" + df.format(new Date());
			String hql = "select count(*) from WaveDoc waveDoc where waveDoc.code like '"
					+ code + "%'";
			Long nums = (Long) this.commonDao.findByQueryUniqueResult(hql,
					new String[] {}, new Object[] {});
			if (nums != 0) {
				code = code + "_" + nums;
			}
			waveDoc.setCode(code);
			Warehouse warehouse=this.load(Warehouse.class,WarehouseHolder.getWarehouse().getId());
			waveDoc.setWarehouse(warehouse);
			
			waveDoc.setUserID( UserHolder.getUser().getId());
			waveDoc.setUserName(UserHolder.getUser().getLoginName());
			waveDoc.setCreateDate(new Date());
			waveDoc.setStatus(WaveDocStatus.OPEN);
			waveDoc.setWorkModel(wr.getWorkModel());
			waveDoc.setQuantity(waveDoc.getOrderQuantity());
			commonDao.store(waveDoc);
		
	}
	
	//对应的订单数量需要更新
	public void Refresh(Long id) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
			
			Double qty= (Double) commonDao.findByQuery("select nvl(sum(o.orderQuantity),0) from PlanPickTicketDetail o where o.pickTicket.waveDoc.id=:id ",
					new String[] { "id"},new Object[] { waveDoc.getId()}).get(0);
			waveDoc.setQuantity(qty);
			commonDao.store(waveDoc);
	
	}
	
	//对应的订单数量需要更新
	public void RefreshWaveDoc(Long id) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
			
			Double qty= (Double) commonDao.findByQuery("select nvl(sum(o.orderQuantity),0) from WaveDocDetail o where o.waveDoc.id=:id ",
					new String[] { "id"},new Object[] { waveDoc.getId()}).get(0);
			waveDoc.setQuantity(qty);
			commonDao.store(waveDoc);
	
	}
	
	@Override
	public void removePickTicketsToWaveDoc(Long waveDocId, List<Long> ids) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		Long waveDocid=null;
		for(Long id : ids) {
			
			PickTicketDetail planPickTicketDetail=this.load(PickTicketDetail.class, id);
			waveDocid=planPickTicketDetail.getPickTicket().getWaveDoc().getId();
			planPickTicketDetail.getPickTicket().setWaveDoc(null);
			planPickTicketDetail.getPickTicket().setWaveCode(null);
			commonDao.store(planPickTicketDetail);
		
		}
		if(waveDocid!=null) {
			Refresh(waveDocid);//对应的订单数量需要更新
		}
		
	}
	@Override
	public void savePickTicketsToWaveDoc(Long waveDocId, List<Long> ids) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		for(Long id : ids) {
			PickTicket pickTicket=this.load(PickTicket.class, id);
			pickTicket.setWaveDoc(waveDoc);
			pickTicket.setWaveCode(waveDoc.getCode());
			commonDao.store(pickTicket);
		}
		
		Refresh(waveDocId);//对应的订单数量需要更新
	
	}
	
	@Override
	public void saveWaveDoc(Long waveDocId, List<Long> ids) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		for(Long id : ids) {
			WaveDocDetail waveDocDetail=this.load(WaveDocDetail.class, id);
			waveDocDetail.setWaveDoc(waveDoc);;
			commonDao.store(waveDocDetail);
		}
		RefreshWaveDoc(waveDocId);//对应的订单数量需要更新
	}
	@Override
	public void removeWaveDoc(Long waveDocId, List<Long> ids) {
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		Long waveDocid=null;
		for(Long id : ids) {
			WaveDocDetail WaveDocDetail=this.load(WaveDocDetail.class, id);
			waveDocid=WaveDocDetail.getWaveDoc().getId();
			WaveDocDetail.setWaveDoc(null);
			commonDao.store(WaveDocDetail);
		}
		if(waveDocid!=null) {
			RefreshWaveDoc(waveDocid);//对应的订单数量需要更新
		}
		
	}
	
	
	public void addMachiningDetail(MachiningDetail md,Long waveDocId){
		
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
		md.setWaveDoc(waveDoc);
		commonDao.store(md);
		refmd(waveDocId);
	}
	public void refmd(Long waveDocId){
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
	Double qty= (Double) commonDao.findByQuery("select nvl(sum(o.quantity),0) from MachiningDetail o where o.waveDoc.id=:id ",
			new String[] { "id"},new Object[] { waveDoc.getId()}).get(0);
	waveDoc.setOrderQuantity(qty);
	commonDao.store(waveDoc);
	}
	
	public void deleteMachiningDetail(List<Long> ids){
		for(Long id:ids){
			WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
			List<WaveDocItem> mds=commonDao.findByQuery(" from WaveDocItem o where o.waveDoc.id=:id ",
					new String[] { "id"},new Object[] { id});
			for(WaveDocItem md:mds){
				commonDao.delete(md);
			}
			commonDao.delete(waveDoc);
//			refwaveDocItem(id);
		}
		
	}
	
	
	public void effectiveMachiningDetail(List<Long> ids){
		
		for(Long id:ids){
			WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
			List<WaveDocItem> mds=commonDao.findByQuery(" from WaveDocItem o where o.waveDoc.id=:id ",
					new String[] { "id"},new Object[] { waveDoc.getId()});
			if(mds.size()>0){
				for(WaveDocItem md:mds){
					Item i=commonDao.load(Item.class, md.getItem().getId());
					WaveDocDetail wdd=new WaveDocDetail();
					wdd.setWaveDoc(md.getWaveDoc());
					wdd.setItem(md.getItem());
					wdd.setQuantity(md.getQuantity());
					wdd.setPackageUnit(md.getItem().getMinPackageUnit());
					wdd.setCaCoefficient(md.getItem().getCAcoefficient());
					commonDao.store(wdd);
				}
			}else{
				throw new BusinessException("无明显，生效失败");
			}
		}
		
		
	}
	
	
	public void abateMachiningDetail(List<Long> ids){
		
		
	}
	
	public void saveWaveDocItem(Long id,WaveDocItem waveDocItem){
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,id);
		waveDocItem.setWaveDoc(waveDoc);
		commonDao.store(waveDocItem);
		refwaveDocItem(id);
	}
	@Transactional
	public void deleteWaveDocItem(Long waveDocid,List<Long> ids){
		for(Long id:ids){
			WaveDocItem waveDocItem=commonDao.load(WaveDocItem.class, id);
			commonDao.delete(waveDocItem);
		}
		refwaveDocItem(waveDocid);
	}
	
	
	
	public void refwaveDocItem(Long waveDocId){
		WaveDoc waveDoc = this.commonDao.load(WaveDoc.class,waveDocId);
	Double qty= (Double) commonDao.findByQuery("select nvl(sum(o.quantity),0) from WaveDocItem o where o.waveDoc.id=:id ",
			new String[] { "id"},new Object[] { waveDoc.getId()}).get(0);
	waveDoc.setOrderQuantity(qty);
	commonDao.store(waveDoc);
	}
	
	
}
