package com.dev.swms.server.model.receiving;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import com.dev.swms.server.model.base.Address;
import com.dev.swms.server.model.base.BillType;
import com.dev.swms.server.model.base.Contact;
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.LotInfo;
import com.dev.swms.server.model.base.SignFile;
import com.dev.swms.server.model.base.Structure;
import com.dev.swms.server.model.base.PackageUnit;
import com.dev.swms.server.model.base.Warehouse;
import com.dev.swms.server.model.base.WarehouseArea;
import com.dev.swms.server.model.base.Worker;
import com.dev.swms.server.model.inventory.Stock;
import com.dev.energy.server.exception.BusinessException;
import com.dev.energy.server.model.VersionalEntity;
import com.dev.energy.server.web.security.UserHolder;

public class ASN extends VersionalEntity {
	private static final long serialVersionUID = 2749582140284525084L;
	/** 定义单据类型 */
	public static final String PO_STRAIGHT = "PO_STRAIGHT";
	public static final String PO_TRANSFER = "ZCRK";
	public static final String PO_SWAP = "PO_SWAP";

	/** 所属仓库 */
	private Warehouse warehouse;

	/** 所属客户 */
	private Structure company;

	/** 收货单号 */
	private String code;

	/** 状态 */
	private String status;

	/** 相关单据号1 */
	private String relatedBill1;

	/** 相关单据号2 */
	private String relatedBill2;

	/** 相关单据号3--标记退货字符 */
	private String relatedBill3;

	/** 订单日期 */
	private Date orderDate;

	/** 预计到货日期 */
	private Date estimateDate;

	/** 收货日期 */
	private Date receivedDate;

	/** 发货方名称 */
	private String fromName;

	/** 发货方地址 */
	private Address fromAddress;

	/** 发货方联系方式 */
	private Contact fromContact;

	/** 预期数量 */
	private double expectedQuantity;

	/** 实收数量 */
	private double receivedQuantity;

	/** 上架数量 */
	private double putawayQuantity;

	/** 收货单明细 */
	private Set<ASNDetail> asnDetails = new HashSet<ASNDetail>();

	/** 收货记录明细 */
	private Set<ReceivedRecord> receivedRecords;

	/** 最后修改人ID * */
	private Long userID;

	/** 最后修改人姓名 * */
	private String userName;

	/** 最后修改时间 * */
	private Date updateTime;

	/** 单据类型 */
	private BillType billType;

	/** 月台 */
	private Location dock;
	
	/** 月台 */
	private WarehouseArea zone;

	/** 车牌号 */
	private String vehicle;

	/** 出入库状态（入库、出库） */
	private String storeType;

	/** 入库时间 */
	private Date inputDate;

	/** 出库时间 */
	private Date outputDate;

	/** 入库车辆管理作业人员 */
	private Worker inputWorker;

	/** 出库车辆管理作业人员 */
	private Worker outputWorker;

	/** 收货预约开始时间 * */
	private Date receiveDateStart;

	/** 收货预约结束时间 * */
	private Date receiveDateEnd;

	/** 备注 */
	private String description;
	/**  */
	private String temperature;//保税/课税

	private double weight;// 重量

	private double receivedWeight;// 毛重

	private double volume;// 体积
	/**  */
	private String sourceId;//贸易方式
	/**  */
	private String sourceType;//大品名
	/** 作业人员 */
	private Worker worker1;
	/** 作业人员 */
	private Worker worker2;
	/** 作业人员 */
	private Worker worker3;
	/** 作业人员 */
	private Worker worker4;

	private double warehouseTrayNO;// 入库托盘数

	private String shipmentType;// 入库类型

	private Set<SignFile> files = new HashSet<SignFile>(); // 附件清单

	public WarehouseArea getZone() {
		return zone;
	}

	public void setZone(WarehouseArea zone) {
		this.zone = zone;
	}

	public double getReceivedWeight() {
		return receivedWeight;
	}

	public void setReceivedWeight(double receivedWeight) {
		this.receivedWeight = receivedWeight;
	}

	public Set<SignFile> getFiles() {
		return files;
	}

	public void setFiles(Set<SignFile> files) {
		this.files = files;
	}

	public String getShipmentType() {
		return shipmentType;
	}

	public void setShipmentType(String shipmentType) {
		this.shipmentType = shipmentType;
	}

	public double getWarehouseTrayNO() {
		return warehouseTrayNO;
	}

	public void setWarehouseTrayNO(double warehouseTrayNO) {
		this.warehouseTrayNO = warehouseTrayNO;
	}

	public Worker getWorker1() {
		return worker1;
	}

	public void setWorker1(Worker worker1) {
		this.worker1 = worker1;
	}

	public Worker getWorker2() {
		return worker2;
	}

	public void setWorker2(Worker worker2) {
		this.worker2 = worker2;
	}

	public Worker getWorker3() {
		return worker3;
	}

	public void setWorker3(Worker worker3) {
		this.worker3 = worker3;
	}

	public Worker getWorker4() {
		return worker4;
	}

	public void setWorker4(Worker worker4) {
		this.worker4 = worker4;
	}

	public String getSourceId() {
		return sourceId;
	}

	public void setSourceId(String sourceId) {
		this.sourceId = sourceId;
	}

	public String getSourceType() {
		return sourceType;
	}

	public void setSourceType(String sourceType) {
		this.sourceType = sourceType;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public double getVolume() {
		return volume;
	}

	public void setVolume(double volume) {
		this.volume = volume;
	}

	public ASN() {

	}

	public String getTemperature() {
		return temperature;
	}

	public void setTemperature(String temperature) {
		this.temperature = temperature;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public double getPutawayQuantity() {
		return putawayQuantity;
	}

	public void setPutawayQuantity(double putawayQuantity) {
		this.putawayQuantity = putawayQuantity;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public Structure getCompany() {
		return company;
	}

	public void setCompany(Structure company) {
		this.company = company;
	}

	public Date getEstimateDate() {
		return estimateDate;
	}

	public void setEstimateDate(Date estimateDate) {
		this.estimateDate = estimateDate;
	}

	public double getExpectedQuantity() {
		return expectedQuantity;
	}

	public void setExpectedQuantity(double expectedQuantity) {
		this.expectedQuantity = expectedQuantity;
	}

	public Address getFromAddress() {
		return fromAddress;
	}

	public void setFromAddress(Address fromAddress) {
		this.fromAddress = fromAddress;
	}

	public Contact getFromContact() {
		return fromContact;
	}

	public void setFromContact(Contact fromContact) {
		this.fromContact = fromContact;
	}

	public String getFromName() {
		return fromName;
	}

	public void setFromName(String fromName) {
		this.fromName = fromName;
	}

	public Date getOrderDate() {
		return orderDate;
	}

	public void setOrderDate(Date orderDate) {
		this.orderDate = orderDate;
	}

	public Date getReceivedDate() {
		return receivedDate;
	}

	public void setReceivedDate(Date receivedDate) {
		this.receivedDate = receivedDate;
	}

	public double getReceivedQuantity() {
		return receivedQuantity;
	}

	public void setReceivedQuantity(double receivedQuantity) {
		this.receivedQuantity = receivedQuantity;
	}

	public String getRelatedBill1() {
		return relatedBill1;
	}

	public void setRelatedBill1(String relatedBill1) {
		this.relatedBill1 = relatedBill1;
	}

	public String getRelatedBill2() {
		return relatedBill2;
	}

	public void setRelatedBill2(String relatedBill2) {
		this.relatedBill2 = relatedBill2;
	}

	public String getRelatedBill3() {
		return relatedBill3;
	}

	public void setRelatedBill3(String relatedBill3) {
		this.relatedBill3 = relatedBill3;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public Warehouse getWarehouse() {
		return warehouse;
	}

	public void setWarehouse(Warehouse warehouse) {
		this.warehouse = warehouse;
	}

	public Set<ReceivedRecord> getReceivedRecords() {
		return receivedRecords;
	}

	public void setReceivedRecords(Set<ReceivedRecord> receivedRecords) {
		this.receivedRecords = receivedRecords;
	}

	public Date getUpdateTime() {
		return updateTime;
	}

	public void setUpdateTime(Date updateTime) {
		this.updateTime = updateTime;
	}

	public Long getUserID() {
		return userID;
	}

	public void setUserID(Long userID) {
		this.userID = userID;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	/**
	 * 记录收货单的最新修改用户和修改时间信息
	 * 
	 */
	public void setUserTracking() {
		this.setUserID(UserHolder.getUser().getId());
		this.setUserName(UserHolder.getUser().getName());
		this.setUpdateTime(new Date());
	}

	/**
	 * 检查是否有重复的ASNDetail
	 */
	public ASNDetail geASNDetailByCondition(String PO, Integer lineNo, Item item, LotInfo lotInfo, PackageUnit unit) {
		if (this.asnDetails != null) {
			for (ASNDetail detail : this.getAsnDetails()) {
				if (
//						PO.equals(detail.getPO()) 
//						&& 
				lineNo == detail.getLineNo() && item.getId().equals(detail.getItem().getId())
						&& lotInfo.equals(detail.getLotInfo())
						&& unit.getId().equals(detail.getPackageUnit().getId())) {
					return detail;
				}
			}
		}
		return null;
	}

	/**
	 * 删除收货单明细，并扣减收货单的期待数量
	 * 
	 * @param asnDetailIds 待删除收货单明细的ID集合
	 */
	public void removeDetails(List<Long> asnDetailIds) {
		if (asnDetails != null && ASNStatus.OPEN.equals(this.status)) {
			Set<ASNDetail> toBeRemoved = new HashSet<ASNDetail>();
			for (ASNDetail detail : asnDetails) {
				if (asnDetailIds.contains(detail.getId())) {
					toBeRemoved.add(detail);
				}
			}
			for (ASNDetail detail : toBeRemoved) {
				asnDetails.remove(detail);
				this.expectedQuantity -= detail.getExpectedQtyOfMasterUnit();
			}
		}
	}

	/**
	 * 增加收货单明细，并增加收货单的期待数量
	 * 
	 * @param asnDetail 待增加的收货单明细
	 */
	public void addASNDetail(ASNDetail asnDetail) {
		asnDetail.setAsn(this);
		if (this.getAsnDetails().contains(asnDetail)) {
			throw new BusinessException("DataIntegrityViolationException");
		}
		this.getAsnDetails().add(asnDetail);
		this.expectedQuantity += asnDetail.getExpectedQtyOfMasterUnit();
	}

	/**
	 * 增加收货单和收货单明细的期待数量
	 * 
	 * @param docDetail 待增加期待数量的收货单明细
	 * @param quantity  待增加的期待数量(包装数量)
	 */
	public void addExpectedQuantity(ASNDetail asnDetail, double quantity) {
		this.expectedQuantity += quantity * asnDetail.getPackageUnit().getConvertFigure();
		asnDetail.addExpectedQuantity(quantity);
		setUserTracking();
	}

	/**
	 * 检查收货单是否所有明细都完成收货
	 * 
	 * @return true-收货完毕 false－未完全收货
	 */
	public boolean isAllReceived() {
		for (ASNDetail detail : this.getAsnDetails()) {
			if (detail.getExpectedQtyOfMasterUnit() != detail.getReceivedQuantity()) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 收货确认－修改收货单明细收货数量、收货单收货数量、收货日期以及收货单状态
	 * 
	 * @param asnDetail   待收货的收货单明细
	 * @param location    收货库位
	 * @param itemKey     收货商品
	 * @param quantity    收货数量
	 * @param packageUnit 收货包装
	 * @param receiveDate 收货日期
	 * @param asn         入库单
	 */
	public ReceivedRecord receiving(ASNDetail asnDetail, Location location, ItemKey itemKey, double quantity,
			PackageUnit packageUnit, Stock stock, String barCode, double weight, double volume, Date receiveDate,
			ReceivedDoc receivedDoc, Worker worker, String qualityCode) {
		ReceivedRecord receivedRecord = asnDetail.receiving(location, itemKey, quantity, packageUnit, stock, barCode,
				weight, volume, receivedDoc, receiveDate, worker, qualityCode);

		this.receivedQuantity += quantity;
		if (this.receivedDate == null) {
			this.receivedDate = receiveDate;
		}
		// 如果货主允许多收，则收货完毕后，ASN状态维持"收货中",如果货主需要收货质检,ASN收货确认时修改ASN状态为"收货中"
		if (isAllReceived()){// && !this.getCompany().isBeRevLimit()) {
			this.status = ASNStatus.RECEIVED;
		} else {
			this.status = ASNStatus.RECEIVING;
		}
		//setUserTracking();
		return receivedRecord;
	}

	/**
	 * 激活收货单－设置收货单“打开”状态为“生效”
	 * 
	 * @return true-成功 false－失败
	 */
	public boolean active() {
		if (this.getAsnDetails().size() > 0 && ASNStatus.OPEN.equals(this.getStatus())
				&& (this.getDock() != null || (this.getBillType().getCode().equals(ASN.PO_STRAIGHT)
						|| this.getBillType().getCode().equals(ASN.PO_TRANSFER)))) {
			this.setStatus(ASNStatus.ACTIVE);
			this.setUserTracking();
			return true;
		}
		return false;
	}

	/**
	 * 取消激活收货单－设置收货单“生效”状态为“打开” 已收货的单据不能取消激活
	 * 
	 * @return true-成功 false－失败
	 */
	public boolean inactive() {
		if (ASNStatus.ACTIVE.equals(this.getStatus()) && this.getReceivedQuantity() == 0) {
			this.setStatus(ASNStatus.OPEN);
			this.receivedDate = null;
			setUserTracking();
			return true;
		}
		return false;
	}

	/**
	 * 关闭收货单－设置收货单“收货”状态为“关闭”
	 * 
	 * @return true-成功 false－失败
	 */
	public boolean close() {
		if (ASNStatus.PUTAWAY.equals(this.getStatus())) {
			this.setStatus(ASNStatus.CLOSE);
			setUserTracking();
			return true;
		}
		return false;
	}

	/**
	 * 取消收货单－设置收货单“打开”状态为“取消”
	 * 
	 * @return true-成功 false－失败
	 */
	public boolean cancel() {
		if (ASNStatus.OPEN.equals(this.getStatus())) {
			this.setStatus(ASNStatus.CANCEL);
			setUserTracking();
			return true;
		}
		return false;
	}

	public BillType getBillType() {
		return billType;
	}

	public void setBillType(BillType billType) {
		this.billType = billType;
	}

	public void addReceivedQuantity(double quantity) {
		this.receivedQuantity += quantity;
	}

	public boolean equals(final Object other) {
		if (!(other instanceof ASN))
			return false;
		ASN castOther = (ASN) other;
		return new EqualsBuilder().append(warehouse, castOther.getWarehouse()).append(code, castOther.getCode())
				.isEquals();
	}

	public int hashCode() {
		return new HashCodeBuilder().append(warehouse).append(code).toHashCode();
	}

	public void receiveBack(double quantity, MoveDoc moveDoc) {
		this.receivedQuantity -= quantity;
		if (this.receivedQuantity > 0) {
			this.setStatus(ASNStatus.RECEIVING);
		} else {
			this.setStatus(ASNStatus.ACTIVE);
			this.setReceivedDate(null);
		}
		if (moveDoc != null) {
			for (ReceivedRecord record : this.getReceivedRecords()) {
				if (moveDoc.getCode().equals(record.getMoveDocCode())) {
					record.setMoveDocCode(null);
				}
			}
		}
	}

	public Set<ASNDetail> getAsnDetails() {
		return asnDetails;
	}

	public void setAsnDetails(Set<ASNDetail> asnDetails) {
		this.asnDetails = asnDetails;
	}

	public Location getDock() {
		return dock;
	}

	public void setDock(Location dock) {
		this.dock = dock;
	}

	public Date getInputDate() {
		return inputDate;
	}

	public void setInputDate(Date inputDate) {
		this.inputDate = inputDate;
	}

	public Date getOutputDate() {
		return outputDate;
	}

	public void setOutputDate(Date outputDate) {
		this.outputDate = outputDate;
	}

	public String getStoreType() {
		return storeType;
	}

	public void setStoreType(String storeType) {
		this.storeType = storeType;
	}

	public String getVehicle() {
		return vehicle;
	}

	public void setVehicle(String vehicle) {
		this.vehicle = vehicle;
	}

	public Worker getInputWorker() {
		return inputWorker;
	}

	public void setInputWorker(Worker inputWorker) {
		this.inputWorker = inputWorker;
	}

	public Worker getOutputWorker() {
		return outputWorker;
	}

	public void setOutputWorker(Worker outputWorker) {
		this.outputWorker = outputWorker;
	}

	public Date getReceiveDateEnd() {
		return receiveDateEnd;
	}

	public void setReceiveDateEnd(Date receiveDateEnd) {
		this.receiveDateEnd = receiveDateEnd;
	}

	public Date getReceiveDateStart() {
		return receiveDateStart;
	}

	public void setReceiveDateStart(Date receiveDateStart) {
		this.receiveDateStart = receiveDateStart;
	}

	/**
	 * 移除和附件的关联关系
	 * 
	 * @author dev
	 */
	public void removeFiles(SignFile signFile) {
		this.getFiles().remove(signFile);
	}
}
