예제 #1
0
 private BigDecimal calculateTotal() {
   BigDecimal total = new BigDecimal(0);
   for (Iterator it = invoice.getInvoiceLines().iterator(); it.hasNext(); ) {
     InvoiceLineDTO line = (InvoiceLineDTO) it.next();
     total = total.add(line.getAmount());
   }
   return total;
 }
  private String composeLine(InvoiceDTO invoice, InvoiceLineDTO invoiceLine, Integer userId) {
    StringBuffer line = new StringBuffer();
    ContactBL contact = new ContactBL();
    contact.set(userId);

    // cono
    line.append("\"" + emptyIfNull(contact.getEntity().getPostalCode()) + "\"");
    line.append(",");
    // custno
    line.append("\"" + userId + "\"");
    line.append(",");
    // naddrcode
    line.append("\"" + "000" + "\"");
    line.append(",");
    // lookupnm
    line.append("\"" + emptyIfNull(contact.getEntity().getOrganizationName()) + "\"");
    line.append(",");
    // totallineamt
    line.append("\"" + invoiceLine.getAmount() + "\"");
    line.append(",");
    // period
    line.append("\"" + new SimpleDateFormat("yyyyMM").format(invoice.getCreateDatetime()) + "\"");
    line.append(",");
    // name
    line.append("\"" + emptyIfNull(contact.getEntity().getOrganizationName()) + "\"");
    line.append(",");
    // deliveryaddr
    line.append("\"" + emptyIfNull(contact.getEntity().getAddress1()) + "\"");
    line.append(",");
    // city
    line.append("\"" + emptyIfNull(contact.getEntity().getCity()) + "\"");
    line.append(",");
    // state
    line.append("\"" + emptyIfNull(contact.getEntity().getStateProvince()) + "\"");
    line.append(",");
    // zip5
    line.append("\"" + emptyIfNull(contact.getEntity().getPostalCode()) + "\"");
    line.append(",");
    // totdue - round to two decimals
    line.append("\"" + new UserBL().getBalance(userId).round(new MathContext(2)) + "\"");
    line.append(",");
    // qty
    line.append("\"" + invoiceLine.getQuantity() + "\"");
    line.append(",");
    // description
    line.append("\"" + invoiceLine.getDescription() + "\"");
    line.append(",");
    // invoiceno
    line.append("\"" + invoice.getNumber() + "\"");
    line.append(",");
    // custstatus
    line.append("\"" + "TRUE" + "\"");

    LOG.debug("Line to export:" + line);
    return line.toString();
  }
  /**
   * Creates a Suretax request from inputs from the invoice.
   *
   * @param invoice Invoice for which tax lines need to be calculated.
   * @return Returns instance of com.sapienter.jbilling.client.suretax.request.SuretaxRequest
   * @throws TaskException
   */
  private SuretaxRequest getAssembledRequest(NewInvoiceContext invoice, Integer userId)
      throws TaskException {
    // Construct a suretax request to get the tax lines.
    SuretaxRequest suretaxRequest = new SuretaxRequest();

    // Get the pluggable task parameters here.
    String clientNumber = getParameter(CLIENT_NUMBER, "");
    String validationKey = getParameter(VALIDATION_KEY, "");
    String responseGroup = getParameter(RESPONSE_GROUP, "03");
    String responseType = getParameter(RESPONSE_TYPE, "D");
    String numberOfDecimals = getParameter(NUMBER_OF_DECIMAL, "2");
    Integer dataYear = null;
    Integer dataMonth = null;
    try {
      dataYear = getParameter(DATA_YEAR, Calendar.getInstance().get(Calendar.YEAR));
      dataMonth = getParameter(DATA_MONTH, Calendar.getInstance().get(Calendar.MONTH) + 1);
    } catch (PluggableTaskException e) {
      LOG.debug("Exception while retrieving Data Year or Data Month");
    }

    suretaxRequest.setClientNumber(clientNumber);
    suretaxRequest.setValidationKey(validationKey);
    String uniqueTrackingCode = System.currentTimeMillis() + "";
    suretaxRequest.setClientTracking(uniqueTrackingCode);
    suretaxRequest.setDataMonth(dataMonth.toString());
    suretaxRequest.setDataYear(dataYear.toString());
    suretaxRequest.setIndustryExemption("");
    suretaxRequest.setBusinessUnit("");
    suretaxRequest.setResponseGroup(responseGroup);
    suretaxRequest.setResponseType(responseType + numberOfDecimals);
    suretaxRequest.setReturnFileCode("0");
    suretaxRequest.setTotalRevenue(getTotalRevenue(invoice).floatValue());

    List<LineItem> itemList = new ArrayList<LineItem>();
    for (InvoiceLineDTO invoiceLine : (List<InvoiceLineDTO>) invoice.getResultLines()) {

      if (invoiceLine.getInvoiceLineType().getId()
          != ServerConstants.INVOICE_LINE_TYPE_TAX.intValue()) {

        LOG.debug("Populating itemlist for invoice line: %s", invoiceLine);

        itemList.add(
            getLineItem(invoiceLine.getItem().getId(), invoiceLine, uniqueTrackingCode, userId));
      }
    }
    suretaxRequest.setItemList(itemList);
    return suretaxRequest;
  }
  protected BigDecimal getTotalRevenue(NewInvoiceContext invoice) {

    // calculate TOTAL to include result lines
    invoice.calculateTotal();
    BigDecimal invoiceAmountSum = invoice.getTotal();

    // Remove CARRIED BALANCE from tax calculation to avoid double taxation
    LOG.debug("Carried balance is %s", invoice.getCarriedBalance());
    if (null != invoice.getCarriedBalance()) {
      invoiceAmountSum = invoiceAmountSum.subtract(invoice.getCarriedBalance());
    }

    // Remove TAX ITEMS from Invoice to avoid calculating tax on tax
    for (int i = 0; i < invoice.getResultLines().size(); i++) {
      InvoiceLineDTO invoiceLine = (InvoiceLineDTO) invoice.getResultLines().get(i);
      if (null != invoiceLine.getInvoiceLineType()
          && invoiceLine.getInvoiceLineType().getId() == ServerConstants.INVOICE_LINE_TYPE_TAX) {
        invoiceAmountSum = invoiceAmountSum.subtract(invoiceLine.getAmount());
      }
    }

    return invoiceAmountSum;
  }
예제 #5
0
  public InvoiceDTO getDTOEx(Integer languageId, boolean forDisplay) {

    if (!forDisplay) {
      return invoice;
    }

    InvoiceDTO invoiceDTO = new InvoiceDTO(invoice);
    // make sure that the lines are properly ordered
    List<InvoiceLineDTO> orderdLines = new ArrayList<InvoiceLineDTO>(invoiceDTO.getInvoiceLines());
    Collections.sort(orderdLines, new InvoiceLineComparator());
    invoiceDTO.setInvoiceLines(orderdLines);

    UserBL userBl = new UserBL(invoice.getBaseUser());
    Locale locale = userBl.getLocale();
    ResourceBundle bundle = ResourceBundle.getBundle("entityNotifications", locale);

    // now add headres and footers if this invoices has subaccount
    // lines
    if (invoiceDTO.hasSubAccounts()) {
      addHeadersFooters(orderdLines, bundle);
    }
    // add a grand total final line
    InvoiceLineDTO total = new InvoiceLineDTO();
    total.setDescription(bundle.getString("invoice.line.total"));
    total.setAmount(invoice.getTotal());
    total.setIsPercentage(0);
    invoiceDTO.getInvoiceLines().add(total);

    // add some currency info for the human
    CurrencyBL currency = new CurrencyBL(invoice.getCurrency().getId());
    if (languageId != null) {
      invoiceDTO.setCurrencyName(currency.getEntity().getDescription(languageId));
    }
    invoiceDTO.setCurrencySymbol(currency.getEntity().getSymbol());

    return invoiceDTO;
  }
 /**
  * Converts the instance of SuretaxResponse object into tax lines.
  *
  * @param suretaxResponse
  * @param order
  * @return
  */
 private List<InvoiceLineDTO> getTaxLines(SuretaxResponse suretaxResponse, OrderDTO order) {
   List<InvoiceLineDTO> taxLines = new ArrayList<InvoiceLineDTO>();
   if (suretaxResponse.successful.equals("Y")) {
     for (Group group : suretaxResponse.groupList) {
       for (TaxItem taxItem : group.taxList) {
         InvoiceLineDTO invoiceLineDTO = new InvoiceLineDTO();
         invoiceLineDTO.setAmount(new BigDecimal(taxItem.taxAmount));
         invoiceLineDTO.setDescription(taxItem.taxTypeCode + ":" + taxItem.taxTypeDesc);
         invoiceLineDTO.setInvoiceLineType(
             new InvoiceLineTypeDTO(ServerConstants.INVOICE_LINE_TYPE_TAX));
         invoiceLineDTO.setIsPercentage(null);
         invoiceLineDTO.setOrder(order);
         invoiceLineDTO.setPrice(new BigDecimal(taxItem.taxAmount));
         invoiceLineDTO.setQuantity(1);
         taxLines.add(invoiceLineDTO);
       }
     }
   }
   return taxLines;
 }
  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;
  }
예제 #8
0
  /**
   * Will add lines with headers and footers to make an invoice with subaccounts more readable. The
   * lines have to be already sorted.
   *
   * @param lines
   */
  private void addHeadersFooters(List<InvoiceLineDTO> lines, ResourceBundle bundle) {
    Integer nowProcessing = Integer.valueOf(-1);
    BigDecimal total = null;
    int totalLines = lines.size();
    int subaccountNumber = 0;

    LOG.debug("adding headers & footers." + totalLines);

    for (int idx = 0; idx < totalLines; idx++) {
      InvoiceLineDTO line = (InvoiceLineDTO) lines.get(idx);

      if (line.getTypeId() == Constants.INVOICE_LINE_TYPE_SUB_ACCOUNT
          && !line.getSourceUserId().equals(nowProcessing)) {
        // line break
        nowProcessing = line.getSourceUserId();
        subaccountNumber++;
        // put the total first
        if (total != null) { // it could be the first subaccount
          InvoiceLineDTO totalLine = new InvoiceLineDTO();
          totalLine.setDescription(bundle.getString("invoice.line.subAccount.footer"));
          totalLine.setAmount(total);
          lines.add(idx, totalLine);
          idx++;
          totalLines++;
        }
        total = BigDecimal.ZERO;

        // now the header anouncing a new subaccout
        InvoiceLineDTO headerLine = new InvoiceLineDTO();
        try {
          ContactBL contact = new ContactBL();
          contact.set(nowProcessing);
          StringBuffer text = new StringBuffer();
          text.append(subaccountNumber + " - ");
          text.append(bundle.getString("invoice.line.subAccount.header1"));
          text.append(
              " " + bundle.getString("invoice.line.subAccount.header2") + " " + nowProcessing);
          if (contact.getEntity().getFirstName() != null) {
            text.append(" " + contact.getEntity().getFirstName());
          }
          if (contact.getEntity().getLastName() != null) {
            text.append(" " + contact.getEntity().getLastName());
          }
          headerLine.setDescription(text.toString());
          lines.add(idx, headerLine);
          idx++;
          totalLines++;
        } catch (Exception e) {
          LOG.error("Exception", e);
          return;
        }
      }

      // update the total
      if (total != null) {
        // there had been at least one sub-account processed
        if (line.getTypeId() == Constants.INVOICE_LINE_TYPE_SUB_ACCOUNT) {
          total = total.add(line.getAmount());
        } else {
          // this is the last total to display, from now on the
          // lines are not of subaccounts
          InvoiceLineDTO totalLine = new InvoiceLineDTO();
          totalLine.setDescription(bundle.getString("invoice.line.subAccount.footer"));
          totalLine.setAmount(total);
          lines.add(idx, totalLine);
          total = null; // to avoid repeating
        }
      }
    }
    // if there are no lines after the last subaccount, we need
    // a total for it
    if (total != null) { // only if it wasn't added before
      InvoiceLineDTO totalLine = new InvoiceLineDTO();
      totalLine.setDescription(bundle.getString("invoice.line.subAccount.footer"));
      totalLine.setAmount(total);
      lines.add(totalLine);
    }

    LOG.debug("done " + lines.size());
  }
예제 #9
0
  public static InvoiceWS getWS(InvoiceDTO i) {
    if (i == null) {
      return null;
    }
    InvoiceWS retValue = new InvoiceWS();
    retValue.setId(i.getId());
    retValue.setCreateDateTime(i.getCreateDatetime());
    retValue.setCreateTimeStamp(i.getCreateTimestamp());
    retValue.setLastReminder(i.getLastReminder());
    retValue.setDueDate(i.getDueDate());
    retValue.setTotal(i.getTotal());
    retValue.setToProcess(i.getToProcess());
    retValue.setStatusId(i.getInvoiceStatus().getId());
    retValue.setBalance(i.getBalance());
    retValue.setCarriedBalance(i.getCarriedBalance());
    retValue.setInProcessPayment(i.getInProcessPayment());
    retValue.setDeleted(i.getDeleted());
    retValue.setPaymentAttempts(i.getPaymentAttempts());
    retValue.setIsReview(i.getIsReview());
    retValue.setCurrencyId(i.getCurrency().getId());
    retValue.setCustomerNotes(i.getCustomerNotes());
    retValue.setNumber(i.getPublicNumber());
    retValue.setOverdueStep(i.getOverdueStep());
    retValue.setUserId(i.getBaseUser().getId());

    Integer delegatedInvoiceId = i.getInvoice() == null ? null : i.getInvoice().getId();
    Integer userId = i.getBaseUser().getId();
    Integer payments[] = new Integer[i.getPaymentMap().size()];
    com.sapienter.jbilling.server.entity.InvoiceLineDTO invoiceLines[] =
        new com.sapienter.jbilling.server.entity.InvoiceLineDTO[i.getInvoiceLines().size()];
    Integer orders[] = new Integer[i.getOrderProcesses().size()];

    int f;
    f = 0;
    for (PaymentInvoiceMapDTO p : i.getPaymentMap()) {
      payments[f++] = p.getPayment().getId();
    }
    f = 0;
    for (OrderProcessDTO orderP : i.getOrderProcesses()) {
      orders[f++] = orderP.getPurchaseOrder().getId();
    }
    f = 0;
    for (InvoiceLineDTO line : i.getInvoiceLines()) {
      invoiceLines[f++] =
          new com.sapienter.jbilling.server.entity.InvoiceLineDTO(
              line.getId(),
              line.getDescription(),
              line.getAmount(),
              line.getPrice(),
              line.getQuantity(),
              line.getDeleted(),
              line.getItem() == null ? null : line.getItem().getId(),
              line.getSourceUserId(),
              line.getIsPercentage());
    }

    retValue.setDelegatedInvoiceId(delegatedInvoiceId);
    retValue.setUserId(userId);
    retValue.setPayments(payments);
    retValue.setInvoiceLines(invoiceLines);
    retValue.setOrders(orders);

    retValue.setMetaFields(MetaFieldBL.convertMetaFieldsToWS(new UserBL().getEntityId(userId), i));

    return retValue;
  }
예제 #10
0
  public void createLines(NewInvoiceDTO newInvoice) {
    Collection invoiceLines = invoice.getInvoiceLines();

    // Now create all the invoice lines, from the lines in the DTO
    // put there by the invoice composition pluggable tasks
    InvoiceLineDAS invoiceLineDas = new InvoiceLineDAS();

    // get the result DTO lines
    Iterator dueInvoiceLines = newInvoice.getResultLines().iterator();
    // go over the DTO lines, creating one invoice line for each

    // #2196 - GET Invoice Rounding Preference for entity entityId
    PreferenceBL pref = new PreferenceBL();
    Integer entityId = newInvoice.getEntityId();
    if (null == entityId) {
      entityId = newInvoice.getBaseUser().getEntity().getId();
    }

    int decimals = Constants.BIGDECIMAL_SCALE;
    try {
      pref.set(entityId, Constants.PREFERENCE_INVOICE_DECIMALS);
      decimals = pref.getInt();
    } catch (EmptyResultDataAccessException e) {
      // ignore
    }
    // #2196

    while (dueInvoiceLines.hasNext()) {
      InvoiceLineDTO lineToAdd = (InvoiceLineDTO) dueInvoiceLines.next();
      // define if the line is a percentage or not
      lineToAdd.setIsPercentage(0);
      if (lineToAdd.getItem() != null) {
        try {
          ItemBL item = new ItemBL(lineToAdd.getItem());
          if (item.getEntity().getPercentage() != null) {
            lineToAdd.setIsPercentage(1);
          }
        } catch (SessionInternalError e) {
          LOG.error("Could not find item to create invoice line " + lineToAdd.getItem().getId());
        }
      }

      // #2196 - Use Invoice Rounding Preference to round Invoice Lines
      if (null != lineToAdd.getAmount()) {
        lineToAdd.setAmount(lineToAdd.getAmount().setScale(decimals, Constants.BIGDECIMAL_ROUND));
      }
      // #2196

      // create the database row
      InvoiceLineDTO newLine =
          invoiceLineDas.create(
              lineToAdd.getDescription(),
              lineToAdd.getAmount(),
              lineToAdd.getQuantity(),
              lineToAdd.getPrice(),
              lineToAdd.getTypeId(),
              lineToAdd.getItem(),
              lineToAdd.getSourceUserId(),
              lineToAdd.getIsPercentage());

      // update the invoice-lines relationship
      newLine.setInvoice(invoice);
      invoiceLines.add(newLine);
    }
    getHome().save(invoice);
    EventManager.process(new NewInvoiceEvent(invoice));
  }
  /* (non-Javadoc)
   * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
   */
  public int compare(InvoiceLineDTO perA, InvoiceLineDTO perB) {
    int retValue;

    // the line type should tell first
    if (perA.getOrderPosition() == perB.getOrderPosition()) {

      try {
        if (perA.getTypeId() == Constants.INVOICE_LINE_TYPE_SUB_ACCOUNT
            && perB.getTypeId() == Constants.INVOICE_LINE_TYPE_SUB_ACCOUNT) {
          // invoice lines have to be grouped by user
          // find out both users
          retValue = perA.getSourceUserId().compareTo(perB.getSourceUserId());
          /*
          Logger.getLogger(InvoiceLineComparator.class).debug(
                  "Testing two sub account lines. a.userid " +
                  perA.getSourceUserId() + " b.userid " + perB.getSourceUserId() +
                  " result " + retValue);
                  */
          if (retValue != 0) {
            // these are lines for two different users, so
            // they are different enough now
            return retValue;
          }
        }
        // use the number
        if (perA.getItem() != null && perB.getItem() != null) {
          ItemBL itemA = new ItemBL(perA.getItem());
          ItemBL itemB = new ItemBL(perB.getItem());
          if (itemA.getEntity().getNumber() == null && itemB.getEntity().getNumber() == null) {
            retValue =
                new Integer(perA.getItem().getId()).compareTo(new Integer(perB.getItem().getId()));
          } else if (itemA.getEntity().getNumber() == null) {
            retValue = 1;
          } else if (itemB.getEntity().getNumber() == null) {
            retValue = -1;
          } else {
            // none are null
            retValue = itemA.getEntity().getNumber().compareTo(itemB.getEntity().getNumber());
          }
        } else {
          retValue = 0;
        }
      } catch (Exception e) {
        Logger.getLogger(InvoiceLineComparator.class)
            .error("Comparing invoice lines " + perA + " " + perB, e);
        retValue = 0;
      }
    } else {
      retValue = new Integer(perA.getOrderPosition()).compareTo(perB.getOrderPosition());
    }
    /*
            Logger.getLogger(InvoiceLineComparator.class).debug(
                    "Comparing " + perA.getId() + " " + perB.getId() +
                    " result " + retValue);
    */
    return retValue;
  }