public List<InvoiceItem> dispatchToInvoicePluginsAndInsertItems(
      final UUID accountId,
      final boolean isDryRun,
      final WithAccountLock withAccountLock,
      final CallContext context)
      throws InvoiceApiException {
    GlobalLock lock = null;
    try {
      lock =
          locker.lockWithNumberOfTries(
              LockerType.ACCNT_INV_PAY.toString(),
              accountId.toString(),
              invoiceConfig.getMaxGlobalLockRetries());

      final Iterable<Invoice> invoicesForPlugins = withAccountLock.prepareInvoices();

      final List<InvoiceModelDao> invoiceModelDaos = new LinkedList<InvoiceModelDao>();
      for (final Invoice invoiceForPlugin : invoicesForPlugins) {
        // Call plugin
        final List<InvoiceItem> additionalInvoiceItems =
            invoicePluginDispatcher.getAdditionalInvoiceItems(invoiceForPlugin, isDryRun, context);
        invoiceForPlugin.addInvoiceItems(additionalInvoiceItems);

        // Transformation to InvoiceModelDao
        final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(invoiceForPlugin);
        final List<InvoiceItem> invoiceItems = invoiceForPlugin.getInvoiceItems();
        final List<InvoiceItemModelDao> invoiceItemModelDaos = toInvoiceItemModelDao(invoiceItems);
        invoiceModelDao.addInvoiceItems(invoiceItemModelDaos);

        // Keep track of modified invoices
        invoiceModelDaos.add(invoiceModelDao);
      }

      final InternalCallContext internalCallContext =
          internalCallContextFactory.createInternalCallContext(accountId, context);
      final List<InvoiceItemModelDao> createdInvoiceItems =
          dao.createInvoices(invoiceModelDaos, internalCallContext);
      return fromInvoiceItemModelDao(createdInvoiceItems);
    } catch (final LockFailedException e) {
      log.error(
          String.format("Failed to process invoice items for account %s", accountId.toString()), e);
      return ImmutableList.<InvoiceItem>of();
    } finally {
      if (lock != null) {
        lock.release();
      }
    }
  }
  public RawUsageOptimizerResult getConsumableInArrearUsage(
      final LocalDate firstEventStartDate,
      final LocalDate targetDate,
      final Iterable<InvoiceItem> existingUsageItems,
      final Map<String, Usage> knownUsage,
      final InternalCallContext internalCallContext) {
    final LocalDate targetStartDate =
        config.getMaxRawUsagePreviousPeriod() > 0
            ? getOptimizedRawUsageStartDate(
                firstEventStartDate, targetDate, existingUsageItems, knownUsage)
            : firstEventStartDate;
    log.info(
        "RawUsageOptimizer [accountRecordId = {}]: rawUsageStartDate = {}, (proposed) firstEventStartDate = {}",
        new Object[] {
          internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate
        });

    final List<RawUsage> rawUsageData =
        usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext);
    return new RawUsageOptimizerResult(firstEventStartDate, targetStartDate, rawUsageData);
  }
  @VisibleForTesting
  LocalDate getOptimizedRawUsageStartDate(
      final LocalDate firstEventStartDate,
      final LocalDate targetDate,
      final Iterable<InvoiceItem> existingUsageItems,
      final Map<String, Usage> knownUsage) {

    if (!existingUsageItems.iterator().hasNext()) {
      return firstEventStartDate;
    }
    // Extract all usage billing period known in that catalog
    final Set<BillingPeriod> knownUsageBillingPeriod =
        ImmutableSet.copyOf(
            Iterables.transform(
                knownUsage.values(),
                new Function<Usage, BillingPeriod>() {
                  @Nullable
                  @Override
                  public BillingPeriod apply(final Usage input) {
                    return input.getBillingPeriod();
                  }
                }));

    // Make sure all usage items are sorted by endDate
    final List<InvoiceItem> sortedUsageItems = USAGE_ITEM_ORDERING.sortedCopy(existingUsageItems);

    // Compute an array with one date per BillingPeriod:
    // If BillingPeriod is never defined in the catalog (no need to look for items), we initialize
    // its value
    // such that it cannot be chosen
    //
    final LocalDate[] perBillingPeriodMostRecentConsumableInArrearItemEndDate =
        new LocalDate[BillingPeriod.values().length - 1]; // Exclude the NO_BILLING_PERIOD
    int idx = 0;
    for (BillingPeriod bp : BillingPeriod.values()) {
      if (bp != BillingPeriod.NO_BILLING_PERIOD) {
        final LocalDate makerDateThanCannotBeChosenAsTheMinOfAllDates =
            targetDate.plusMonths(config.getMaxRawUsagePreviousPeriod() * bp.getNumberOfMonths());
        perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx++] =
            (knownUsageBillingPeriod.contains(bp))
                ? null
                : makerDateThanCannotBeChosenAsTheMinOfAllDates;
      }
    }

    final ListIterator<InvoiceItem> iterator =
        sortedUsageItems.listIterator(sortedUsageItems.size());
    while (iterator.hasPrevious()) {
      final InvoiceItem previous = iterator.previous();
      Preconditions.checkState(previous instanceof UsageInvoiceItem);
      final UsageInvoiceItem item = (UsageInvoiceItem) previous;
      final Usage usage = knownUsage.get(item.getUsageName());

      if (perBillingPeriodMostRecentConsumableInArrearItemEndDate[
              usage.getBillingPeriod().ordinal()]
          == null) {
        perBillingPeriodMostRecentConsumableInArrearItemEndDate[
                usage.getBillingPeriod().ordinal()] =
            item.getEndDate();
        if (!containsNullEntries(perBillingPeriodMostRecentConsumableInArrearItemEndDate)) {
          break;
        }
      }
    }

    // Extract the min from all the dates
    LocalDate targetStartDate = null;
    idx = 0;
    for (BillingPeriod bp : BillingPeriod.values()) {
      if (bp != BillingPeriod.NO_BILLING_PERIOD) {
        final LocalDate tmp = perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx];
        final LocalDate targetBillingPeriodDate =
            tmp != null
                ? tmp.minusMonths(config.getMaxRawUsagePreviousPeriod() * bp.getNumberOfMonths())
                : null;
        if (targetStartDate == null
            || (targetBillingPeriodDate != null
                && targetBillingPeriodDate.compareTo(targetStartDate) < 0)) {
          targetStartDate = targetBillingPeriodDate;
        }
        idx++;
      }
    }

    final LocalDate result =
        targetStartDate.compareTo(firstEventStartDate) > 0 ? targetStartDate : firstEventStartDate;
    return result;
  }