static void updateStatusFinished(
      OrderDTO order, Date startOfBillingPeriod, Date endOfBillingPeriod)
      throws SessionInternalError {

    // all one timers are done
    if (order.getOrderPeriod().getId() == Constants.ORDER_PERIOD_ONCE) {
      OrderBL orderBL = new OrderBL(order);
      orderBL.setStatus(null, Constants.ORDER_STATUS_FINISHED);
    } else { // recursive orders get more complicated
      // except those that are immortal :)
      if (order.getActiveUntil() == null) {
        return;
      }
      // see until when the incoming process will cover
      // compare if this is after the order exipres
      Logger log = Logger.getLogger(BillingProcessBL.class);
      log.debug(
          "order "
              + order.getId()
              + "end of bp "
              + endOfBillingPeriod
              + " active until "
              + order.getActiveUntil());
      if (endOfBillingPeriod.compareTo(Util.truncateDate(order.getActiveUntil())) >= 0) {
        OrderBL orderBL = new OrderBL(order);
        orderBL.setStatus(null, Constants.ORDER_STATUS_FINISHED);
      }
    }
  }
 public static void updateNextBillableDay(OrderDTO order, Date end) throws SessionInternalError {
   // if this order won't be process ever again, the
   // it shouldn't have a next billable day
   if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) {
     order.setNextBillableDay(null);
   } else {
     order.setNextBillableDay(end);
   }
 }
    public void removeOrder(Integer itemId) {
      List<OrderLineDTO> list =
          new OrderLineDAS().findByUserItem(order.getBaseUserByUserId().getId(), itemId);

      for (OrderLineDTO line : list) {
        LOG.debug("Deleting order %s", line.getPurchaseOrder().getId());

        // who is the executor? we'll use the owner.. she is cancelling
        new OrderBL(line.getPurchaseOrder()).delete(order.getBaseUserByUserId().getId());
      }
    }
 /**
  * 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(),
       order.getCurrencyId(),
       order,
       null,
       persist);
 }
  private static void addItem(
      Integer itemID,
      BigDecimal quantity,
      Integer language,
      Integer userId,
      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(
        newOrder, myLine, language, userId, currencyId, itemID, 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()
              .setScale(CommonConstants.BIGDECIMAL_SCALE, CommonConstants.BIGDECIMAL_ROUND));
      line.setAmount(line.getAmount().add(myLine.getAmount()));
    }
  }
  private void createOrderProcess(
      NewInvoiceDTO newInvoice, InvoiceDTO invoice, BillingProcessDTO process, Integer origin)
      throws SessionInternalError {
    LOG.debug("Generating order process records...");
    // update the orders involved, now that their old data is not needed
    // anymore
    for (int f = 0; f < newInvoice.getOrders().size(); f++) {

      OrderDTO order = (OrderDTO) newInvoice.getOrders().get(f);

      LOG.debug(" ... order " + order.getId());
      // this will help later
      List<PeriodOfTime> periodsList = newInvoice.getPeriods().get(f);
      Date startOfBillingPeriod = (Date) periodsList.get(0).getStart();
      Date endOfBillingPeriod = periodsList.get(periodsList.size() - 1).getEnd();
      Integer periods = (Integer) newInvoice.getPeriods().get(f).size();

      // We don't update orders if this is just a review
      if (newInvoice.getIsReview().intValue() == 0) {
        // update the to_process if applicable
        updateStatusFinished(order, startOfBillingPeriod, endOfBillingPeriod);

        // update this order process field
        updateNextBillableDay(order, endOfBillingPeriod);
      }

      // create the period and update the order-invoice relationship
      try {

        OrderProcessDAS das = new OrderProcessDAS();
        OrderProcessDTO orderProcess = new OrderProcessDTO();
        orderProcess.setPeriodStart(startOfBillingPeriod);
        orderProcess.setPeriodEnd(endOfBillingPeriod);
        orderProcess.setIsReview(newInvoice.getIsReview());
        orderProcess.setPurchaseOrder(order);
        InvoiceDAS invDas = new InvoiceDAS();
        orderProcess.setInvoice(invDas.find(invoice.getId()));
        BillingProcessDAS proDas = new BillingProcessDAS();
        orderProcess.setBillingProcess(process != null ? proDas.find(process.getId()) : null);
        orderProcess.setPeriodsIncluded(periods);
        orderProcess.setOrigin(origin);
        orderProcess = das.save(orderProcess);
        LOG.debug(
            "created order process id " + orderProcess.getId() + " for order " + order.getId());

      } catch (Exception e) {
        throw new SessionInternalError(e);
      }
    }
  }
 /**
  * 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(),
       order.getCurrencyId(),
       order,
       line,
       false);
 }
    public OrderLineDTO addItem(Integer itemID, BigDecimal quantity) throws TaskException {
      LOG.debug("Adding item %s q: %s", itemID, 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,
          lines,
          false,
          null,
          null);
      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 %s , hash: %s", retValue, retValue.hashCode());
      return retValue;
    }
  private boolean isOrderProvisionable(OrderDTO order) {
    if (order != null) {

      Date today = new Date();

      if (order.getOrderStatus() != null
          && order.getOrderStatus().getId() == Constants.ORDER_STATUS_ACTIVE) {
        if (order.getActiveSince() != null
            && order.getActiveSince().before(today)
            && order.getActiveUntil() != null
            && order.getActiveUntil().after(today)) {

          return true;
        }
      }
    }

    return false;
  }
  public void process(Event event) throws PluggableTaskException {
    if (event instanceof SubscriptionActiveEvent) {
      SubscriptionActiveEvent activeEvent = (SubscriptionActiveEvent) event;

      LOG.debug("Processing order " + activeEvent.getOrder().getId() + " subscription activation");
      doActivate(activeEvent.getOrder(), activeEvent.getOrder().getLines());

    } else if (event instanceof SubscriptionInactiveEvent) {
      SubscriptionInactiveEvent inactiveEvent = (SubscriptionInactiveEvent) event;

      LOG.debug(
          "Processing order " + inactiveEvent.getOrder().getId() + " subscription deactivation");
      doDeactivate(inactiveEvent.getOrder(), inactiveEvent.getOrder().getLines());

    } else if (event instanceof NewQuantityEvent) {
      NewQuantityEvent quantityEvent = (NewQuantityEvent) event;

      if (BigDecimal.ZERO.compareTo(quantityEvent.getOldQuantity()) != 0
          && BigDecimal.ZERO.compareTo(quantityEvent.getNewQuantity()) != 0) {
        LOG.debug("Order line quantities did not change, no provisioning necessary.");
        return;
      }

      OrderDTO order = new OrderBL(quantityEvent.getOrderId()).getEntity();
      if (!isOrderProvisionable(order)) {
        LOG.warn("Order is not active and cannot be provisioned.");
        return;
      }

      if (BigDecimal.ZERO.compareTo(quantityEvent.getOldQuantity()) == 0) {
        LOG.debug("Processing order " + order.getId() + " activation");
        doActivate(order, Arrays.asList(quantityEvent.getOrderLine()));
      }

      if (BigDecimal.ZERO.compareTo(quantityEvent.getNewQuantity()) == 0) {
        LOG.debug("Processing order " + order.getId() + " deactivation");
        doDeactivate(order, Arrays.asList(quantityEvent.getOrderLine()));
      }

    } else {
      throw new PluggableTaskException("Cannot process event " + event);
    }
  }
  /**
   * Returns an order line with everything correctly initialized. It does not call plug-ins to set
   * the price
   *
   * @param order
   * @param language
   * @param userId
   * @param currencyId
   * @param precision
   * @return
   */
  private static void populateWithSimplePrice(
      OrderDTO order,
      OrderLineDTO line,
      Integer language,
      Integer userId,
      Integer currencyId,
      Integer itemId,
      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) {
      BigDecimal price = item.getPercentage();
      if (price == null) {
        Date pricingDate = order != null ? order.getPricingDate() : null;
        price =
            itemBl.getPriceByCurrency(
                pricingDate, item, userId, currencyId); // basic price, ignoring current usage and
        // and quantity purchased for price calculations
      }
      line.setPrice(price);
    }

    if (line.getAmount() == null) {
      BigDecimal additionAmount = item.getPercentage(); // percentage ignores the quantity
      if (additionAmount == null) {
        // normal price, multiply by quantity
        additionAmount = line.getPrice().setScale(precision, CommonConstants.BIGDECIMAL_ROUND);
        additionAmount = additionAmount.multiply(line.getQuantity());
      }
      line.setAmount(additionAmount.setScale(precision, CommonConstants.BIGDECIMAL_ROUND));
    }

    line.setCreateDatetime(null);
    line.setDeleted(0);
    line.setTypeId(type);
    line.setEditable(editable);
    line.setItem(item);
  }
  protected void processRules(OrderDTO order, Integer userId) throws TaskException {

    rulesMemoryContext.add(order);

    // add OrderDTO to rules memory context
    order.setCurrency(new CurrencyDAS().find(order.getCurrency().getId()));
    if (order.getCreateDate() == null) {
      order.setCreateDate(new Date());
    }

    // needed for calls to 'rateOrder'
    if (order.getPricingFields() != null) {
      for (PricingField field : order.getPricingFields()) {
        rulesMemoryContext.add(field);
      }
    }

    try {
      UserDTOEx user = DTOFactory.getUserDTOEx(userId);
      rulesMemoryContext.add(user);
      ContactBL contact = new ContactBL();
      contact.set(userId);
      ContactDTOEx contactDTO = contact.getDTO();
      rulesMemoryContext.add(contactDTO);
    } catch (Exception e) {
      throw new TaskException(e);
    }

    executeRules();
  }
 @SuppressWarnings("unchecked")
 private void processOrderAddedOnInvoiceEvents(NewInvoiceDTO newInvoice, Integer entityId) {
   List<OrderDTO> orders = newInvoice.getOrders();
   List<List<PeriodOfTime>> periods = newInvoice.getPeriods();
   for (int i = 0; i < orders.size(); i++) {
     OrderDTO order = orders.get(i);
     Integer userId = findUserId(order);
     for (PeriodOfTime period : periods.get(i)) {
       LOG.info("Number of orders in map: " + newInvoice.getOrderTotalContributions().size());
       LOG.info("Map: " + newInvoice.getOrderTotalContributions());
       OrderAddedOnInvoiceEvent newEvent =
           new OrderAddedOnInvoiceEvent(
               entityId,
               userId,
               order,
               newInvoice.getOrderTotalContributions().get(order.getId()));
       newEvent.setStart(period.getStart());
       newEvent.setEnd(period.getEnd());
       EventManager.process(newEvent);
     }
   }
 }
  private Integer findUserId(OrderDTO order) {
    UserDTO user = order.getUser();

    // while this user has a parent and the flag is off, keep looking
    while (user.getCustomer().getParent() != null
        && (user.getCustomer().getInvoiceChild() == null
            || user.getCustomer().getInvoiceChild() == 0)) {
      // go up one level
      LOG.debug("finding parent for invoicing. Now " + user.getUserId());
      user = user.getCustomer().getParent().getBaseUser();
    }

    return user.getUserId();
  }
    /**
     * 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, 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(
                  new OrderStatusDAS().getDefaultOrderStatusId(OrderStatusFlag.INVOICE, entityId)));
      // 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 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(),
        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: %s", oldLine);
      LOG.debug("New line: %s", newLine);
      orderBl.checkOrderLineQuantities(
          oldLines, newLines, user.getEntity().getEntity().getId(), order.getId(), true);
    }
  }
  private LineItem getLineItem(
      Integer itemId, InvoiceLineDTO invoiceLine, String uniqueTrackingCode, Integer userId)
      throws TaskException {
    // Get the meta field names
    String secondaryZipCodeExtensionFieldname =
        getParameter(SECONDARY_ZIP_CODE_EXTN_FIELDNAME, "Secondary Zip code extension");
    String secondaryZipCodeFieldname =
        getParameter(SECONDARY_ZIP_CODE_FIELDNAME, "Secondary Zip code");
    String billingZipCodeFieldname =
        getParameter(BILLING_ZIP_CODE_FIELDNAME, "Billing Zip code extension");
    String regulatoryCodeFieldname = getParameter(REGULATORY_CODE_FIELDNAME, "Regulatory Code");
    String salesTypeCodeFieldname = getParameter(SALES_TYPE_CODE_FIELDNAME, "Sales Type Code");
    String taxExemptionCodeFieldname =
        getParameter(TAX_EXEMPTION_CODE_FIELDNAME, "Tax exemption code");
    String transactionTypeCodeFieldname =
        getParameter(TRANSACTION_TYPE_CODE_FIELDNAME, "Transaction Type Code");

    LineItem lineItem = new LineItem();
    lineItem.setBillToNumber(""); // TODO: need to be addressed ?
    String customerNumber = null;
    List<NewInvoiceContext.OrderContext> orders = invoice.getOrders();
    // We need to get the fresh item from the database because
    // the item in the invoiceLine doesn't yet contain meta fields.
    ItemDTO item = new ItemDAS().find(itemId);
    OrderDTO orderDTO = null;
    UserDTO invoiceToUser = null;
    for (NewInvoiceContext.OrderContext orderCtx : orders) {
      if (orderCtx.order.getId().intValue() == invoiceLine.getOrder().getId()) {
        orderDTO = orderCtx.order;
        break;
      }
    }

    if (null == orderDTO) {
      orderDTO = orders.get(0).order;
    }

    invoiceToUser = new UserDAS().find(userId);
    customerNumber = invoiceToUser.getCustomer().getId() + "";

    lineItem.setCustomerNumber(customerNumber);
    lineItem.setInvoiceNumber("JB" + uniqueTrackingCode);
    lineItem.setLineNumber(""); // TODO: need to be addressed ?
    lineItem.setOrigNumber(""); // TODO: need to be addressed ?

    MetaFieldValue<String> p2PPlus4 =
        invoiceToUser.getCustomer().getMetaField(secondaryZipCodeExtensionFieldname);
    if (p2PPlus4 != null) {
      lineItem.setP2PPlus4(p2PPlus4.getValue());
    } else {
      lineItem.setP2PPlus4("");
    }

    MetaFieldValue<String> p2PZipcode =
        invoiceToUser.getCustomer().getMetaField(secondaryZipCodeFieldname);
    if (p2PZipcode != null) {
      lineItem.setP2PZipcode(p2PZipcode.getValue());
    } else {
      lineItem.setP2PZipcode("");
    }

    MetaFieldValue<String> plus4 =
        invoiceToUser.getCustomer().getMetaField(billingZipCodeFieldname);
    if (plus4 != null) {
      lineItem.setPlus4(plus4.getValue());
    } else {
      lineItem.setPlus4("");
    }

    LOG.debug("Meta fields: p2PPlus4: %s, p2PZipcode: %s, plus4:%s", p2PPlus4, p2PZipcode, plus4);

    MetaFieldValue<String> regulatoryCode = null;
    regulatoryCode = item.getMetaField(regulatoryCodeFieldname);
    if (regulatoryCode == null
        || regulatoryCode.getValue() == null
        || regulatoryCode.getValue().isEmpty()) {
      lineItem.setRegulatoryCode("00");
    } else {
      lineItem.setRegulatoryCode(regulatoryCode.getValue());
    }

    lineItem.setRevenue(invoiceLine.getAmount().floatValue());

    MetaFieldValue<String> salesTypeCode = orderDTO.getMetaField(salesTypeCodeFieldname);
    if (salesTypeCode == null
        || salesTypeCode.getValue() == null
        || salesTypeCode.getValue().isEmpty()) {
      lineItem.setSalesTypeCode("R");
    } else {
      lineItem.setSalesTypeCode(salesTypeCode.getValue());
    }

    lineItem.setSeconds(
        invoiceLine.getQuantity() != null ? invoiceLine.getQuantity().intValue() : 0);
    List<String> taxExemptionCodeList = new ArrayList<String>();
    // First get the tax exemption code from the customer
    MetaFieldValue<String> taxExemptionCode =
        invoiceToUser.getCustomer().getMetaField(taxExemptionCodeFieldname);
    LOG.debug("Tax exemption code from customer: %s", taxExemptionCode);
    if (!(taxExemptionCode != null
        && taxExemptionCode.getValue() != null
        && !taxExemptionCode.getValue().isEmpty())) {
      taxExemptionCode = item.getMetaField(taxExemptionCodeFieldname);
      LOG.debug("Tax exemption code from product: %s", taxExemptionCode);
    }
    if (taxExemptionCode == null) {
      LOG.debug("Setting tax exemption code to be 00");
      taxExemptionCodeList.add("00");
    } else {
      taxExemptionCodeList.add(taxExemptionCode.getValue());
    }
    LOG.debug(
        "Meta fields: regulatoryCode: %s, salesTypeCode: %s, taxExemptionCode: %s",
        regulatoryCode, salesTypeCode, taxExemptionCode);
    lineItem.setTaxExemptionCodeList(taxExemptionCodeList);
    lineItem.setTaxIncludedCode("0");

    lineItem.setTermNumber("");

    // TODO: Need to check if trans date will be current date or based on data year and data month ?
    lineItem.setTransDate("07-10-2012");

    MetaFieldValue<String> transTypeCode = null;
    transTypeCode = item.getMetaField(transactionTypeCodeFieldname);

    if (transTypeCode == null
        || transTypeCode.getValue() == null
        || transTypeCode.getValue().isEmpty()) {
      throw new SessionInternalError(
          "No valid transaction type code found on the product",
          new String[] {"ItemDTOEx,transTypeCode,no.valid.transactionTypeCode.on.product"});
    }
    lineItem.setTransTypeCode(transTypeCode.getValue());
    lineItem.setUnits(invoiceLine.getQuantity() != null ? invoiceLine.getQuantity().intValue() : 0);
    lineItem.setUnitType("00");

    if (invoiceToUser.getContact().getPostalCode() != null
        && plus4 != null
        && plus4.getValue() != null
        && !plus4.getValue().isEmpty()) {
      lineItem.setZipcode(invoiceToUser.getContact().getPostalCode());
      lineItem.setTaxSitusRule("05");
    } else if (invoiceToUser.getContact().getPostalCode() != null
        && (plus4 == null || plus4.getValue() == null || plus4.getValue().isEmpty())) {
      lineItem.setZipcode(invoiceToUser.getContact().getPostalCode());
      lineItem.setPlus4("0000");
      lineItem.setTaxSitusRule("05");
    }
    return lineItem;
  }
Exemple #19
0
  /**
   * This will remove all the records (sql delete, not just flag them). It will also update the
   * related orders if applicable
   */
  public void delete(Integer executorId) throws SessionInternalError {
    if (invoice == null) {
      throw new SessionInternalError("An invoice has to be set before delete");
    }

    // prevent a delegated Invoice from being deleted
    if (invoice.getDelegatedInvoiceId() != null && invoice.getDelegatedInvoiceId().intValue() > 0) {
      SessionInternalError sie =
          new SessionInternalError("A carried forward Invoice cannot be deleted");
      sie.setErrorMessages(
          new String[] {"InvoiceDTO,invoice,invoice.error.fkconstraint," + invoice.getId()});
      throw sie;
    }
    // start by updating purchase_order.next_billable_day if applicatble
    // for each of the orders included in this invoice
    for (OrderProcessDTO orderProcess : (Collection<OrderProcessDTO>) invoice.getOrderProcesses()) {
      OrderDTO order = orderProcess.getPurchaseOrder();
      if (order.getNextBillableDay() == null) {
        // the next billable day doesn't need updating
        if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) {
          OrderBL orderBL = new OrderBL(order);
          orderBL.setStatus(null, Constants.ORDER_STATUS_ACTIVE);
        }
        continue;
      }
      // only if this invoice is the responsible for the order's
      // next billable day
      if (order.getNextBillableDay().equals(orderProcess.getPeriodEnd())) {
        order.setNextBillableDay(orderProcess.getPeriodStart());
        if (order.getStatusId().equals(Constants.ORDER_STATUS_FINISHED)) {
          OrderBL orderBL = new OrderBL(order);
          orderBL.setStatus(null, Constants.ORDER_STATUS_ACTIVE);
        }
      }
    }

    // go over the order process records again just to delete them
    // we are done with this order, delete the process row
    for (OrderProcessDTO orderProcess : (Collection<OrderProcessDTO>) invoice.getOrderProcesses()) {
      OrderDTO order = orderProcess.getPurchaseOrder();
      OrderProcessDAS das = new OrderProcessDAS();
      order.getOrderProcesses().remove(orderProcess);
      das.delete(orderProcess);
    }
    invoice.getOrderProcesses().clear();

    // get rid of the contact associated with this invoice
    try {
      ContactBL contact = new ContactBL();
      if (contact.setInvoice(invoice.getId())) {
        contact.delete();
      }
    } catch (Exception e1) {
      LOG.error("Exception deleting the contact of an invoice", e1);
    }

    // remove the payment link/s
    PaymentBL payment = new PaymentBL();
    Iterator<PaymentInvoiceMapDTO> it = invoice.getPaymentMap().iterator();
    while (it.hasNext()) {
      PaymentInvoiceMapDTO map = it.next();
      payment.removeInvoiceLink(map.getId());
      invoice.getPaymentMap().remove(map);
      // needed because the collection has changed
      it = invoice.getPaymentMap().iterator();
    }

    // log that this was deleted, otherwise there will be no trace
    if (executorId != null) {
      eLogger.audit(
          executorId,
          invoice.getBaseUser().getId(),
          Constants.TABLE_INVOICE,
          invoice.getId(),
          EventLogger.MODULE_INVOICE_MAINTENANCE,
          EventLogger.ROW_DELETED,
          null,
          null,
          null);
    }

    // before delete the invoice most delete the reference in table
    // PAYMENT_INVOICE
    new PaymentInvoiceMapDAS().deleteAllWithInvoice(invoice);

    Set<InvoiceDTO> invoices = invoice.getInvoices();
    if (invoices.size() > 0) {
      for (InvoiceDTO delegate : invoices) {
        // set status to unpaid as against carried
        delegate.setInvoiceStatus(new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID));
        // remove delegated invoice link
        delegate.setInvoice(null);
        getHome().save(delegate);
      }
    }

    // now delete the invoice itself
    getHome().delete(invoice);
    getHome().flush();
  }
  private boolean addOrderToInvoice(
      Integer entityId, OrderDTO order, NewInvoiceDTO newInvoice, Date processDate, int maxPeriods)
      throws SessionInternalError, TaskException, PluggableTaskException {
    // require the calculation of the period dates
    PluggableTaskManager taskManager =
        new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_ORDER_PERIODS);
    OrderPeriodTask optask = (OrderPeriodTask) taskManager.getNextClass();

    if (optask == null) {
      throw new SessionInternalError(
          "There has to be " + "one order period pluggable task configured");
    }
    Date start = optask.calculateStart(order);
    Date end = optask.calculateEnd(order, processDate, maxPeriods, start);
    List<PeriodOfTime> periods = optask.getPeriods();
    // there isn't anything billable from this order
    if (periods.size() == 0) {
      return false;
    }

    if (start != null && end != null && start.after(end)) {
      // how come it starts after it ends ???
      throw new SessionInternalError(
          "Calculated for "
              + "order "
              + order.getId()
              + " a period that"
              + " starts after it ends:"
              + start
              + " "
              + end);
    }

    // add this order to the invoice being created
    newInvoice.addOrder(order, start, end, periods);

    // prepaid orders shouldn't have to be included
    // past time.
    if (order.getBillingTypeId().compareTo(Constants.ORDER_BILLING_PRE_PAID) == 0
        && start != null
        && // it has to be recursive too
        processDate.after(start)) {

      eLogger.warning(
          entityId,
          order.getBaseUserByUserId().getId(),
          order.getId(),
          EventLogger.MODULE_BILLING_PROCESS,
          EventLogger.BILLING_PROCESS_UNBILLED_PERIOD,
          Constants.TABLE_PUCHASE_ORDER);

      LOG.warn("Order " + order.getId() + " is prepaid " + "but has past time not billed.");
    }

    // initialize the currency of the new invoice
    if (newInvoice.getCurrency() == null) {
      newInvoice.setCurrency(order.getCurrency());
    } else {
      // now we are not supporting orders with different
      // currencies in the same invoice. Later this could be done
      if (newInvoice.getCurrency().getId() != order.getCurrency().getId()) {
        throw new SessionInternalError(
            "Orders with different "
                + "currencies not supported in one invoice. "
                + "Currency = "
                + newInvoice.getCurrency().getId()
                + "order = "
                + order.getId());
      }
    }
    return true;
  }
  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);

      // 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);
  }
  private boolean processOrdersForUser(
      UserDTO user,
      Integer entityId,
      BillingProcessDTO process,
      boolean isReview,
      boolean onlyRecurring,
      boolean useProcessDateForInvoice,
      int maximumPeriods,
      Hashtable<TimePeriod, NewInvoiceDTO> newInvoices) {

    boolean includedOrders = false;
    Integer userId = user.getUserId();

    LOG.debug("Processing orders for user " + userId);

    // initialize the subaccounts iterator if this user is a parent
    Iterator subAccountsIt = null;
    if (user.getCustomer().getIsParent() != null
        && user.getCustomer().getIsParent().intValue() == 1) {
      UserBL parent = new UserBL(userId);
      subAccountsIt = parent.getEntity().getCustomer().getChildren().iterator();
    }

    // get the orders that might be processable for this user
    OrderDAS orderDas = new OrderDAS();
    Collection<OrderDTO> orders = orderDas.findByUser_Status(userId, Constants.ORDER_STATUS_ACTIVE);

    // go through each of them, and update the DTO if it applies
    for (OrderDTO order : orders) {
      LOG.debug("Processing order :" + order.getId());
      // apply any order processing filter pluggable task
      try {
        PluggableTaskManager taskManager =
            new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_ORDER_FILTER);
        OrderFilterTask task = (OrderFilterTask) taskManager.getNextClass();
        boolean isProcessable = true;
        while (task != null) {
          isProcessable = task.isApplicable(order, process);
          if (!isProcessable) {
            break; // no need to keep doing more tests
          }
          task = (OrderFilterTask) taskManager.getNextClass();
        }

        // include this order only if it complies with all the
        // rules
        if (isProcessable) {

          LOG.debug("Order processable");

          if (onlyRecurring) {
            if (order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE) {
              includedOrders = true;
              LOG.debug("It is not one-timer. " + "Generating invoice");
            }
          } else {
            includedOrders = true;
          }
          /*
           * now find if there is already an invoice being
           * generated for the given due date period
           */
          // find the due date that applies to this order
          OrderBL orderBl = new OrderBL();
          orderBl.set(order);
          TimePeriod dueDatePeriod = orderBl.getDueDate();
          // look it up in the hashtable
          NewInvoiceDTO thisInvoice = (NewInvoiceDTO) newInvoices.get(dueDatePeriod);
          if (thisInvoice == null) {
            LOG.debug(
                "Adding new invoice for period "
                    + dueDatePeriod
                    + " process date:"
                    + process.getBillingDate());
            // we need a new one with this period
            // define the invoice date
            thisInvoice = new NewInvoiceDTO();
            if (useProcessDateForInvoice) {
              thisInvoice.setDate(process.getBillingDate());
            } else {
              thisInvoice.setDate(
                  orderBl.getInvoicingDate(),
                  order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE);
            }
            thisInvoice.setIsReview(isReview ? new Integer(1) : new Integer(0));
            thisInvoice.setCarriedBalance(BigDecimal.ZERO);
            thisInvoice.setDueDatePeriod(dueDatePeriod);
          } else {
            LOG.debug("invoice found for period " + dueDatePeriod);
            if (!useProcessDateForInvoice) {
              thisInvoice.setDate(
                  orderBl.getInvoicingDate(),
                  order.getOrderPeriod().getId() != Constants.ORDER_PERIOD_ONCE);
            }
          }
          /*
           * The order periods plug-in might not add any period. This should not happen
           * but if it does, the invoice should not be included
           */
          if (addOrderToInvoice(
              entityId, order, thisInvoice, process.getBillingDate(), maximumPeriods)) {
            // add or replace
            newInvoices.put(dueDatePeriod, thisInvoice);
          }
          LOG.debug("After putting period there are " + newInvoices.size() + " periods.");

          // here it would be easy to update this order
          // to_process and
          // next_billable_time. I can't do that because these
          // fields
          // will be read by the following tasks, and they
          // will asume
          // they are not modified.
        }

      } catch (PluggableTaskException e) {
        LOG.fatal("Problems handling order filter task.", e);
        throw new SessionInternalError("Problems handling order filter task.");
      } catch (TaskException e) {
        LOG.fatal("Problems excecuting order filter task.", e);
        throw new SessionInternalError("Problems executing order filter task.");
      }
    } // for - all the orders for this user

    // see if there is any subaccounts to include in this invoice
    while (subAccountsIt != null) { // until there are no more subaccounts (subAccountsIt != null) {
      CustomerDTO customer = null;
      while (subAccountsIt.hasNext()) {
        customer = (CustomerDTO) subAccountsIt.next();
        if (customer.getInvoiceChild() == null || customer.getInvoiceChild().intValue() == 0) {
          break;
        } else {
          LOG.debug("Subaccount not included in parent's invoice " + customer.getId());
          customer = null;
        }
      }
      if (customer != null) {
        userId = customer.getBaseUser().getUserId();
        // if the child does not have any orders to invoice, this should
        // not affect the current value of includedOrders
        if (processOrdersForUser(
            customer.getBaseUser(),
            entityId,
            process,
            isReview,
            onlyRecurring,
            useProcessDateForInvoice,
            maximumPeriods,
            newInvoices)) {
          // if ANY child has orders to invoice, it is enough for includedOrders to be true
          includedOrders = true;
        }
        LOG.debug("Now processing subaccount " + userId);

      } else {
        subAccountsIt = null;
        LOG.debug("No more subaccounts to process");
      }
    }

    return includedOrders;
  }
  public InvoiceDTO[] generateInvoice(
      BillingProcessDTO process, UserDTO user, boolean isReview, boolean onlyRecurring)
      throws SessionInternalError {

    Integer userId = user.getUserId();
    Integer entityId = user.getEntity().getId();

    // get the configuration
    boolean useProcessDateForInvoice = true;
    int maximumPeriods = 1;
    boolean paymentApplication = false;
    try {
      ConfigurationBL config = new ConfigurationBL(process.getEntity().getId());
      useProcessDateForInvoice = config.getEntity().getInvoiceDateProcess() == 1;
      maximumPeriods = config.getEntity().getMaximumPeriods();
      paymentApplication = config.getEntity().getAutoPaymentApplication() == 1;
    } catch (Exception e) {
      // swallow exception
    }

    // this contains the generated invoices, one per due date
    // found in the applicable purchase orders.
    // The key is the object TimePeriod
    Hashtable<TimePeriod, NewInvoiceDTO> newInvoices = new Hashtable<TimePeriod, NewInvoiceDTO>();
    InvoiceDTO[] retValue = null;

    LOG.debug(
        "In generateInvoice for user " + userId + " process date:" + process.getBillingDate());

    /*
     * Go through the orders first
     * This method will recursively call itself to find sub-accounts in any
     * level
     */
    boolean includedOrders =
        processOrdersForUser(
            user,
            entityId,
            process,
            isReview,
            onlyRecurring,
            useProcessDateForInvoice,
            maximumPeriods,
            newInvoices);

    if (!includedOrders || newInvoices.size() == 0) {
      // check if invoices without orders are allowed
      PreferenceBL preferenceBL = new PreferenceBL();
      try {
        preferenceBL.set(entityId, Constants.PREFERENCE_ALLOW_INVOICES_WITHOUT_ORDERS);
      } catch (EmptyResultDataAccessException fe) {
        // use default
      }

      if (preferenceBL.getInt() == 0) {
        LOG.debug("No applicable orders. No invoice generated (skipping invoices).");
        return null;
      }
    }

    if (!isReview) {
      for (Map.Entry<TimePeriod, NewInvoiceDTO> newInvoiceEntry : newInvoices.entrySet()) {
        // process events before orders added to invoice
        processOrderToInvoiceEvents(newInvoiceEntry.getValue(), entityId);
      }
    }

    /*
     * Include those invoices that should've been paid
     * (or have negative balance, as credits)
     */
    LOG.debug("Considering overdue invoices");
    // find the invoice home interface
    InvoiceDAS invoiceDas = new InvoiceDAS();
    // any of the new invoices being created could hold the overdue invoices
    NewInvoiceDTO holder =
        newInvoices.isEmpty() ? null : (NewInvoiceDTO) newInvoices.elements().nextElement();

    Collection dueInvoices = invoiceDas.findWithBalanceByUser(user);
    LOG.debug("Processing invoices for user " + user.getUserId());
    // go through each of them, and update the DTO if it applies

    for (Iterator it = dueInvoices.iterator(); it.hasNext(); ) {
      InvoiceDTO invoice = (InvoiceDTO) it.next();
      if (invoice.getToProcess() == InvoiceDTO.DO_NOT_PROCESS) {
        LOG.debug("skipping invoice " + invoice.getId() + " because DO_NOT_PROCESS is set");
        continue;
      }
      LOG.debug("Processing invoice " + invoice.getId());
      // apply any invoice processing filter pluggable task
      try {
        PluggableTaskManager taskManager =
            new PluggableTaskManager(entityId, Constants.PLUGGABLE_TASK_INVOICE_FILTER);
        InvoiceFilterTask task = (InvoiceFilterTask) taskManager.getNextClass();
        boolean isProcessable = true;
        while (task != null) {
          isProcessable = task.isApplicable(invoice, process);
          if (!isProcessable) {
            break; // no need to keep doing more tests
          }
          task = (InvoiceFilterTask) taskManager.getNextClass();
        }

        // include this invoice only if it complies with all the rules
        if (isProcessable) {
          // check for an invoice
          if (holder == null) {
            // Since there are no new invoices (therefore no orders),
            // don't let invoices with positive balances generate
            // an invoice.
            if (BigDecimal.ZERO.compareTo(invoice.getBalance()) < 0) {
              continue;
            }

            // no invoice/s yet (no applicable orders), so create one
            holder = new NewInvoiceDTO();
            holder.setDate(process.getBillingDate());
            holder.setIsReview(isReview ? new Integer(1) : new Integer(0));
            holder.setCarriedBalance(BigDecimal.ZERO);
            holder.setInvoiceStatus(new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID));

            // need to set a due date, so use the order default
            OrderBL orderBl = new OrderBL();
            OrderDTO order = new OrderDTO();
            order.setBaseUserByUserId(user);
            orderBl.set(order);
            TimePeriod dueDatePeriod = orderBl.getDueDate();

            holder.setDueDatePeriod(dueDatePeriod);
            newInvoices.put(dueDatePeriod, holder);
          }

          InvoiceBL ibl = new InvoiceBL(invoice);
          holder.addInvoice(ibl.getDTO());
          // for those invoices wiht only overdue invoices, the
          // currency has to be initialized

          if (holder.getCurrency() == null) {
            holder.setCurrency(invoice.getCurrency());
          } else if (holder.getCurrency().getId() != invoice.getCurrency().getId()) {
            throw new SessionInternalError(
                "An invoice with different "
                    + "currency is not supported. "
                    + "Currency = "
                    + holder.getCurrency().getId()
                    + "invoice = "
                    + invoice.getId());
          }
          // update the amount of the new invoice that is due to
          // previously unpaid overdue invoices

          // carry the remaining balance, plus the previously carried balance to the new invoice
          BigDecimal balance =
              (invoice.getBalance() == null) ? BigDecimal.ZERO : invoice.getBalance();
          BigDecimal carried = balance.add(holder.getCarriedBalance());
          holder.setCarriedBalance(carried);
        }

        LOG.debug("invoice " + invoice.getId() + " result " + isProcessable);

      } catch (PluggableTaskException e) {
        LOG.fatal("Problems handling task invoice filter.", e);
        throw new SessionInternalError("Problems handling task invoice filter.");
      } catch (TaskException e) {
        LOG.fatal("Problems excecuting task invoice filter.", e);
        throw new SessionInternalError("Problems executing task invoice filter.");
      }
    }

    if (newInvoices.size() == 0) {
      // no orders or invoices for this invoice
      LOG.debug("No applicable orders or invoices. No invoice generated (skipping invoices).");
      return null;
    }

    try {
      retValue = new InvoiceDTO[newInvoices.size()];
      int index = 0;
      for (NewInvoiceDTO invoice : newInvoices.values()) {
        LOG.debug(
            "Processing invoice "
                + invoice.getId()
                + " "
                + invoice.getBalance()
                + " "
                + invoice.getTotal());
        /*
         * Apply invoice composition tasks to the new invoices object
         */
        composeInvoice(entityId, user.getUserId(), invoice);

        LOG.debug(
            "  after composeInvoice "
                + invoice.getId()
                + " "
                + invoice.getBalance()
                + " "
                + invoice.getTotal());
        if (!isReview) {
          // process events after orders added to invoice
          processOrderAddedOnInvoiceEvents(invoice, entityId);
          LOG.debug("processing old invoices");
          for (InvoiceDTO oldInvoice : invoice.getInvoices()) {
            // since this invoice is being delegated, mark it as being carried forward
            // so that it is not re-processed later. do not clear the old balance!
            LOG.debug(
                "  setting status unpaid and carried on "
                    + oldInvoice.getId()
                    + " "
                    + oldInvoice.getBalance()
                    + " "
                    + oldInvoice.getTotal());
            oldInvoice.setInvoiceStatus(
                new InvoiceStatusDAS().find(Constants.INVOICE_STATUS_UNPAID_AND_CARRIED));
          }
        }

        /*
         * apply this object to the DB, generating the actual rows
         */
        // only if the invoice generated actually has some lines in it
        if (invoice.areLinesGeneratedEmpty()) {
          LOG.warn(
              "User "
                  + user.getUserId()
                  + " had orders or invoices but"
                  + " the invoice composition task didn't generate any lines.");
          continue;
        }

        // If this is a web services API call, the billing
        // process id is 0. Don't link to the billing process
        // object for API calls.
        retValue[index] =
            generateDBInvoice(
                user.getUserId(),
                invoice,
                (process.getId() != 0 ? process : null),
                Constants.ORDER_PROCESS_ORIGIN_PROCESS);

        // try to get this new invioce paid by previously unlinked
        // payments
        if (paymentApplication && !isReview) {
          PaymentBL pBL = new PaymentBL();
          pBL.automaticPaymentApplication(retValue[index]);
        }

        index++;
      }
    } catch (PluggableTaskException e1) {
      LOG.error("Error handling pluggable tasks when composing an invoice");
      throw new SessionInternalError(e1);
    } catch (TaskException e1) {
      LOG.error("Task exception when composing an invoice");
      throw new SessionInternalError(e1);
    } catch (Exception e1) {
      LOG.error("Error, probably linking payments", e1);
      throw new SessionInternalError(e1);
    }

    InvoicesGeneratedEvent generatedEvent = new InvoicesGeneratedEvent(entityId, process.getId());
    generatedEvent.addInvoices(retValue);
    EventManager.process(generatedEvent);

    return retValue;
  }
 public void removeItem(Integer itemId) {
   removeObject(order.getLine(itemId));
   order.removeLine(itemId);
 }