package de.onyxbits.tradetrax.entities; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Vector; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Table; import org.hibernate.annotations.Type; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Restrictions; /** * An item (or stack of items) in the inventory. * * @author patrick * */ @Entity @Table(name = "stock") public class Stock implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * Row index */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; /** * Optional storage location identifier (free form text) */ @Column(name = "location") private String location; /** * Optional comment (free form text). */ @Column(name = "comment") private String comment; /** * How much a single unit cost when buying it */ @Column(name = "buyprice") private long buyPrice; /** * How much a single unit return when selling it. */ @Column(name = "sellprice") private long sellPrice; /** * How many individual items are in this stack? Stacks may only be sold as a * whole. In case a partial amount is to be sold, the Asset must be split into * smaller stacks first. * <p> * Any integer number is valid. A value of zero might mean the user is * tracking stock in refillable bins, a negative value might represent stock * that was bought on margins. */ @Column(name = "stacksize") private int unitCount = 1; /** * Human readable name of the asset */ @ManyToOne(cascade = { CascadeType.ALL }) private Name name; /** * Subtype (e.g. if the asset is available in multiple colors). */ @ManyToOne(cascade = CascadeType.ALL) private Variant variant; /** * Timestamp: when the asset was bought. */ @Column @Type(type = "timestamp") private Date acquired; /** * Timestamp: when the asset was sold */ @Column @Type(type = "timestamp") private Date liquidated; public Stock() { } /** * Create a stock using a template */ public Stock(Stock template) { acquired = template.acquired; buyPrice = template.buyPrice; comment = template.comment; id = template.id; liquidated = template.liquidated; if (template.name != null) { name = new Name(); name.setId(template.name.getId()); name.setLabel(template.name.getLabel()); } sellPrice = template.sellPrice; unitCount = template.unitCount; location = template.location; if (template.variant != null) { variant = new Variant(); variant.setId(template.variant.getId()); variant.setLabel(template.variant.getLabel()); } } /** * Split a new Stock off from this one. * * @param amount * number of units to transfer to the split off stock * @return a new instance with the specified number of units. */ public Stock splitStock(int amount) { Stock ret = new Stock(); if (acquired != null) { ret.acquired = (Date) acquired.clone(); } ret.buyPrice = buyPrice; if (liquidated != null) { ret.liquidated = (Date) liquidated.clone(); } ret.name = name; ret.sellPrice = sellPrice; ret.variant = variant; ret.unitCount = amount; unitCount -= amount; return ret; } /** * Create the criteria for finding other stock items that are allowed to merge * with this one. Two items may merge if they only differ in comment, * unitcount and acquisition/liquidation date (but they need to be in the same * state of acquisition/liquidation). * * @return Hibernate criterias */ public List<Criterion> allowedToMergeWith() { Vector<Criterion> ret = new Vector<Criterion>(); ret.add(Restrictions.ne("id", id)); ret.add(Restrictions.eq("buyPrice", buyPrice)); ret.add(Restrictions.eq("sellPrice", sellPrice)); if (name == null) { // This should never happen ret.add(Restrictions.isNull("name")); } else { ret.add(Restrictions.eq("name.id", name.getId())); } if (variant == null) { ret.add(Restrictions.isNull("variant")); } else { ret.add(Restrictions.eq("variant.id", variant.getId())); } if (acquired == null) { ret.add(Restrictions.isNull("acquired")); } else { ret.add(Restrictions.isNotNull("acquired")); } if (liquidated == null) { ret.add(Restrictions.isNull("liquidated")); } else { ret.add(Restrictions.isNotNull("liquidated")); } return ret; } /** * Calculate the profit, disregarding of whether the asset has been acquired * and liquidated. * * @return the profit in database format. */ public long calcProfit() { return (sellPrice - buyPrice) * unitCount; } /** * Calculate the total aquisition cost, disregarding whether or not the asset * has been acquired. * * @return buyprice times unitcount */ public long calcTotalCost() { return buyPrice * unitCount; } /** * Calculate the total returns, disregarding whetehr or not the asset has been * liquidated. * * @return sellprice times unit */ public long calcTotalReturns() { return sellPrice * unitCount; } /** * Calculate the financial impact of the stock in its current state on the * owner's wallet. * * @return the balance in database format. */ public long calcBalance() { long bal = 0; if (liquidated != null) { bal = sellPrice * unitCount; } if (acquired != null) { bal -= buyPrice * unitCount; } return bal; } /** * @return the comment */ public String getComment() { return comment; } /** * @param comment * the comment to set */ public void setComment(String comment) { this.comment = comment; } /** * @return the id */ public long getId() { return id; } /** * @param id * the id to set */ public void setId(long id) { this.id = id; } /** * @return the buyPrice */ public long getBuyPrice() { return buyPrice; } /** * @param buyPrice * the buyPrice to set */ public void setBuyPrice(long buyPrice) { this.buyPrice = buyPrice; } /** * @return the sellPrice */ public long getSellPrice() { return sellPrice; } /** * @param sellPrice * the sellPrice to set */ public void setSellPrice(long sellPrice) { this.sellPrice = sellPrice; } /** * @return the unitCount */ public int getUnitCount() { return unitCount; } /** * @param unitCount * the unitCount to set */ public void setUnitCount(int unitCount) { this.unitCount = unitCount; } /** * @return the name */ public Name getName() { return name; } /** * @param name * the name to set */ public void setName(Name name) { this.name = name; } /** * @return the variant */ public Variant getVariant() { return variant; } /** * @param variant * the variant to set */ public void setVariant(Variant variant) { this.variant = variant; } /** * @return the aquired */ public Date getAcquired() { return acquired; } /** * @param aquired * the aquired to set */ public void setAcquired(Date aquired) { this.acquired = aquired; } /** * @return the liquidated */ public Date getLiquidated() { return liquidated; } /** * @param liquidated * the liquidated to set */ public void setLiquidated(Date liquidated) { this.liquidated = liquidated; } /** * @return the location */ public String getLocation() { return location; } /** * @param location * the location to set */ public void setLocation(String location) { this.location = location; } }