/* * Copyright (C) 2014 GG-Net GmbH - Oliver Günther * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.ggnet.dwoss.uniqueunit.entity; import eu.ggnet.dwoss.rules.ProductGroup; import eu.ggnet.dwoss.rules.TradeName; import java.io.Serializable; import java.util.*; import javax.persistence.*; import javax.validation.constraints.NotNull; import javax.validation.constraints.Null; import eu.ggnet.dwoss.util.MathUtil; import eu.ggnet.dwoss.util.persistence.EagerAble; import lombok.*; import static javax.persistence.CascadeType.*; /** * Represents a Product. * * @has 1 - 1 TradeName * @has 1 - 1 ProductGroup * @has 1 - n PriceHistory * @has n - m Flag * @has 1 - n UniqueUnit * @author oliver.guenther */ @Entity @EqualsAndHashCode(of = "id") @NamedQueries({ @NamedQuery(name = "Product.byTradeNames", query = "select p from Product p where p.tradeName in (?1)"), @NamedQuery(name = "Product.byPartNos", query = "select p from Product p where p.partNo in (?1)"), @NamedQuery(name = "Product.byContractor", query = "SELECT DISTINCT p FROM Product p JOIN p.units u WHERE u.contractor = ?1") }) public class Product implements Serializable, EagerAble, Comparable<Product> { public static NavigableMap<String, Product> asMapByPartNos(Collection<Product> products) { NavigableMap<String, Product> result = new TreeMap<>(); for (Product product : products) { result.put(product.getPartNo(), product); } return result; } public static enum Flag { PRICE_FIXED } @Getter @Id @GeneratedValue private long id; @Version private short optLock; /** * The name of the Product. * * It sounds weird but, it can happen, that a Spec overwrites the value */ @Getter @Setter @Basic(optional = false) private String name; @Getter @Setter @Basic @Column(length = 65536) @Lob private String description; /** * Represents the trade name of the Product. * Normally this is the manufacturer, oder the company responsible, but may also be a brand or else. */ @Getter @Setter @Basic(optional = false) private TradeName tradeName; @Getter @Setter @Basic(optional = false) @Column(name = "productGroup") private ProductGroup group; /** * This is the primary PartNo of the Product. It should be the PartNo of the TradeName */ @Getter @Setter @Basic(optional = false) private String partNo; @Getter @Setter @Temporal(javax.persistence.TemporalType.DATE) private Date eol; @NotNull @ElementCollection(fetch = FetchType.EAGER) @MapKeyEnumerated private Map<TradeName, String> additionalPartNo = new EnumMap<>(TradeName.class); @NotNull @ElementCollection(fetch = FetchType.EAGER) @MapKeyEnumerated private Map<PriceType, Double> prices = new EnumMap<>(PriceType.class); @NotNull @OneToMany(cascade = ALL) private List<PriceHistory> priceHistories = new ArrayList<>(); /** * Represents Flags the user can set for this element. * This is a better aproacht, than creating multiple boolean falues. */ @Getter @Setter @NotNull @ElementCollection(fetch = FetchType.EAGER) private Set<Flag> flags = EnumSet.noneOf(Flag.class); // TODO: Add validation, that you cannot remove a Product if it has one or more units @NotNull @OneToMany(cascade = {MERGE, REFRESH, PERSIST, DETACH}, mappedBy = "product") List<UniqueUnit> units = new ArrayList<>(); @Setter @Getter private int imageId; /** * Global Trade Item Number, was EAN. */ @Setter @Getter private String gtin; public Product() { } public Product(ProductGroup group, TradeName tradeName, String partNo, String name) { this.group = group; this.tradeName = tradeName; this.partNo = partNo; this.name = name; } public void addUnit(UniqueUnit unit) { if ( unit == null ) return; unit.setProduct(this); } public void removeUnit(UniqueUnit unit) { if ( unit == null ) return; unit.setProduct(null); } public void setPrice(PriceType type, double price, String comment) { if ( MathUtil.equals(getPrice(type), price) ) return; // Don't set the same price prices.put(type, price); priceHistories.add(new PriceHistory(type, price, new Date(), comment)); } public double getPrice(PriceType type) { return prices.get(type) == null ? 0 : prices.get(type); } public boolean hasPrice(PriceType type) { return prices.get(type) != null && prices.get(type) > 0.01; } public Map<PriceType, Double> getPrices() { return prices; } public List<PriceHistory> getPriceHistory() { return priceHistories; } public void setAdditionalPartNo(TradeName tradeName, String partNo) { additionalPartNo.put(tradeName, partNo); } public String getAdditionalPartNo(TradeName tradeName) { return additionalPartNo.get(tradeName); } public void removeAdditionalPartNo(TradeName tradeName) { additionalPartNo.remove(tradeName); } public Map<TradeName, String> getAdditionalPartNos() { return additionalPartNo; } public boolean removeFlag(Flag flag) { return flags.remove(flag); } public boolean addFlag(Flag flag) { return flags.add(flag); } /** * Returns null if the instance is valid, or a string representing the error. * <p/> * @return null if the instance is valid, or a string representing the error. */ @Null public String getViolationMessage() { if ( tradeName == null ) return null; if ( !tradeName.isBrand() ) return tradeName + " is not a Brand"; if ( tradeName.getManufacturer().getPartNoSupport() == null ) return null; // No Support, so everything is ok. return tradeName.getManufacturer().getPartNoSupport().violationMessages(partNo); } @Override public int compareTo(Product other) { if ( other == null ) return +1; if ( this.group != other.group ) return this.group.compareTo(other.group); if ( this.tradeName != other.tradeName ) return this.tradeName.compareTo(other.tradeName); if ( !this.partNo.equals(other.partNo) ) return this.partNo.compareTo(other.partNo); return (int)(this.id - other.id); } @Override public void fetchEager() { priceHistories.size(); units.size(); } @Override public String toString() { return "Product{" + "id=" + id + ", partNo=" + partNo + ", group=" + group + ", tradeName=" + tradeName + ", name=" + name + ", eol=" + eol + ", gtin=" + gtin + ", description=" + description + ", additionalPartNo=" + additionalPartNo + ", prices=" + prices + ", flags=" + flags + ", imageId=" + imageId + '}'; } }