/* 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.item.tasks; import java.math.BigDecimal; import java.util.Collection; import java.util.Date; import org.apache.log4j.Logger; import org.drools.KnowledgeBase; import org.drools.runtime.rule.FactHandle; import com.sapienter.jbilling.common.Constants; import com.sapienter.jbilling.server.item.ItemBL; import com.sapienter.jbilling.server.item.PricingField; import com.sapienter.jbilling.server.item.db.ItemDTO; import com.sapienter.jbilling.server.mediation.Record; import com.sapienter.jbilling.server.order.OrderBL; import com.sapienter.jbilling.server.order.db.OrderDAS; import com.sapienter.jbilling.server.order.db.OrderDTO; import com.sapienter.jbilling.server.order.db.OrderLineDAS; import com.sapienter.jbilling.server.order.db.OrderLineDTO; import com.sapienter.jbilling.server.order.db.OrderStatusDAS; import com.sapienter.jbilling.server.pluggableTask.TaskException; import com.sapienter.jbilling.server.user.ContactBL; import com.sapienter.jbilling.server.user.ContactDTOEx; import com.sapienter.jbilling.server.user.UserDTOEx; import com.sapienter.jbilling.server.user.contact.db.ContactFieldDTO; import com.sapienter.jbilling.server.util.DTOFactory; import com.sapienter.jbilling.server.util.db.CurrencyDAS; import java.util.ArrayList; import java.util.List; public class RulesItemManager extends BasicItemManager { private static final Logger LOG = Logger.getLogger(RulesItemManager.class); protected OrderManager helperOrder = null; private List<Record> records; public void addItem(Integer itemID, BigDecimal quantity, Integer language, Integer userId, Integer entityId, Integer currencyId, OrderDTO order, List<Record> records) throws TaskException { super.addItem(itemID, quantity, language, userId, entityId, currencyId, order, records); this.records = records; helperOrder = new OrderManager(order, language, userId, entityId, currencyId); processRules(order); } protected void processRules(OrderDTO newOrder) throws TaskException { // now we have the line with good defaults, the order and the item // These have to be visible to the rules KnowledgeBase knowledgeBase; try { knowledgeBase = readKnowledgeBase(); } catch (Exception e) { throw new TaskException(e); } session = knowledgeBase.newStatefulKnowledgeSession(); List<Object> rulesMemoryContext = new ArrayList<Object>(); rulesMemoryContext.add(helperOrder); // add OrderDTO to rules memory context newOrder.setCurrency(new CurrencyDAS().find(newOrder.getCurrency().getId())); if (newOrder.getCreateDate() == null) { newOrder.setCreateDate(new Date()); } rulesMemoryContext.add(newOrder); for (OrderLineDTO line : newOrder.getLines()) { if (line.getItem() != null) { ItemBL item = new ItemBL(line.getItemId()); rulesMemoryContext.add(item.getDTO(helperOrder.getLanguage(), helperOrder.getUserId(), helperOrder.getEntityId(), helperOrder.getCurrencyId())); } rulesMemoryContext.add(line); } if (newOrder.getPricingFields() != null && newOrder.getPricingFields().size() > 0) { for (PricingField pf : newOrder.getPricingFields()) { rulesMemoryContext.add(pf); } } try { Integer userId = newOrder.getBaseUserByUserId().getId(); UserDTOEx user = DTOFactory.getUserDTOEx(userId); rulesMemoryContext.add(user); ContactBL contact = new ContactBL(); contact.set(userId); ContactDTOEx contactDTO = contact.getDTO(); rulesMemoryContext.add(contactDTO); for (ContactFieldDTO field : (Collection<ContactFieldDTO>) contactDTO.getFieldsTable().values()) { rulesMemoryContext.add(field); } // Add the subscriptions OrderBL order = new OrderBL(); for (OrderDTO myOrder : order.getActiveRecurringByUser(userId)) { for (OrderLineDTO myLine : myOrder.getLines()) { rulesMemoryContext.add(new Subscription(myLine)); } } } catch (Exception e) { throw new TaskException(e); } session.setGlobal("order", helperOrder); // then execute the rules executeStatefulRules(session, rulesMemoryContext); } public class OrderManager { private OrderDTO order = null; private Integer language = null; private Integer userId = null; private Integer entityId = null; private Integer currencyId = null; public OrderManager(OrderDTO order, Integer language, Integer userId, Integer entityId, Integer currencyId) { this.order = order; this.language = language; this.userId = userId; this.entityId = entityId; this.currencyId = currencyId; } public OrderDTO getOrder() { return order; } public void setOrder(OrderDTO order) { this.order = order; } public OrderLineDTO addItem(Integer itemID, Integer quantity) throws TaskException { return addItem(itemID, new BigDecimal(quantity)); } public OrderLineDTO addItem(Integer itemID, BigDecimal quantity) throws TaskException { LOG.debug("Adding item " + itemID + " q: " + quantity); BasicItemManager helper = new BasicItemManager(); OrderLineDTO oldLine = order.getLine(itemID); FactHandle h = null; if (oldLine != null) { h = handlers.get(oldLine); } helper.addItem(itemID, quantity, language, userId, entityId, currencyId, order, records); OrderLineDTO retValue = helper.getLatestLine(); if (h != null) { LOG.debug("updating"); session.update(h, retValue); } else { LOG.debug("inserting"); handlers.put(retValue, session.insert(retValue)); } LOG.debug("Now order line is " + retValue + " hash: " + retValue.hashCode()); return retValue; } public OrderLineDTO addItem(Integer itemId) throws TaskException { return addItem(itemId, 1); } public void removeItem(Integer itemId) { removeObject(order.getLine(itemId)); order.removeLine(itemId); } /** * Adds or updates an order line. It will calculate the amount to add based on the * price of the itemBase, using the price as a percentage of the itemId * @param itemId * @param itemBase * @return * @throws TaskException */ public OrderLineDTO percentageIncrease(Integer itemId, Integer itemBase) throws TaskException { OrderLineDTO updateLine = order.getLine(itemId); if (updateLine == null) { // no luck, create a new one updateLine = addItem(itemId); updateLine.setAmount(BigDecimal.ZERO); // starts from 0 updateLine.setTotalReadOnly(true); } // now add the amount based on the itemBase ItemBL item = new ItemBL(itemBase); ItemDTO itemDto = item.getDTO(language, userId, entityId, currencyId); BigDecimal percentage = updateLine.getItem().getPercentage(); BigDecimal base = itemDto.getPrice(); BigDecimal result = base.divide(new BigDecimal("100"), Constants.BIGDECIMAL_SCALE, Constants.BIGDECIMAL_ROUND) .multiply(percentage).add(updateLine.getAmount()); updateLine.setAmount(result); return updateLine; } /** * Adds or updates an order line. Calculates a percentage item order * line amount based on the amount of another order line. This is added * to the existing percentage order line's amount. */ public OrderLineDTO percentageOnOrderLine(Integer percentageItemId, OrderLineDTO line) throws TaskException { // try to get percentage item order line OrderLineDTO percentageLine = order.getLine(percentageItemId); if (percentageLine == null) { // add percentage item percentageLine = addItem(percentageItemId); percentageLine.setAmount(BigDecimal.ZERO); percentageLine.setTotalReadOnly(true); } // now add the percentage amount based on the order line item amount BigDecimal percentage = percentageLine.getItem().getPercentage(); BigDecimal base = line.getPrice().multiply(line.getQuantity()); BigDecimal result = base.divide(new BigDecimal("100"), Constants.BIGDECIMAL_SCALE, Constants.BIGDECIMAL_ROUND).multiply(percentage).add(percentageLine.getAmount()); percentageLine.setAmount(result); return percentageLine; } public OrderDTO createOrder(Integer itemId, Double quantity) throws TaskException { BigDecimal quant = new BigDecimal(quantity).setScale(Constants.BIGDECIMAL_SCALE, Constants.BIGDECIMAL_ROUND); return createOrder(itemId, quant); } public OrderDTO createOrder(Integer itemId, BigDecimal quantity) throws TaskException { // copy the current order OrderDTO newOrder = new OrderDTO(order); newOrder.setId(0); newOrder.setVersionNum(null); // the period needs to be in the session newOrder.setOrderPeriodId(order.getOrderPeriod().getId()); // the status should be active newOrder.setOrderStatus(new OrderStatusDAS().find(Constants.ORDER_STATUS_ACTIVE)); // but without the lines newOrder.getLines().clear(); // but do get the new line in OrderManager helper = new OrderManager(newOrder, language, userId, entityId, currencyId); OrderLineDTO newLine = helper.addItem(itemId, quantity); newLine.setPurchaseOrder(newOrder); newLine.setDefaults(); return new OrderDAS().save(newOrder); } public void removeOrder(Integer itemId) { List<OrderLineDTO> list = new OrderLineDAS().findByUserItem( order.getBaseUserByUserId().getId(), itemId); for (OrderLineDTO line : list) { LOG.debug("Deleting order " + line.getPurchaseOrder().getId()); // who is the executor? we'll use the owner.. she is cancelling new OrderBL(line.getPurchaseOrder()).delete(order.getBaseUserByUserId().getId()); } } public Integer getCurrencyId() { return currencyId; } public Integer getEntityId() { return entityId; } public Integer getLanguage() { return language; } public Integer getUserId() { return userId; } } }