/*
* 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.price;
import eu.ggnet.dwoss.uniqueunit.entity.PriceType;
import eu.ggnet.dwoss.uniqueunit.entity.Product;
import eu.ggnet.dwoss.uniqueunit.entity.UniqueUnit;
import java.util.*;
import java.util.Map.Entry;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.*;
import eu.ggnet.dwoss.mandator.api.value.Mandator;
import eu.ggnet.saft.api.progress.IMonitor;
import eu.ggnet.dwoss.progress.SubMonitor;
import eu.ggnet.dwoss.rules.Warranty;
import eu.ggnet.dwoss.spec.assist.Specs;
import eu.ggnet.dwoss.spec.eao.ProductSpecEao;
import eu.ggnet.dwoss.spec.entity.ProductSpec;
import eu.ggnet.dwoss.stock.assist.Stocks;
import eu.ggnet.dwoss.stock.eao.StockUnitEao;
import eu.ggnet.dwoss.uniqueunit.assist.UniqueUnits;
import eu.ggnet.dwoss.uniqueunit.eao.ProductEao;
import eu.ggnet.dwoss.uniqueunit.eao.UniqueUnitEao;
import eu.ggnet.dwoss.price.engine.PriceEngine;
import eu.ggnet.dwoss.price.engine.PriceEngineResult;
import eu.ggnet.dwoss.stock.entity.StockUnit;
import lombok.Data;
import static eu.ggnet.dwoss.price.engine.PriceEngineResult.Change.*;
import static eu.ggnet.dwoss.uniqueunit.entity.UniqueUnit.Identifier.REFURBISHED_ID;
/**
* Contains all business methods for price generaimport static de.dw.uniqueunit.entity.UniqueUnit.Identifier.REFURBISHED_ID;
* tion, import and export
*
* @author oliver.guenther
*/
@Stateless
public class PriceCoreOperation {
@Data
private static class SuperProduct {
private final Product product;
private ProductSpec spec;
}
@Inject
@UniqueUnits
private EntityManager uuEm;
@Inject
@Stocks
private EntityManager stockEm;
@Inject
@Specs
private EntityManager specEm;
@Inject
private PriceEngine priceEngine;
@Inject
private Mandator mandator;
private final Logger L = LoggerFactory.getLogger(PriceCoreOperation.class);
/**
* Loads all AVAILABLE SopoUnits from the Sopodb an puts them trough the PriceEngine
*
* @param monitor
* @return
*/
public List<PriceEngineResult> loadAndCalculate(IMonitor monitor) {
L.info("Starting loadAndCalculate()");
final SubMonitor m = SubMonitor.convert(monitor, 100);
m.start();
final StockUnitEao stockUnitEao = new StockUnitEao(stockEm);
final UniqueUnitEao uniqueUnitEao = new UniqueUnitEao(uuEm);
final ProductSpecEao productSpecEao = new ProductSpecEao(specEm);
m.message("loading Units");
List<Integer> uuids = stockUnitEao.findByNoLogicTransactionAsUniqueUnitId();
List<UniqueUnit> uus = uniqueUnitEao.findByIds(uuids);
m.worked(10, "updating Eols");
updateEols(uus);
m.worked(5, "loading ProductSpecs");
Set<Product> products = toProducts(uus);
List<ProductSpec> productSpecs = productSpecEao.findByProductIds(toProductIds(products));
Map<Product, ProductSpec> productToSpecs = toProductProductSpec(products, productSpecs);
m.worked(10);
final List<PriceEngineResult> pers = new ArrayList<>();
m.setWorkRemaining(uus.size() + 5);
for (UniqueUnit uu : uus) {
m.worked(1, "Calculating RefurbishId(" + uu.getRefurbishId() + ")");
StockUnit su = stockUnitEao.findByUniqueUnitId(uu.getId());
pers.add(priceEngine.estimate(uu, productToSpecs.get(uu.getProduct()), su.getStock() != null ? su.getStock().getName() : "kein Lager"));
}
m.finish();
L.info("Finished loadAndCalculate(), estimated {} Units", pers.size());
return pers;
}
/**
* Stores the supplied Prices to units and the manufacturerPartNoPriceFixeds
*
* @param pers results to store
* @param comment a comment for the price history
* @param arranger a arranger for the price history
* @param monitor an optional monitor
*/
public void store(final List<PriceEngineResult> pers, String comment, String arranger, IMonitor monitor) {
final SubMonitor m = SubMonitor.convert(monitor, pers.size() + 27);
UniqueUnitEao uniqueUnitEao = new UniqueUnitEao(uuEm);
ProductEao productEao = new ProductEao(uuEm);
// preload sopo and unique units
m.message("Preloading UniqueUnits");
NavigableMap<String, UniqueUnit> uniqueUnits = UniqueUnit.asMapByRefurbishId(uniqueUnitEao.findByIdentifiers(REFURBISHED_ID, PriceEngineResult.toRefurbishIds(pers)));
m.worked(5);
for (PriceEngineResult per : pers) {
String msg = "Storing Unit " + per.getRefurbishedId() + " HP:" + per.getRetailerPrice()
+ " EP:" + per.getCustomerPrice() + " UnitFix:" + per.getUnitPriceFixed() + " ProductFix:" + per.getManufacturerPartPriceFixed();
L.info(msg);
m.worked(1, msg);
update(uniqueUnits.get(per.getRefurbishedId()), per, arranger, comment);
}
//Inferenced filtering for fixprices
Map<String, PriceEngineResult> fixPriceImports = new HashMap<>();
for (PriceEngineResult per : pers) {
if ( per.getManufacturerPartPriceFixed() == NO_CHANGE ) continue;
fixPriceImports.put(per.getManufacturerPartNo(), per);
}
m.worked(1, "Perloading Products");
NavigableMap<String, Product> products = Product.asMapByPartNos(productEao.findByPartNos(PriceEngineResult.toPartNos(pers)));
m.worked(3);
m.setWorkRemaining(fixPriceImports.size());
for (PriceEngineResult per : fixPriceImports.values()) {
update(products.get(per.getManufacturerPartNo()), per, arranger, comment);
String msg = "Storing ProductDescription Fixed Price " + per.getProductName() + " Retailer:" + per.getRetailerPrice() + " Customer:" + per.getCustomerPrice() + " Manual:" + per.getManufacturerPartPriceFixed();
L.info(msg);
m.worked(1, msg);
}
m.finish();
}
private void update(UniqueUnit uniqueUnit, PriceEngineResult per, String arranger, String comment) {
if ( uniqueUnit == null ) return;
if ( per.getUnitPriceFixed() == SET ) {
uniqueUnit.addFlag(UniqueUnit.Flag.PRICE_FIXED);
} else if ( per.getUnitPriceFixed() == UNSET ) {
uniqueUnit.removeFlag(UniqueUnit.Flag.PRICE_FIXED);
}
String type = "estimated";
if ( per.getUnitPriceFixed() == SET ) type = "unitfixed";
/* TODO: This is incomplete, because I only get new fixed infos.
* If a product was fixed in a run before, this is type estimated.
* Whould need to import this information allso. (Need to change the report itself and the export) */
if ( per.getManufacturerPartPriceFixed() == SET ) type = "productfixed";
uniqueUnit.setPrice(eu.ggnet.dwoss.uniqueunit.entity.PriceType.CUSTOMER, per.getCustomerPrice(), arranger + " - " + type + ", " + comment);
uniqueUnit.setPrice(eu.ggnet.dwoss.uniqueunit.entity.PriceType.RETAILER, per.getRetailerPrice(), arranger + " - " + type + ", " + comment);
uniqueUnit.setWarranty(Warranty.values()[per.getWarrantyId()]);
}
private void update(Product product, PriceEngineResult per, String arranger, String comment) {
if ( product == null ) return;
product.setPrice(PriceType.RETAILER, per.getRetailerPrice(), arranger + " - productfix," + comment);
product.setPrice(PriceType.CUSTOMER, per.getCustomerPrice(), arranger + " - productfix," + comment);
if ( per.getManufacturerPartPriceFixed() == SET ) product.addFlag(Product.Flag.PRICE_FIXED);
if ( per.getManufacturerPartPriceFixed() == UNSET ) product.removeFlag(Product.Flag.PRICE_FIXED);
}
private Set<Product> toProducts(List<UniqueUnit> uus) {
Set<Product> products = new HashSet<>();
for (UniqueUnit uniqueUnit : uus) {
products.add(uniqueUnit.getProduct());
}
return products;
}
private Set<Long> toProductIds(Set<Product> products) {
Set<Long> productIds = new HashSet<>();
for (Product product : products) {
productIds.add(product.getId());
}
return productIds;
}
private Map<Product, ProductSpec> toProductProductSpec(Set<Product> products, List<ProductSpec> productSpecs) {
Map<Product, ProductSpec> productToSpecs = new HashMap<>();
for (Product product : products) {
for (ProductSpec productSpec : productSpecs) {
if ( product.getId() == productSpec.getProductId() ) {
productToSpecs.put(product, productSpec);
break;
}
}
}
return productToSpecs;
}
private void updateEols(List<UniqueUnit> uus) {
Map<Product, Date> productEols = new HashMap<>();
for (UniqueUnit uu : uus) {
if ( !productEols.containsKey(uu.getProduct()) || productEols.get(uu.getProduct()).before(uu.getMfgDate()) ) {
productEols.put(uu.getProduct(), uu.getMfgDate());
}
}
for (Entry<Product, Date> entry : productEols.entrySet()) {
Date eol = DateUtils.addMonths(entry.getValue(), 3);
if ( entry.getKey().getEol() == null ) entry.getKey().setEol(eol);
else if ( entry.getKey().getEol().before(eol) ) entry.getKey().setEol(eol);
}
}
}