Details
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 2118220..ad59ef2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
@@ -18,7 +20,11 @@ package org.killbill.billing.invoice.api.user;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -34,6 +40,7 @@ import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.InvoiceDispatcher;
+import org.killbill.billing.invoice.InvoicePluginDispatcher;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -55,18 +62,22 @@ import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
+import org.killbill.billing.util.globallocker.LockerType;
import org.killbill.billing.util.tag.ControlTagType;
import org.killbill.billing.util.tag.Tag;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.commons.locker.GlobalLock;
+import org.killbill.commons.locker.GlobalLocker;
+import org.killbill.commons.locker.LockFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.inject.Inject;
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationNoException;
@@ -75,46 +86,43 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceUserApi.class);
+ private static final int NB_LOCK_TRY = 5;
+
private final InvoiceDao dao;
private final InvoiceDispatcher dispatcher;
+ private final InvoicePluginDispatcher invoicePluginDispatcher;
private final AccountInternalApi accountUserApi;
private final TagInternalApi tagApi;
private final HtmlInvoiceGenerator generator;
private final InternalCallContextFactory internalCallContextFactory;
+ private final GlobalLocker locker;
private final PersistentBus eventBus;
@Inject
- public DefaultInvoiceUserApi(final InvoiceDao dao, final InvoiceDispatcher dispatcher, final AccountInternalApi accountUserApi, final PersistentBus eventBus,
+ public DefaultInvoiceUserApi(final InvoiceDao dao, final InvoiceDispatcher dispatcher, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountInternalApi accountUserApi,
+ final GlobalLocker locker, final PersistentBus eventBus,
final TagInternalApi tagApi, final HtmlInvoiceGenerator generator, final InternalCallContextFactory internalCallContextFactory) {
this.dao = dao;
this.dispatcher = dispatcher;
+ this.invoicePluginDispatcher = invoicePluginDispatcher;
this.accountUserApi = accountUserApi;
this.tagApi = tagApi;
this.generator = generator;
this.internalCallContextFactory = internalCallContextFactory;
+ this.locker = locker;
this.eventBus = eventBus;
}
@Override
public List<Invoice> getInvoicesByAccount(final UUID accountId, final TenantContext context) {
- return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context)),
- new Function<InvoiceModelDao, Invoice>() {
- @Override
- public Invoice apply(final InvoiceModelDao input) {
- return new DefaultInvoice(input);
- }
- }));
+ final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context));
+ return fromInvoiceModelDao(invoicesByAccount);
}
@Override
public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final TenantContext context) {
- return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getInvoicesByAccount(fromDate, internalCallContextFactory.createInternalTenantContext(accountId, context)),
- new Function<InvoiceModelDao, Invoice>() {
- @Override
- public Invoice apply(final InvoiceModelDao input) {
- return new DefaultInvoice(input);
- }
- }));
+ final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(fromDate, internalCallContextFactory.createInternalTenantContext(accountId, context));
+ return fromInvoiceModelDao(invoicesByAccount);
}
@Override
@@ -122,9 +130,9 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(context);
final UUID invoiceId = dao.getInvoiceIdByPaymentId(paymentId, tenantContext);
if (invoiceId == null) {
- throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+ throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, paymentId);
}
- final InvoiceModelDao invoiceModelDao = invoiceId != null ? dao.getById(invoiceId, tenantContext) : null;
+ final InvoiceModelDao invoiceModelDao = dao.getById(invoiceId, tenantContext);
return new DefaultInvoice(invoiceModelDao);
}
@@ -191,16 +199,10 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final TenantContext context) {
- return ImmutableList.<Invoice>copyOf(Collections2.transform(dao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContextFactory.createInternalTenantContext(accountId, context)),
- new Function<InvoiceModelDao, Invoice>() {
- @Override
- public Invoice apply(final InvoiceModelDao input) {
- return new DefaultInvoice(input);
- }
- }));
+ final List<InvoiceModelDao> unpaidInvoicesByAccountId = dao.getUnpaidInvoicesByAccountId(accountId, upToDate, internalCallContextFactory.createInternalTenantContext(accountId, context));
+ return fromInvoiceModelDao(unpaidInvoicesByAccountId);
}
-
@Override
public Invoice triggerInvoiceGeneration(final UUID accountId, final LocalDate targetDate, final DryRunArguments dryRunArguments,
final CallContext context) throws InvoiceApiException {
@@ -209,7 +211,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
final Account account;
try {
account = accountUserApi.getAccountById(accountId, internalContext);
- } catch (AccountApiException e) {
+ } catch (final AccountApiException e) {
throw new InvoiceApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, e.toString());
}
@@ -266,26 +268,50 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
}
- final Iterable<InvoiceItemModelDao> chargesModelDao = Iterables.<InvoiceItem, InvoiceItemModelDao>transform(charges,
- new Function<InvoiceItem, InvoiceItemModelDao>() {
- @Nullable
- @Override
- public InvoiceItemModelDao apply(final InvoiceItem externalCharge) {
- return new InvoiceItemModelDao(externalCharge);
- }
- }
- );
- final List<InvoiceItemModelDao> externalCharges = dao.insertExternalCharges(accountId, effectiveDate, chargesModelDao, internalCallContextFactory.createInternalCallContext(accountId, context));
-
- return Lists.<InvoiceItemModelDao, InvoiceItem>transform(externalCharges,
- new Function<InvoiceItemModelDao, InvoiceItem>() {
- @Nullable
- @Override
- public InvoiceItem apply(@Nullable final InvoiceItemModelDao externalCharge) {
- return InvoiceItemFactory.fromModelDao(externalCharge);
- }
- }
- );
+ final WithAccountLock withAccountLock = new WithAccountLock() {
+
+ @Override
+ public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
+ // Group all new external charges on the same invoice (per currency)
+ final Map<Currency, Invoice> newInvoicesForExternalCharges = new HashMap<Currency, Invoice>();
+ final Map<UUID, Invoice> existingInvoicesForExternalCharges = new HashMap<UUID, Invoice>();
+
+ for (final InvoiceItem charge : charges) {
+ final Invoice invoiceForExternalCharge;
+ final UUID invoiceIdForExternalCharge = charge.getInvoiceId();
+ // Create an invoice for that external charge if it doesn't exist
+ if (invoiceIdForExternalCharge == null) {
+ final Currency currency = charge.getCurrency();
+ if (newInvoicesForExternalCharges.get(currency) == null) {
+ final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+ newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
+ }
+ invoiceForExternalCharge = newInvoicesForExternalCharges.get(currency);
+ } else {
+ if (existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge) == null) {
+ final Invoice existingInvoiceForExternalCharge = getInvoice(invoiceIdForExternalCharge, context);
+ existingInvoicesForExternalCharges.put(invoiceIdForExternalCharge, existingInvoiceForExternalCharge);
+ }
+ invoiceForExternalCharge = existingInvoicesForExternalCharges.get(invoiceIdForExternalCharge);
+ }
+
+ final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(UUID.randomUUID(),
+ context.getCreatedDate(),
+ invoiceForExternalCharge.getId(),
+ accountId,
+ charge.getBundleId(),
+ charge.getDescription(),
+ effectiveDate,
+ charge.getAmount(),
+ charge.getCurrency());
+ invoiceForExternalCharge.addInvoiceItem(externalCharge);
+ }
+
+ return Iterables.<Invoice>concat(newInvoicesForExternalCharges.values(), existingInvoicesForExternalCharges.values());
+ }
+ };
+
+ return dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
}
@Override
@@ -312,8 +338,43 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
throw new InvoiceApiException(ErrorCode.CREDIT_AMOUNT_INVALID, amount);
}
- final InvoiceItemModelDao credit = dao.insertCredit(accountId, invoiceId, amount, effectiveDate, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
- return InvoiceItemFactory.fromModelDao(credit);
+ final WithAccountLock withAccountLock = new WithAccountLock() {
+
+ private InvoiceItem creditItem;
+
+ @Override
+ public List<Invoice> prepareInvoices() throws InvoiceApiException {
+ // Create an invoice for that credit if it doesn't exist
+ final Invoice invoiceForCredit;
+ if (invoiceId == null) {
+ invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+ } else {
+ invoiceForCredit = getInvoice(invoiceId, context);
+ // Check the specified currency matches the one of the existing invoice
+ if (invoiceForCredit.getCurrency() != currency) {
+ throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, currency, invoiceForCredit.getCurrency());
+ }
+ }
+
+ // Create the new credit
+ creditItem = new CreditAdjInvoiceItem(UUID.randomUUID(),
+ context.getCreatedDate(),
+ invoiceForCredit.getId(),
+ accountId,
+ effectiveDate,
+ // Note! The amount is negated here!
+ amount.negate(),
+ currency);
+ invoiceForCredit.addInvoiceItem(creditItem);
+
+ return ImmutableList.<Invoice>of(invoiceForCredit);
+ }
+ };
+
+ final List<InvoiceItem> creditInvoiceItems = dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
+ Preconditions.checkState(creditInvoiceItems.size() == 1, "Should have created a single credit invoice item: " + creditInvoiceItems);
+
+ return creditInvoiceItems.get(0);
}
@Override
@@ -330,6 +391,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE, amount);
}
+ // TODO
final InvoiceItemModelDao adjustment = dao.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, amount, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
return InvoiceItemFactory.fromModelDao(adjustment);
}
@@ -359,7 +421,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
}
- HtmlInvoice htmlInvoice = generator.generateInvoice(account, invoice, manualPay, internalContext);
+ final HtmlInvoice htmlInvoice = generator.generateInvoice(account, invoice, manualPay, internalContext);
return htmlInvoice.getBody();
}
@@ -371,8 +433,79 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) {
try {
eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
- } catch (EventBusException e) {
+ } catch (final EventBusException e) {
log.warn("Failed to post adjustment event for invoice " + invoiceId, e);
}
}
+
+ private List<Invoice> fromInvoiceModelDao(final Collection<InvoiceModelDao> invoiceModelDaos) {
+ return ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceModelDaos,
+ new Function<InvoiceModelDao, Invoice>() {
+ @Override
+ public Invoice apply(final InvoiceModelDao input) {
+ return new DefaultInvoice(input);
+ }
+ }));
+ }
+
+ private List<InvoiceItem> fromInvoiceItemModelDao(final Collection<InvoiceItemModelDao> invoiceItemModelDaos) {
+ return ImmutableList.<InvoiceItem>copyOf(Collections2.transform(invoiceItemModelDaos,
+ new Function<InvoiceItemModelDao, InvoiceItem>() {
+ @Override
+ public InvoiceItem apply(final InvoiceItemModelDao input) {
+ return InvoiceItemFactory.fromModelDao(input);
+ }
+ }));
+ }
+
+ private List<InvoiceItemModelDao> toInvoiceItemModelDao(final Collection<InvoiceItem> invoiceItems) {
+ return ImmutableList.copyOf(Collections2.transform(invoiceItems,
+ new Function<InvoiceItem, InvoiceItemModelDao>() {
+ @Override
+ public InvoiceItemModelDao apply(final InvoiceItem input) {
+ return new InvoiceItemModelDao(input);
+ }
+ }));
+ }
+
+ public interface WithAccountLock {
+
+ public Iterable<Invoice> prepareInvoices() throws InvoiceApiException;
+ }
+
+ private List<InvoiceItem> dispatchToInvoicePluginsAndInsertItems(final UUID accountId, final WithAccountLock withAccountLock, final CallContext context) throws InvoiceApiException {
+ GlobalLock lock = null;
+ try {
+ lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountId.toString(), NB_LOCK_TRY);
+
+ 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, 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();
+ }
+ }
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index e879aa1..0c4bb3a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -19,9 +19,6 @@
package org.killbill.billing.invoice.dao;
import java.math.BigDecimal;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -60,7 +57,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
-import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -239,6 +235,41 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
+ public List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context) {
+ return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
+ @Override
+ public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+ final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+ final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+
+ final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
+ for (final InvoiceModelDao invoiceModelDao : invoices) {
+ // Create the invoice if needed
+ if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
+ invoiceSqlDao.create(invoiceModelDao, context);
+ }
+
+ // Create the invoice items if needed
+ for (final InvoiceItemModelDao invoiceItemModelDao : invoiceModelDao.getInvoiceItems()) {
+ if (transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context) == null) {
+ transInvoiceItemSqlDao.create(invoiceItemModelDao, context);
+ createdInvoiceItems.add(transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context));
+ }
+ }
+
+ cbaDao.addCBAComplexityFromTransaction(invoiceModelDao.getId(), entitySqlDaoWrapperFactory, context);
+
+ // Notify the bus since the balance of the invoice changed
+ // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
+ notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
+ }
+
+ return createdInvoiceItems;
+ }
+ });
+ }
+
+ @Override
public List<InvoiceModelDao> getInvoicesBySubscription(final UUID subscriptionId, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
@@ -605,69 +636,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public List<InvoiceItemModelDao> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate,
- final Iterable<InvoiceItemModelDao> charges, final InternalCallContext context)
- throws InvoiceApiException {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
- @Override
- public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao transInvoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
- final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
-
- // Group all new external charges on the same invoice (per currency)
- final Map<Currency, InvoiceModelDao> newInvoicesForExternalCharges = new HashMap<Currency, InvoiceModelDao>();
-
- final List<InvoiceItemModelDao> createdExternalCharges = new LinkedList<InvoiceItemModelDao>();
- final Collection<UUID> changedInvoices = new HashSet<UUID>();
- for (final InvoiceItemModelDao charge : charges) {
- UUID invoiceIdForExternalCharge = charge.getInvoiceId();
- // Create an invoice for that external charge if it doesn't exist
- if (invoiceIdForExternalCharge == null) {
- final Currency currency = charge.getCurrency();
- if (newInvoicesForExternalCharges.get(currency) == null) {
- final InvoiceModelDao newInvoiceForExternalCharge = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, currency);
- transInvoiceDao.create(newInvoiceForExternalCharge, context);
- newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
- }
- invoiceIdForExternalCharge = newInvoicesForExternalCharges.get(currency).getId();
- }
-
- final InvoiceItemModelDao externalCharge = new InvoiceItemModelDao(context.getCreatedDate(),
- InvoiceItemType.EXTERNAL_CHARGE,
- invoiceIdForExternalCharge,
- accountId,
- charge.getBundleId(),
- null,
- charge.getDescription(),
- null,
- null,
- null,
- Objects.firstNonNull(charge.getStartDate(), effectiveDate),
- charge.getEndDate(),
- charge.getAmount(),
- null,
- charge.getCurrency(),
- null);
- transInvoiceItemDao.create(externalCharge, context);
-
- createdExternalCharges.add(externalCharge);
- changedInvoices.add(invoiceIdForExternalCharge);
- }
-
- cbaDao.addCBAComplexityFromTransaction(entitySqlDaoWrapperFactory, context);
-
- // Notify the bus since the balance of the invoice changed
- // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
- for (final UUID invoiceId : changedInvoices) {
- notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
- }
-
- return createdExternalCharges;
- }
- });
- }
-
- @Override
public InvoiceItemModelDao getCreditById(final UUID creditId, final InternalTenantContext context) throws InvoiceApiException {
return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
@Override
@@ -683,39 +651,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public InvoiceItemModelDao insertCredit(final UUID accountId, @Nullable final UUID invoiceId, final BigDecimal positiveCreditAmount,
- final LocalDate effectiveDate, final Currency currency, final InternalCallContext context) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
- @Override
- public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
-
- UUID invoiceIdForCredit = invoiceId;
- // Create an invoice for that credit if it doesn't exist
- if (invoiceIdForCredit == null) {
- final InvoiceModelDao invoiceForCredit = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, currency);
- transactional.create(invoiceForCredit, context);
- invoiceIdForCredit = invoiceForCredit.getId();
- }
-
- // Note! The amount is negated here!
- final InvoiceItemModelDao credit = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.CREDIT_ADJ, invoiceIdForCredit,
- accountId, null, null, null, null, null, null, effectiveDate,
- null, positiveCreditAmount.negate(), null,
- currency, null);
- invoiceDaoHelper.insertItem(entitySqlDaoWrapperFactory, credit, context);
-
- cbaDao.addCBAComplexityFromTransaction(invoiceIdForCredit, entitySqlDaoWrapperFactory, context);
-
- // Notify the bus since the balance of the invoice changed
- notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
-
- return credit;
- }
- });
- }
-
- @Override
public InvoiceItemModelDao insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
final LocalDate effectiveDate, @Nullable final BigDecimal positiveAdjAmount,
@Nullable final Currency currency, final InternalCallContext context) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index b350b42..ad63d71 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -28,13 +28,10 @@ import org.joda.time.LocalDate;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.entity.EntityPersistenceException;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDao;
-import org.killbill.billing.util.entity.dao.EntitySqlDao;
-import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceApiException> {
@@ -42,6 +39,8 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
final boolean isRealInvoice, final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions,
final InternalCallContext context);
+ List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context);
+
InvoiceModelDao getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
List<InvoiceModelDao> getInvoicesByAccount(InternalTenantContext context);
@@ -50,7 +49,7 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
List<InvoiceModelDao> getInvoicesBySubscription(UUID subscriptionId, InternalTenantContext context);
- public Pagination<InvoiceModelDao> searchInvoices(String searchKey, Long offset, Long limit, InternalTenantContext context);
+ Pagination<InvoiceModelDao> searchInvoices(String searchKey, Long offset, Long limit, InternalTenantContext context);
UUID getInvoiceIdByPaymentId(UUID paymentId, InternalTenantContext context);
@@ -60,7 +59,7 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
BigDecimal getAccountBalance(UUID accountId, InternalTenantContext context);
- public BigDecimal getAccountCBA(UUID accountId, InternalTenantContext context);
+ BigDecimal getAccountCBA(UUID accountId, InternalTenantContext context);
List<InvoiceModelDao> getUnpaidInvoicesByAccountId(UUID accountId, @Nullable LocalDate upToDate, InternalTenantContext context);
@@ -71,18 +70,18 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
InvoiceItemModelDao doCBAComplexity(InvoiceModelDao invoice, InternalCallContext context) throws InvoiceApiException;
- /**
- * Create a refund.
- *
- * @param paymentId payment associated with that refund
- * @param amount amount to refund
- * @param isInvoiceAdjusted whether the refund should trigger an invoice or invoice item adjustment
- * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
- * @param transactionExternalKey transaction refund externalKey
- * @param context the call callcontext
- * @return the created invoice payment object associated with this refund
- * @throws InvoiceApiException
- */
+ /**
+ * Create a refund.
+ *
+ * @param paymentId payment associated with that refund
+ * @param amount amount to refund
+ * @param isInvoiceAdjusted whether the refund should trigger an invoice or invoice item adjustment
+ * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
+ * @param transactionExternalKey transaction refund externalKey
+ * @param context the call callcontext
+ * @return the created invoice payment object associated with this refund
+ * @throws InvoiceApiException
+ */
InvoicePaymentModelDao createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
String transactionExternalKey, InternalCallContext context) throws InvoiceApiException;
@@ -106,17 +105,6 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
InvoiceItemModelDao getExternalChargeById(UUID externalChargeId, InternalTenantContext context) throws InvoiceApiException;
/**
- * Add one or more external charges to a given account.
- *
- * @param accountId the account id
- * @param effectiveDate the effective date for newly created invoices (in the account timezone)
- * @param charges the external charges
- * @param context the call context
- * @return the newly created external charges invoice items
- */
- List<InvoiceItemModelDao> insertExternalCharges(UUID accountId, LocalDate effectiveDate, Iterable<InvoiceItemModelDao> charges, InternalCallContext context) throws InvoiceApiException;
-
- /**
* Retrieve a credit by id.
*
* @param creditId the credit id
@@ -126,20 +114,6 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
InvoiceItemModelDao getCreditById(UUID creditId, InternalTenantContext context) throws InvoiceApiException;
/**
- * Add a credit to a given account and invoice. If invoiceId is null, a new invoice will be created.
- *
- * @param accountId the account id
- * @param invoiceId the invoice id
- * @param amount the credit amount
- * @param effectiveDate the day to grant the credit, in the account timezone
- * @param currency the credit currency
- * @param context the call callcontext
- * @return the newly created credit invoice item
- */
- InvoiceItemModelDao insertCredit(UUID accountId, @Nullable UUID invoiceId, BigDecimal amount,
- LocalDate effectiveDate, Currency currency, InternalCallContext context);
-
- /**
* Adjust an invoice item.
*
* @param accountId the account id
@@ -151,6 +125,7 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
* @param context the call callcontext
* @return the newly created adjustment item
*/
+ // TODO Needed?
InvoiceItemModelDao insertInvoiceItemAdjustment(UUID accountId, UUID invoiceId, UUID invoiceItemId, LocalDate effectiveDate,
@Nullable BigDecimal amount, @Nullable Currency currency, InternalCallContext context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 726dda2..b2514bf 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -28,7 +28,6 @@ import java.util.UUID;
import javax.annotation.Nullable;
import org.joda.time.LocalDate;
-
import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
@@ -180,12 +179,13 @@ public class InvoiceDaoHelper {
* @param currency the currency of the amount. Pass null to default to the original currency used
* @return the adjustment item
*/
+ // TODO change refund path too to call the plugin
public InvoiceItemModelDao createAdjustmentItem(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID invoiceItemId,
final BigDecimal positiveAdjAmount, final Currency currency,
final LocalDate effectiveDate, final InternalCallContext context) throws InvoiceApiException {
// First, retrieve the invoice item in question
- final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- final InvoiceItemModelDao invoiceItemToBeAdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
+ final InvoiceItemSqlDao invoiceItemSqlDaoX = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+ final InvoiceItemModelDao invoiceItemToBeAXdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
if (invoiceItemToBeAdjusted == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
}
@@ -213,11 +213,12 @@ public class InvoiceDaoHelper {
* @param item the invoice item to create
* @param context the call callcontext
*/
- public void insertItem(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
- final InvoiceItemModelDao item,
- final InternalCallContext context) throws EntityPersistenceException {
+ public InvoiceItemModelDao insertItem(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
+ final InvoiceItemModelDao item,
+ final InternalCallContext context) throws EntityPersistenceException {
final InvoiceItemSqlDao transInvoiceItemDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
transInvoiceItemDao.create(item, context);
+ return transInvoiceItemDao.getById(item.getId().toString(), context);
}
public void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index cd7aa45..81f5311 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -41,7 +41,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
private Currency currency;
private boolean migrated;
- // Note in the database, for convenience only
+ // Not in the database, for convenience only
private List<InvoiceItemModelDao> invoiceItems = new LinkedList<InvoiceItemModelDao>();
private List<InvoicePaymentModelDao> invoicePayments = new LinkedList<InvoicePaymentModelDao>();
private Currency processedCurrency;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 4a0e1a9..321c00a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -63,11 +63,8 @@ import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
import org.killbill.billing.invoice.model.InvoiceItemFactory;
import org.killbill.billing.invoice.model.RecurringInvoiceItem;
-import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.junction.BillingInternalApi;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
import org.killbill.billing.util.callcontext.CallContext;
@@ -103,24 +100,23 @@ public class InvoiceDispatcher {
private final InvoiceDao invoiceDao;
private final InternalCallContextFactory internalCallContextFactory;
private final InvoiceNotifier invoiceNotifier;
+ private final InvoicePluginDispatcher invoicePluginDispatcher;
private final GlobalLocker locker;
private final PersistentBus eventBus;
private final Clock clock;
- private final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
@Inject
- public InvoiceDispatcher(final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry,
- final InvoiceGenerator generator,
+ public InvoiceDispatcher(final InvoiceGenerator generator,
final AccountInternalApi accountApi,
final BillingInternalApi billingApi,
final SubscriptionBaseInternalApi SubscriptionApi,
final InvoiceDao invoiceDao,
final InternalCallContextFactory internalCallContextFactory,
final InvoiceNotifier invoiceNotifier,
+ final InvoicePluginDispatcher invoicePluginDispatcher,
final GlobalLocker locker,
final PersistentBus eventBus,
final Clock clock) {
- this.pluginRegistry = pluginRegistry;
this.generator = generator;
this.billingApi = billingApi;
this.subscriptionApi = SubscriptionApi;
@@ -128,6 +124,7 @@ public class InvoiceDispatcher {
this.invoiceDao = invoiceDao;
this.internalCallContextFactory = internalCallContextFactory;
this.invoiceNotifier = invoiceNotifier;
+ this.invoicePluginDispatcher = invoicePluginDispatcher;
this.locker = locker;
this.eventBus = eventBus;
this.clock = clock;
@@ -224,20 +221,8 @@ public class InvoiceDispatcher {
//
// Ask external invoice plugins if additional items (tax, etc) shall be added to the invoice
//
- final List<InvoicePluginApi> invoicePlugins = this.getInvoicePlugins();
final CallContext callContext = buildCallContext(context);
- for (final InvoicePluginApi invoicePlugin : invoicePlugins) {
- final List<InvoiceItem> items = invoicePlugin.getAdditionalInvoiceItems(invoice, ImmutableList.<PluginProperty>of(), callContext);
- if (items != null) {
- for (final InvoiceItem item : items) {
- if (InvoiceItemType.EXTERNAL_CHARGE.equals(item.getInvoiceItemType()) || InvoiceItemType.TAX.equals(item.getInvoiceItemType())) {
- invoice.addInvoiceItem(item);
- } else {
- log.warn("Ignoring invoice item of type {} from InvoicePluginApi {}: {}", item.getInvoiceItemType(), invoicePlugin, item);
- }
- }
- }
- }
+ invoice.addInvoiceItems(invoicePluginDispatcher.getAdditionalInvoiceItems(invoice, callContext));
boolean isRealInvoiceWithItems = false;
if (!isDryRun) {
@@ -338,14 +323,6 @@ public class InvoiceDispatcher {
return internalCallContextFactory.createCallContext(context);
}
- private List<InvoicePluginApi> getInvoicePlugins() {
- final List<InvoicePluginApi> invoicePlugins = new ArrayList<InvoicePluginApi>();
- for (final String name : this.pluginRegistry.getAllServices()) {
- invoicePlugins.add(this.pluginRegistry.getServiceForName(name));
- }
- return invoicePlugins;
- }
-
@VisibleForTesting
Map<UUID, List<DateTime>> createNextFutureNotificationDate(final List<InvoiceItemModelDao> invoiceItems, final Map<String, Usage> knownUsages, final DateAndTimeZoneContext dateAndTimeZoneContext) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java
new file mode 100644
index 0000000..8af7af0
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.invoice;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.model.DefaultInvoice;
+import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+
+public class InvoicePluginDispatcher {
+
+ private static final Logger log = LoggerFactory.getLogger(InvoicePluginDispatcher.class);
+
+ private final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
+
+ @Inject
+ public InvoicePluginDispatcher(final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry) {
+ this.pluginRegistry = pluginRegistry;
+ }
+
+ //
+ // If we have multiple plugins there is a question of plugin ordering and also a 'product' questions to decide whether
+ // subsequent plugins should have access to items added by previous plugins
+ //
+ public List<InvoiceItem> getAdditionalInvoiceItems(final Invoice originalInvoice, final CallContext callContext) {
+
+ // We clone the original invoice so plugins don't remove/add items
+ final Invoice clonedInvoice = (Invoice) ((DefaultInvoice) originalInvoice).clone();
+ final List<InvoiceItem> additionalInvoiceItems = new LinkedList<InvoiceItem>();
+ final List<InvoicePluginApi> invoicePlugins = getInvoicePlugins();
+ for (final InvoicePluginApi invoicePlugin : invoicePlugins) {
+ final List<InvoiceItem> items = invoicePlugin.getAdditionalInvoiceItems(clonedInvoice, ImmutableList.<PluginProperty>of(), callContext);
+ if (items != null) {
+ for (final InvoiceItem item : items) {
+ if (item.getInvoiceItemType() != InvoiceItemType.FIXED &&
+ item.getInvoiceItemType() != InvoiceItemType.RECURRING &&
+ item.getInvoiceItemType() != InvoiceItemType.REPAIR_ADJ &&
+ item.getInvoiceItemType() != InvoiceItemType.CBA_ADJ &&
+ item.getInvoiceItemType() != InvoiceItemType.CREDIT_ADJ &&
+ item.getInvoiceItemType() != InvoiceItemType.REFUND_ADJ &&
+ item.getInvoiceItemType() != InvoiceItemType.USAGE) {
+ additionalInvoiceItems.add(item);
+ } else {
+ log.warn("Ignoring invoice item of type {} from InvoicePlugin {}: {}", item.getInvoiceItemType(), invoicePlugin, item);
+ }
+ }
+ }
+ }
+ return additionalInvoiceItems;
+ }
+
+ private List<InvoicePluginApi> getInvoicePlugins() {
+ final List<InvoicePluginApi> invoicePlugins = new ArrayList<InvoicePluginApi>();
+ for (final String name : pluginRegistry.getAllServices()) {
+ invoicePlugins.add(pluginRegistry.getServiceForName(name));
+ }
+ return invoicePlugins;
+ }
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index e95d4eb..15880ec 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -40,10 +40,10 @@ import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
-public class DefaultInvoice extends EntityBase implements Invoice {
+public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
- private final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
- private final List<InvoicePayment> payments = new ArrayList<InvoicePayment>();
+ private final List<InvoiceItem> invoiceItems;
+ private final List<InvoicePayment> payments;
private final UUID accountId;
private final Integer invoiceNumber;
private final LocalDate invoiceDate;
@@ -75,6 +75,8 @@ public class DefaultInvoice extends EntityBase implements Invoice {
this.currency = currency;
this.processedCurrency = processedCurrency;
this.migrationInvoice = isMigrationInvoice;
+ this.invoiceItems = new ArrayList<InvoiceItem>();
+ this.payments = new ArrayList<InvoicePayment>();
}
public DefaultInvoice(final InvoiceModelDao invoiceModelDao) {
@@ -95,6 +97,15 @@ public class DefaultInvoice extends EntityBase implements Invoice {
}));
}
+ // Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
+ @Override
+ public Object clone() {
+ final Invoice clonedInvoice = new DefaultInvoice(getId(), getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice());
+ clonedInvoice.getInvoiceItems().addAll(getInvoiceItems());
+ clonedInvoice.getPayments().addAll(getPayments());
+ return clonedInvoice;
+ }
+
@Override
public boolean addInvoiceItem(final InvoiceItem item) {
return invoiceItems.add(item);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 79a1ca4..afac77d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -93,7 +93,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(externalChargeInvoiceItem.getBundleId(), bundleId);
Assert.assertEquals(externalChargeInvoiceItem.getInvoiceItemType(), InvoiceItemType.EXTERNAL_CHARGE);
Assert.assertEquals(externalChargeInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(externalChargeInvoiceItem.getAmount(), externalChargeAmount);
+ Assert.assertEquals(externalChargeInvoiceItem.getAmount().compareTo(externalChargeAmount), 0);
Assert.assertEquals(externalChargeInvoiceItem.getCurrency(), accountCurrency);
Assert.assertNull(externalChargeInvoiceItem.getLinkedItemId());
@@ -172,7 +172,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(externalChargeInvoiceItem.getBundleId(), bundleId);
Assert.assertEquals(externalChargeInvoiceItem.getInvoiceItemType(), InvoiceItemType.EXTERNAL_CHARGE);
Assert.assertEquals(externalChargeInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(externalChargeInvoiceItem.getAmount(), externalChargeAmount);
+ Assert.assertEquals(externalChargeInvoiceItem.getAmount().compareTo(externalChargeAmount), 0);
Assert.assertEquals(externalChargeInvoiceItem.getCurrency(), accountCurrency);
Assert.assertNull(externalChargeInvoiceItem.getLinkedItemId());
@@ -201,7 +201,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(creditInvoiceItem.getAmount(), invoiceBalance.negate());
+ Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(invoiceBalance.negate()), 0);
Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
Assert.assertNull(creditInvoiceItem.getLinkedItemId());
@@ -231,7 +231,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(creditInvoiceItem.getAmount(), creditAmount.negate());
+ Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(creditAmount.negate()), 0);
Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
Assert.assertNull(creditInvoiceItem.getLinkedItemId());
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index db40324..fe64af0 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -18,6 +18,7 @@ package org.killbill.billing.invoice.dao;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
@@ -61,22 +62,44 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
final boolean isRealInvoice, final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions, final InternalCallContext context) {
synchronized (monitor) {
- invoices.put(invoice.getId(), invoice);
- for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) {
- items.put(invoiceItemModelDao.getId(), invoiceItemModelDao);
- }
- accountRecordIds.put(invoice.getAccountId(), context.getAccountRecordId());
+ storeInvoice(invoice, context);
}
try {
eventBus.post(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
InvoiceModelDaoHelper.getBalance(invoice), invoice.getCurrency(),
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
- } catch (PersistentBus.EventBusException ex) {
+ } catch (final PersistentBus.EventBusException ex) {
throw new RuntimeException(ex);
}
}
@Override
+ public List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoiceModelDaos, final InternalCallContext context) {
+ synchronized (monitor) {
+ final List<InvoiceItemModelDao> createdItems = new LinkedList<InvoiceItemModelDao>();
+ for (final InvoiceModelDao invoice : invoiceModelDaos) {
+ createdItems.addAll(storeInvoice(invoice, context));
+ }
+ return createdItems;
+ }
+ }
+
+ private Collection<InvoiceItemModelDao> storeInvoice(final InvoiceModelDao invoice, final InternalCallContext context) {
+ final Collection<InvoiceItemModelDao> createdItems = new LinkedList<InvoiceItemModelDao>();
+
+ invoices.put(invoice.getId(), invoice);
+ for (final InvoiceItemModelDao invoiceItemModelDao : invoice.getInvoiceItems()) {
+ final InvoiceItemModelDao oldItemOrNull = items.put(invoiceItemModelDao.getId(), invoiceItemModelDao);
+ if (oldItemOrNull == null) {
+ createdItems.add(invoiceItemModelDao);
+ }
+ }
+ accountRecordIds.put(invoice.getAccountId(), context.getAccountRecordId());
+
+ return createdItems;
+ }
+
+ @Override
public InvoiceModelDao getById(final UUID id, final InternalTenantContext context) {
synchronized (monitor) {
return invoices.get(id);
@@ -305,23 +328,11 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
}
@Override
- public List<InvoiceItemModelDao> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate,
- final Iterable<InvoiceItemModelDao> charges, final InternalCallContext context) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public InvoiceItemModelDao getCreditById(final UUID creditId, final InternalTenantContext context) throws InvoiceApiException {
throw new UnsupportedOperationException();
}
@Override
- public InvoiceItemModelDao insertCredit(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
- final Currency currency, final InternalCallContext context) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public InvoiceItemModelDao insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
final LocalDate effectiveDate, @Nullable final BigDecimal amount,
@Nullable final Currency currency, final InternalCallContext context) {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 7345e13..695c1c0 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
*
* The Billing Project licenses this file to you under the Apache License, version 2.0
* (the "License"); you may not use this file except in compliance with the
@@ -26,6 +26,8 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
@@ -815,11 +817,13 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID accountId = account.getId();
final UUID bundleId = UUID.randomUUID();
- invoiceDao.insertCredit(accountId, null, new BigDecimal("20.0"), new LocalDate(), Currency.USD, context);
+ createCredit(accountId, clock.getUTCToday(), new BigDecimal("20.0"));
final String description = UUID.randomUUID().toString();
+ final InvoiceModelDao invoiceForExternalCharge = new InvoiceModelDao(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
final InvoiceItemModelDao externalCharge = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(null, accountId, bundleId, description, clock.getUTCToday(), new BigDecimal("15.0"), Currency.USD));
- final InvoiceItemModelDao charge = invoiceDao.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItemModelDao>of(externalCharge), context).get(0);
+ invoiceForExternalCharge.addInvoiceItem(externalCharge);
+ final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceForExternalCharge), context).get(0);
final InvoiceModelDao newInvoice = invoiceDao.getById(charge.getInvoiceId(), context);
final List<InvoiceItemModelDao> items = newInvoice.getInvoiceItems();
@@ -930,7 +934,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
final BigDecimal creditAmount = new BigDecimal("5.0");
- invoiceDao.insertCredit(accountId, null, creditAmount, effectiveDate, Currency.USD, context);
+ createCredit(accountId, effectiveDate, creditAmount);
final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(context);
assertEquals(invoices.size(), 1);
@@ -999,7 +1003,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
// Create the credit item
final LocalDate effectiveDate = new LocalDate(2011, 3, 1);
- invoiceDao.insertCredit(accountId, invoice1.getId(), creditAmount, effectiveDate, Currency.USD, context);
+ createCredit(accountId, invoice1.getId(), effectiveDate, creditAmount);
final List<InvoiceModelDao> invoices = invoiceDao.getAllInvoicesByAccount(context);
assertEquals(invoices.size(), 1);
@@ -1600,4 +1604,28 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 10.00);
invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
}
+
+ private void createCredit(final UUID accountId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
+ createCredit(accountId, null, effectiveDate, creditAmount);
+ }
+
+ private void createCredit(final UUID accountId, @Nullable final UUID invoiceId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
+ final InvoiceModelDao invoiceModelDao;
+ if (invoiceId == null) {
+ invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD);
+
+ } else {
+ invoiceModelDao = invoiceDao.getById(invoiceId, context);
+ }
+ final CreditAdjInvoiceItem invoiceItem = new CreditAdjInvoiceItem(UUID.randomUUID(),
+ context.getCreatedDate(),
+ invoiceModelDao.getId(),
+ accountId,
+ effectiveDate,
+ // Note! The amount is negated here!
+ creditAmount.negate(),
+ invoiceModelDao.getCurrency());
+ invoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(invoiceItem));
+ invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), context);
+ }
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index de92ab1..a01f7b8 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -109,7 +109,7 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Inject
protected TestInvoiceNotificationQListener testInvoiceNotificationQListener;
@Inject
- protected OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
+ protected InvoicePluginDispatcher invoicePluginDispatcher;
@Override
protected KillbillConfigSource getConfigSource() {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index 1b2a4d0..edb4c64 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -93,8 +93,8 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
final DateTime target = new DateTime();
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
- final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- internalCallContextFactory, invoiceNotifier, locker, busService.getBus(),
+ final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
+ internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
clock);
Invoice invoice = dispatcher.processAccount(accountId, target, new DryRunFutureDateArguments(), context);
@@ -146,8 +146,8 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
- final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- internalCallContextFactory, invoiceNotifier, locker, busService.getBus(),
+ final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
+ internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
clock);
final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), null, context);
@@ -204,8 +204,8 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
null, "planName", "phaseName", null, startDate, endDate, new BigDecimal("23.9"), new BigDecimal("23.9"), Currency.EUR, null);
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
- final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- internalCallContextFactory, invoiceNotifier, locker, busService.getBus(),
+ final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
+ internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
clock);
final Map<UUID, List<DateTime>> result = dispatcher.createNextFutureNotificationDate(Collections.singletonList(item), null, dateAndTimeZoneContext);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 4fc02ca..61e5082 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -63,13 +63,11 @@ import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
import org.killbill.billing.invoice.dao.InvoicePaymentSqlDao;
import org.killbill.billing.invoice.generator.InvoiceGenerator;
import org.killbill.billing.invoice.notification.NullInvoiceNotifier;
-import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.junction.BillingInternalApi;
import org.killbill.billing.lifecycle.api.BusService;
import org.killbill.billing.mock.MockAccountBuilder;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
@@ -143,7 +141,7 @@ public class TestInvoiceHelper {
private final InvoiceGenerator generator;
private final BillingInternalApi billingApi;
private final AccountInternalApi accountApi;
- private final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
+ private final InvoicePluginDispatcher invoicePluginDispatcher;
private final AccountUserApi accountUserApi;
private final SubscriptionBaseInternalApi subscriptionApi;
private final BusService busService;
@@ -158,14 +156,14 @@ public class TestInvoiceHelper {
private final InvoiceItemSqlDao invoiceItemSqlDao;
@Inject
- public TestInvoiceHelper(final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry, final InvoiceGenerator generator, final IDBI dbi,
- final BillingInternalApi billingApi, final AccountInternalApi accountApi, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
+ public TestInvoiceHelper(final InvoiceGenerator generator, final IDBI dbi,
+ final BillingInternalApi billingApi, final AccountInternalApi accountApi, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final InternalCallContext internalCallContext,
final InternalCallContextFactory internalCallContextFactory) {
- this.pluginRegistry = pluginRegistry;
this.generator = generator;
this.billingApi = billingApi;
this.accountApi = accountApi;
+ this.invoicePluginDispatcher = invoicePluginDispatcher;
this.accountUserApi = accountUserApi;
this.subscriptionApi = subscriptionApi;
this.busService = busService;
@@ -195,8 +193,8 @@ public class TestInvoiceHelper {
Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
- final InvoiceDispatcher dispatcher = new InvoiceDispatcher(pluginRegistry, generator, accountApi, billingApi, subscriptionApi,
- invoiceDao, internalCallContextFactory, invoiceNotifier, locker, busService.getBus(),
+ final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi,
+ invoiceDao, internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
clock);
Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, new DryRunFutureDateArguments(), internalCallContext);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 47f8b61..fdd128d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -779,9 +779,6 @@ public class InvoiceResource extends JaxRsResourceBase {
return result.isEmpty() ? Response.status(Status.NOT_FOUND).build() : Response.status(Status.OK).entity(result.get(0)).build();
}
-
-
-
@Timed
@GET
@Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
diff --git a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
index 5977c7e..ef60173 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/dao/DefaultAuditDao.java
@@ -68,7 +68,7 @@ public class DefaultAuditDao implements AuditDao {
// Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
// We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
// Since we want to stream the results out, we don't want to auto-commit when this method returns.
- final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+ final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemandForStreamingResults(EntitySqlDao.class);
final Iterator<AuditLogModelDao> auditLogsForAccountRecordId = auditSqlDao.getAuditLogsForAccountRecordId(context);
final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForAccountRecordId, context);
@@ -87,7 +87,7 @@ public class DefaultAuditDao implements AuditDao {
// Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
// We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
// Since we want to stream the results out, we don't want to auto-commit when this method returns.
- final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+ final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemandForStreamingResults(EntitySqlDao.class);
final Iterator<AuditLogModelDao> auditLogsForTableNameAndAccountRecordId = auditSqlDao.getAuditLogsForTableNameAndAccountRecordId(actualTableName, context);
final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForTableNameAndAccountRecordId, context);
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationSqlDaoHelper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationSqlDaoHelper.java
index 38c8d91..b31c438 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationSqlDaoHelper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/DefaultPaginationSqlDaoHelper.java
@@ -50,7 +50,7 @@ public class DefaultPaginationSqlDaoHelper {
// We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
// Since we want to stream the results out, we don't want to auto-commit when this method returns.
- final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemand(sqlDaoClazz);
+ final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemandForStreamingResults(sqlDaoClazz);
final Long totalCount = sqlDao.getCount(context);
final Iterator<M> results = paginationIteratorBuilder.build((S) sqlDao, limit, context);
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
index 0704baa..7b50fa2 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityDaoBase.java
@@ -120,7 +120,7 @@ public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entit
public Pagination<M> getAll(final InternalTenantContext context) {
// We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
// Since we want to stream the results out, we don't want to auto-commit when this method returns.
- final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemand(realSqlDao);
+ final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemandForStreamingResults(realSqlDao);
// Note: we need to perform the count before streaming the results, as the connection
// will be busy as we stream the results out. This is also why we cannot use
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
index a8cfdfa..857ab32 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -80,7 +80,11 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
}
}
- public <M extends EntityModelDao<E>, E extends Entity, T extends EntitySqlDao<M, E>> T onDemand(final Class<T> sqlObjectType) {
+ //
+ // This is only used in the pagination APIs when streaming results. We want to keep the connection open, and also there is no need
+ // to send bus events, record notifications where we need to keep the Connection through the jDBI Handle.
+ //
+ public <M extends EntityModelDao<E>, E extends Entity, T extends EntitySqlDao<M, E>> T onDemandForStreamingResults(final Class<T> sqlObjectType) {
return dbi.onDemand(sqlObjectType);
}