/* jBilling - The Enterprise Open Source Billing System Copyright (C) 2003-2011 Enterprise jBilling Software Ltd. and Emiliano Conde This file is part of jbilling. jbilling is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jbilling 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with jbilling. If not, see <http://www.gnu.org/licenses/>. */ package com.sapienter.jbilling.server.order; import com.sapienter.jbilling.common.CommonConstants; import org.apache.log4j.Logger; import com.sapienter.jbilling.server.item.ItemBL; import com.sapienter.jbilling.server.item.db.ItemDTO; import com.sapienter.jbilling.server.order.db.OrderDAS; import com.sapienter.jbilling.server.order.db.OrderDTO; import com.sapienter.jbilling.server.order.db.OrderLineDTO; import com.sapienter.jbilling.server.user.UserBL; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * * @author emilc */ public class OrderLineBL { private static final Logger LOG = Logger.getLogger(OrderLineBL.class); public static List<OrderLineDTO> diffOrderLines(List<OrderLineDTO> lines1, List<OrderLineDTO> lines2) { List<OrderLineDTO> diffLines = new ArrayList<OrderLineDTO>(); Collections.sort(lines1, new Comparator<OrderLineDTO>() { public int compare(OrderLineDTO a, OrderLineDTO b) { return new Integer(a.getId()).compareTo(b.getId()); } }); for (OrderLineDTO line : lines2) { int index = Collections.binarySearch(lines1, line, new Comparator<OrderLineDTO>() { public int compare(OrderLineDTO a, OrderLineDTO b) { return new Integer(a.getId()).compareTo(b.getId()); } }); if (index >= 0) { // existing line OrderLineDTO diffLine = new OrderLineDTO(lines1.get(index)); // will fail if amounts or quantities are null... diffLine.setAmount(line.getAmount().subtract(diffLine.getAmount())); diffLine.setQuantity(line.getQuantity().subtract(diffLine.getQuantity())); if (BigDecimal.ZERO.compareTo(diffLine.getAmount()) != 0 || BigDecimal.ZERO.compareTo(diffLine.getQuantity()) != 0) { diffLines.add(diffLine); } } else { // new line diffLines.add(new OrderLineDTO(line)); } } LOG.debug("Diff lines are " + diffLines); return diffLines; } public static List<OrderLineDTO> copy(List<OrderLineDTO> lines) { List<OrderLineDTO> retValue = new ArrayList<OrderLineDTO>(lines.size()); for (OrderLineDTO line : lines) { retValue.add(new OrderLineDTO(line)); } return retValue; } public static void addLine(OrderDTO order, OrderLineDTO line, boolean persist) { if (persist) throw new IllegalArgumentException("persist is oboleted"); // TODO remove the argument UserBL user = new UserBL(order.getUserId()); OrderLineDTO oldLine = order.getLine(line.getItemId()); if (oldLine != null) { // get a copy of the old line oldLine = new OrderLineDTO(oldLine); } addItem(line.getItemId(), line.getQuantity(), user.getLanguage(), order.getUserId(), user.getEntity().getEntity().getId(), order.getCurrencyId(), order, line, persist); if (persist) { // generate NewQuantityEvent OrderLineDTO newLine = order.getLine(line.getItemId()); OrderBL orderBl = new OrderBL(); List<OrderLineDTO> oldLines = new ArrayList<OrderLineDTO>(1); List<OrderLineDTO> newLines = new ArrayList<OrderLineDTO>(1); if (oldLine != null) { oldLines.add(oldLine); } newLines.add(newLine); LOG.debug("Old line: " + oldLine); LOG.debug("New line: " + newLine); orderBl.checkOrderLineQuantities(oldLines, newLines, user.getEntity().getEntity().getId(), order.getId(), true); } } /** * Adds a single item (quantity 1) to the given order for the given item id. * This method will not force persistence of the added item, instead changes will * be persisted when the transaction ends. * * @param order order to add item to * @param itemId id of item to add */ public static void addItem(OrderDTO order, Integer itemId) { addItem(order, itemId, false); } /** * Adds a single item (quantity 1) to the given order for the given item id. * * @param order order to add item to * @param itemId id of item to add * @param persist save changes immediately if true */ public static void addItem(OrderDTO order, Integer itemId, boolean persist) { addItem(order, itemId, new BigDecimal(1), persist); } /** * Adds a quantity of items to the given order for the given item id. * This method will not force persistence of the added item, instead changes will * be persisted when the transaction ends. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add */ public static void addItem(OrderDTO order, Integer itemId, Integer quantity) { addItem(order, itemId, quantity, false); } /** * Adds a quantity of items to the given order for the given item id. * Use the given price for the addition. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add */ public static void addItem(OrderDTO order, Integer itemId, Integer quantity, BigDecimal price) { UserBL user = new UserBL(order.getUserId()); OrderLineDTO line = new OrderLineDTO(); line.setItemId(itemId); line.setQuantity(quantity); line.setPrice(price); addItem(itemId, new BigDecimal(quantity), user.getLanguage(), order.getUserId(), user.getEntity().getEntity().getId(), order.getCurrencyId(), order, line, false); } /** * Adds a quantity of items to the given order for the given item id. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add * @param persist save changes immediately if true */ public static void addItem(OrderDTO order, Integer itemId, Integer quantity, boolean persist) { addItem(order, itemId, new BigDecimal(quantity), persist); } /** * Adds a quantity of items to the given order for the given item id. * This method will not force persistence of the added item, instead changes will * be persisted when the transaction ends. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add */ public static void addItem(OrderDTO order, Integer itemId, BigDecimal quantity) { addItem(order, itemId, quantity, false); } /** * Adds a quantity of items to the given order for the given item id. * * @param order order to add item to * @param itemId id of item to add * @param quantity quantity to add * @param persist save changes immediately if true */ public static void addItem(OrderDTO order, Integer itemId, BigDecimal quantity, boolean persist) { UserBL user = new UserBL(order.getUserId()); addItem(itemId, quantity, user.getLanguage(), order.getUserId(), user.getEntity().getEntity().getId(), order.getCurrencyId(), order, null, persist); } public static void addItem(Integer itemID, BigDecimal quantity, Integer language, Integer userId, Integer entityId, Integer currencyId, OrderDTO newOrder, OrderLineDTO myLine, boolean persist) { if (persist) throw new IllegalArgumentException("persist is oboleted"); // TODO remove the argument // check if the item is already in the order OrderLineDTO line = (OrderLineDTO) newOrder.getLine(itemID); if (myLine == null) { myLine = new OrderLineDTO(); ItemDTO item = new ItemDTO(); item.setId(itemID); myLine.setItem(item); myLine.setQuantity(quantity); } populateWithSimplePrice(language, userId, entityId, currencyId, itemID, myLine, CommonConstants.BIGDECIMAL_SCALE); myLine.setDefaults(); // create a new line if an existing line does not exist // if the line has a different description than the existing, treat it as a new line if (line == null || (myLine.getDescription() != null && !myLine.getDescription().equals(line.getDescription()))) { OrderLineDTO newLine = new OrderLineDTO(myLine); newOrder.getLines().add(newLine); newLine.setPurchaseOrder(newOrder); // save the order (with the new line). Otherwise // the diff line will have a '0' for the order id and the // saving of the mediation record lines gets really complicated if (persist) { new OrderDAS().save(newOrder); } } else { // the item is there, I just have to update the quantity line.setQuantity(line.getQuantity().add(quantity)); // and also the total amount for this order line line.setAmount(line.getAmount().add(myLine.getAmount())); } } /** * Returns an order line with everything correctly * initialized. It does not call plug-ins to set the price * @param language * @param userId * @param entityId * @param currencyId * @param precision * @return */ public static void populateWithSimplePrice(Integer language, Integer userId, Integer entityId, Integer currencyId, Integer itemId, OrderLineDTO line, Integer precision) { ItemBL itemBl = new ItemBL(itemId); ItemDTO item = itemBl.getEntity(); // it takes the line type of the first category this item belongs too... // TODO: redo, when item categories are redone Integer type = item.getItemTypes().iterator().next().getOrderLineTypeId(); Boolean editable = OrderBL.lookUpEditable(type); if (line.getDescription() == null) { line.setDescription(item.getDescription(language)); } if (line.getQuantity() == null) { line.setQuantity(new BigDecimal(1)); } if (line.getPrice() == null) { line.setPrice((item.getPercentage() == null) ? itemBl.getPriceByCurrency(item, userId, currencyId) // basic price, ignoring current usage and : item.getPercentage()); // and quantity purchased for price calculations } if (line.getAmount() == null) { BigDecimal additionAmount = null; if (item.getPercentage() == null) { // normal price, multiply by quantity additionAmount = line.getPrice(); additionAmount = additionAmount.multiply(line.getQuantity()); } else { // percentage ignores the quantity additionAmount = item.getPercentage(); } line.setAmount(additionAmount.setScale(precision, CommonConstants.BIGDECIMAL_ROUND)); } line.setCreateDatetime(null); line.setDeleted(0); line.setTypeId(type); line.setEditable(editable); line.setItem(item); } }