killbill-memoizeit
Changes
invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java 59(+35 -24)
invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java 8(+4 -4)
Details
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index cc1c061..c54ef47 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2011 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:
*
@@ -22,15 +24,10 @@ import java.util.Map;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-
-import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.invoice.api.Invoice;
-import org.killbill.billing.invoice.api.InvoiceApiException;
-import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.Currency;
public interface InvoiceInternalApi {
@@ -67,15 +64,13 @@ public interface InvoiceInternalApi {
public InvoicePayment createRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
String transactionExternalKey, InternalCallContext context) throws InvoiceApiException;
-
public InvoicePayment createChargeback(UUID paymentId, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
/**
* Rebalance CBA for account which have credit and unpaid invoices
*
* @param accountId account id
- * @param context the callcontext
+ * @param context the callcontext
*/
public void consumeExistingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) throws InvoiceApiException;
-
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
new file mode 100644
index 0000000..91919ac
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 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.api;
+
+import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.InvoicePluginDispatcher;
+import org.killbill.billing.invoice.dao.InvoiceDao;
+import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
+import org.killbill.billing.invoice.dao.InvoiceModelDao;
+import org.killbill.billing.invoice.model.InvoiceItemFactory;
+import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.globallocker.LockerType;
+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.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class InvoiceApiHelper {
+
+ private static final Logger log = LoggerFactory.getLogger(InvoiceApiHelper.class);
+
+ private static final int NB_LOCK_TRY = 5;
+
+ private final InvoicePluginDispatcher invoicePluginDispatcher;
+ private final InvoiceDao dao;
+ private final GlobalLocker locker;
+ private final InternalCallContextFactory internalCallContextFactory;
+
+ @Inject
+ public InvoiceApiHelper(final InvoicePluginDispatcher invoicePluginDispatcher, final InvoiceDao dao, final GlobalLocker locker, final InternalCallContextFactory internalCallContextFactory) {
+ this.invoicePluginDispatcher = invoicePluginDispatcher;
+ this.dao = dao;
+ this.locker = locker;
+ this.internalCallContextFactory = internalCallContextFactory;
+ }
+
+ public 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();
+ }
+ }
+ }
+
+ /**
+ * Create an adjustment for a given invoice item. This just creates the object in memory, it doesn't write it to disk.
+ *
+ * @param invoiceToBeAdjusted the invoice
+ * @param invoiceItemId the invoice item id to adjust
+ * @param positiveAdjAmount the amount to adjust. Pass null to adjust the full amount of the original item
+ * @param currency the currency of the amount. Pass null to default to the original currency used
+ * @param effectiveDate adjustment effective date, in the account timezone
+ * @return the adjustment item
+ */
+ public InvoiceItem createAdjustmentItem(final Invoice invoiceToBeAdjusted, final UUID invoiceItemId,
+ @Nullable final BigDecimal positiveAdjAmount, @Nullable final Currency currency,
+ final LocalDate effectiveDate, final InternalCallContext context) throws InvoiceApiException {
+ final InvoiceItem invoiceItemToBeAdjusted = Iterables.<InvoiceItem>tryFind(invoiceToBeAdjusted.getInvoiceItems(),
+ new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(final InvoiceItem input) {
+ return input.getId().equals(invoiceItemId);
+ }
+ }).orNull();
+ if (invoiceItemToBeAdjusted == null) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
+ }
+
+ // Retrieve the amount and currency if needed
+ final BigDecimal amountToAdjust = Objects.firstNonNull(positiveAdjAmount, invoiceItemToBeAdjusted.getAmount());
+ // Check the specified currency matches the one of the existing invoice
+ final Currency currencyForAdjustment = Objects.firstNonNull(currency, invoiceItemToBeAdjusted.getCurrency());
+ if (invoiceItemToBeAdjusted.getCurrency() != currencyForAdjustment) {
+ throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, currency, invoiceItemToBeAdjusted.getCurrency());
+ }
+
+ // Finally, create the adjustment
+ return new ItemAdjInvoiceItem(UUID.randomUUID(),
+ context.getCreatedDate(),
+ invoiceItemToBeAdjusted.getInvoiceId(),
+ invoiceItemToBeAdjusted.getAccountId(),
+ effectiveDate,
+ null,
+ // Note! The amount is negated here!
+ amountToAdjust.negate(),
+ currencyForAdjustment,
+ invoiceItemToBeAdjusted.getId());
+ }
+
+ 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);
+ }
+ }));
+ }
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index 3ac0eb6..db51bbb 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.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:
*
@@ -25,37 +27,33 @@ import java.util.UUID;
import javax.inject.Inject;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
-import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.WithAccountLock;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.DefaultInvoicePayment;
-import org.killbill.billing.invoice.notification.NextBillingDatePoster;
-import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
@@ -63,18 +61,16 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceInternalApi.class);
private final InvoiceDao dao;
- private final NextBillingDatePoster nextBillingDatePoster;
- private final SubscriptionBaseInternalApi subscriptionBaseApi;
- private final Clock clock;
+ private final InvoiceApiHelper invoiceApiHelper;
+ private final InternalCallContextFactory internalCallContextFactory;
@Inject
- public DefaultInvoiceInternalApi(final InvoiceDao dao, final SubscriptionBaseInternalApi subscriptionBaseApi,
- final Clock clock,
- final NextBillingDatePoster nextBillingDatePoster) {
+ public DefaultInvoiceInternalApi(final InvoiceDao dao,
+ final InvoiceApiHelper invoiceApiHelper,
+ final InternalCallContextFactory internalCallContextFactory) {
this.dao = dao;
- this.clock = clock;
- this.subscriptionBaseApi = subscriptionBaseApi;
- this.nextBillingDatePoster = nextBillingDatePoster;
+ this.invoiceApiHelper = invoiceApiHelper;
+ this.internalCallContextFactory = internalCallContextFactory;
}
@Override
@@ -134,7 +130,22 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.PAYMENT_REFUND_AMOUNT_NEGATIVE_OR_NULL, paymentId, amount);
}
- return new DefaultInvoicePayment(dao.createRefund(paymentId, amount, isInvoiceAdjusted, invoiceItemIdsWithAmounts, transactionExternalKey, context));
+
+ final InvoicePaymentModelDao refund = dao.createRefund(paymentId, amount, isInvoiceAdjusted, invoiceItemIdsWithAmounts, transactionExternalKey, context);
+
+ // See https://github.com/killbill/killbill/issues/265
+ final CallContext callContext = internalCallContextFactory.createCallContext(context);
+ final Invoice invoice = getInvoiceById(refund.getInvoiceId(), context);
+ final UUID accountId = invoice.getAccountId();
+ final WithAccountLock withAccountLock = new WithAccountLock() {
+ @Override
+ public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
+ return ImmutableList.<Invoice>of(invoice);
+ }
+ };
+ final List<InvoiceItem> createdInvoiceItems = invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, callContext);
+
+ return new DefaultInvoicePayment(refund);
}
@Override
@@ -154,7 +165,7 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
return new DefaultInvoicePayment(input);
}
});
- if (invoicePayments.size() == 0) {
+ if (invoicePayments.isEmpty()) {
return null;
}
return Iterables.tryFind(invoicePayments, new Predicate<InvoicePayment>() {
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 ad59ef2..b62ab84 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
@@ -22,7 +22,6 @@ 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;
@@ -40,14 +39,14 @@ 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;
+import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceUserApi;
+import org.killbill.billing.invoice.api.WithAccountLock;
import org.killbill.billing.invoice.dao.InvoiceDao;
-import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -62,14 +61,10 @@ 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;
@@ -86,30 +81,31 @@ 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 InvoiceApiHelper invoiceApiHelper;
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 InvoicePluginDispatcher invoicePluginDispatcher, final AccountInternalApi accountUserApi,
- final GlobalLocker locker, final PersistentBus eventBus,
- final TagInternalApi tagApi, final HtmlInvoiceGenerator generator, final InternalCallContextFactory internalCallContextFactory) {
+ public DefaultInvoiceUserApi(final InvoiceDao dao,
+ final InvoiceDispatcher dispatcher,
+ final AccountInternalApi accountUserApi,
+ final PersistentBus eventBus,
+ final TagInternalApi tagApi,
+ final InvoiceApiHelper invoiceApiHelper,
+ final HtmlInvoiceGenerator generator,
+ final InternalCallContextFactory internalCallContextFactory) {
this.dao = dao;
this.dispatcher = dispatcher;
- this.invoicePluginDispatcher = invoicePluginDispatcher;
this.accountUserApi = accountUserApi;
this.tagApi = tagApi;
+ this.invoiceApiHelper = invoiceApiHelper;
this.generator = generator;
this.internalCallContextFactory = internalCallContextFactory;
- this.locker = locker;
this.eventBus = eventBus;
}
@@ -311,7 +307,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
};
- return dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
+ return invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
}
@Override
@@ -349,11 +345,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
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());
- }
+ invoiceForCredit = getInvoiceAndCheckCurrency(invoiceId, currency, context);
}
// Create the new credit
@@ -371,7 +363,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
};
- final List<InvoiceItem> creditInvoiceItems = dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
+ final List<InvoiceItem> creditInvoiceItems = invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
Preconditions.checkState(creditInvoiceItems.size() == 1, "Should have created a single credit invoice item: " + creditInvoiceItems);
return creditInvoiceItems.get(0);
@@ -391,9 +383,26 @@ 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);
+ final WithAccountLock withAccountLock = new WithAccountLock() {
+ @Override
+ public Iterable<Invoice> prepareInvoices() throws InvoiceApiException {
+ final Invoice invoice = getInvoiceAndCheckCurrency(invoiceId, currency, context);
+ final InvoiceItem adjustmentItem = invoiceApiHelper.createAdjustmentItem(invoice,
+ invoiceItemId,
+ amount,
+ currency,
+ effectiveDate,
+ internalCallContextFactory.createInternalCallContext(context));
+ invoice.addInvoiceItem(adjustmentItem);
+
+ return ImmutableList.<Invoice>of(invoice);
+ }
+ };
+
+ final List<InvoiceItem> adjustmentInvoiceItems = invoiceApiHelper.dispatchToInvoicePluginsAndInsertItems(accountId, withAccountLock, context);
+ Preconditions.checkState(adjustmentInvoiceItems.size() == 1, "Should have created a single adjustment item: " + adjustmentInvoiceItems);
+
+ return adjustmentInvoiceItems.get(0);
}
@Override
@@ -448,64 +457,12 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}));
}
- 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();
- }
+ private Invoice getInvoiceAndCheckCurrency(final UUID invoiceId, @Nullable final Currency currency, final TenantContext context) throws InvoiceApiException {
+ final Invoice invoice = getInvoice(invoiceId, context);
+ // Check the specified currency matches the one of the existing invoice
+ if (currency != null && invoice.getCurrency() != currency) {
+ throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, currency, invoice.getCurrency());
}
+ return invoice;
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/WithAccountLock.java b/invoice/src/main/java/org/killbill/billing/invoice/api/WithAccountLock.java
new file mode 100644
index 0000000..165c100
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/WithAccountLock.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 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.api;
+
+public interface WithAccountLock {
+
+ public Iterable<Invoice> prepareInvoices() throws InvoiceApiException;
+}
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 e325360..23e49be 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
@@ -243,9 +243,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
+ boolean madeChanges = false;
+
// Create the invoice if needed
if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
invoiceSqlDao.create(invoiceModelDao, context);
+ madeChanges = true;
}
// Create the invoice items if needed
@@ -253,14 +256,17 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
if (transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context) == null) {
transInvoiceItemSqlDao.create(invoiceItemModelDao, context);
createdInvoiceItems.add(transInvoiceItemSqlDao.getById(invoiceItemModelDao.getId().toString(), context));
+ madeChanges = true;
}
}
- cbaDao.addCBAComplexityFromTransaction(invoiceModelDao.getId(), entitySqlDaoWrapperFactory, context);
+ if (madeChanges) {
+ 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);
+ // 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;
@@ -650,26 +656,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@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) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
- @Override
- public InvoiceItemModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceItemModelDao invoiceItemAdjustment = invoiceDaoHelper.createAdjustmentItem(entitySqlDaoWrapperFactory, invoiceId, invoiceItemId, positiveAdjAmount,
- currency, effectiveDate, context);
- invoiceDaoHelper.insertItem(entitySqlDaoWrapperFactory, invoiceItemAdjustment, context);
-
- cbaDao.addCBAComplexityFromTransaction(invoiceId, entitySqlDaoWrapperFactory, context);
-
- notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceId, accountId, context.getUserToken(), context);
-
- return invoiceItemAdjustment;
- }
- });
- }
-
- @Override
public void deleteCBA(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final InternalCallContext context) throws InvoiceApiException {
transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
@Override
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 ad63d71..7f2ed99 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
@@ -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:
*
@@ -114,22 +116,6 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
InvoiceItemModelDao getCreditById(UUID creditId, InternalTenantContext context) throws InvoiceApiException;
/**
- * Adjust an invoice item.
- *
- * @param accountId the account id
- * @param invoiceId the invoice id
- * @param invoiceItemId the invoice item id to adjust
- * @param effectiveDate adjustment effective date, in the account timezone
- * @param amount the amount to adjust. Pass null to adjust the full amount of the original item
- * @param currency the currency of the amount. Pass null to default to the original currency used
- * @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);
-
- /**
* Delete a CBA item.
*
* @param accountId the account id
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 e885607..56beb81 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
@@ -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:
*
@@ -178,13 +180,12 @@ 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,
+ public InvoiceItemModelDao createAdjustmentItem(final EntitySqlDaoWrapperFactory 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 invoiceItemSqlDaoX = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
- final InvoiceItemModelDao invoiceItemToBeAXdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
+ final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+ final InvoiceItemModelDao invoiceItemToBeAdjusted = invoiceItemSqlDao.getById(invoiceItemId.toString(), context);
if (invoiceItemToBeAdjusted == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
}
@@ -205,21 +206,6 @@ public class InvoiceDaoHelper {
null, null, null, null, null, null, effectiveDate, effectiveDate, amountToAdjust.negate(), null, currencyForAdjustment, invoiceItemToBeAdjusted.getId());
}
- /**
- * Create an invoice item
- *
- * @param entitySqlDaoWrapperFactory the EntitySqlDaoWrapperFactory from the current transaction
- * @param item the invoice item to create
- * @param context the call callcontext
- */
- public InvoiceItemModelDao insertItem(final EntitySqlDaoWrapperFactory 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 entitySqlDaoWrapperFactory, final InternalTenantContext context) {
getInvoiceItemsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
getInvoicePaymentsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
index 1b56ec7..f790741 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.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
@@ -22,6 +22,7 @@ import org.killbill.billing.glue.InvoiceModule;
import org.killbill.billing.invoice.InvoiceListener;
import org.killbill.billing.invoice.InvoiceTagHandler;
import org.killbill.billing.invoice.api.DefaultInvoiceService;
+import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.invoice.api.InvoiceMigrationApi;
import org.killbill.billing.invoice.api.InvoiceNotifier;
@@ -29,6 +30,7 @@ import org.killbill.billing.invoice.api.InvoicePaymentApi;
import org.killbill.billing.invoice.api.InvoiceService;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
+import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
import org.killbill.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
import org.killbill.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
import org.killbill.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
@@ -45,7 +47,6 @@ import org.killbill.billing.invoice.notification.NextBillingDatePoster;
import org.killbill.billing.invoice.notification.NullInvoiceNotifier;
import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
import org.killbill.billing.invoice.template.bundles.DefaultResourceBundleFactory;
-import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.config.InvoiceConfig;
@@ -149,5 +150,7 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
installInvoicePaymentApi();
installInvoiceMigrationApi();
installResourceBundleFactory();
+
+ bind(InvoiceApiHelper.class).asEagerSingleton();
}
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
index 197f811..5f9477d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
@@ -64,7 +64,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
@Test(groups = "slow")
public void testFullRefundWithBothInvoiceItemAdjustments() throws Exception {
// Create an invoice with two items (30 \u20ac and 10 \u20ac)
- final Invoice invoice = createAndPersistInvoice(invoiceDao, clock,
+ final Invoice invoice = createAndPersistInvoice(invoiceUtil, invoiceDao, clock,
ImmutableList.<BigDecimal>of(THIRTY, BigDecimal.TEN), CURRENCY, internalCallContext);
// Fully adjust both items
@@ -78,7 +78,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
@Test(groups = "slow")
public void testPartialRefundWithSingleInvoiceItemAdjustment() throws Exception {
// Create an invoice with two items (30 \u20ac and 10 \u20ac)
- final Invoice invoice = createAndPersistInvoice(invoiceDao, clock,
+ final Invoice invoice = createAndPersistInvoice(invoiceUtil, invoiceDao, clock,
ImmutableList.<BigDecimal>of(THIRTY, BigDecimal.TEN), CURRENCY, internalCallContext);
// Fully adjust both items
@@ -91,7 +91,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
@Test(groups = "slow")
public void testPartialRefundWithTwoInvoiceItemAdjustment() throws Exception {
// Create an invoice with two items (30 \u20ac and 10 \u20ac)
- final Invoice invoice = createAndPersistInvoice(invoiceDao, clock,
+ final Invoice invoice = createAndPersistInvoice(invoiceUtil, invoiceDao, clock,
ImmutableList.<BigDecimal>of(THIRTY, BigDecimal.TEN), CURRENCY, internalCallContext);
// Adjust partially both items: the invoice posted was 40 \u20ac, but we should really just have charged you 2 \u20ac
final ImmutableMap<UUID, BigDecimal> adjustments = ImmutableMap.<UUID, BigDecimal>of(invoice.getInvoiceItems().get(0).getId(), new BigDecimal("29"),
@@ -101,7 +101,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
private void verifyRefund(final BigDecimal invoiceAmount, final BigDecimal refundAmount, final BigDecimal finalInvoiceAmount,
final boolean adjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) throws InvoiceApiException {
- final Invoice invoice = createAndPersistInvoice(invoiceDao, clock, invoiceAmount, CURRENCY, internalCallContext);
+ final Invoice invoice = createAndPersistInvoice(invoiceUtil, invoiceDao, clock, invoiceAmount, CURRENCY, internalCallContext);
verifyRefund(invoice, invoiceAmount, refundAmount, finalInvoiceAmount, adjusted, invoiceItemIdsWithAmounts);
}
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 fe64af0..cf40438 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
@@ -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:
*
@@ -25,8 +27,6 @@ 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.callcontext.InternalCallContext;
@@ -333,13 +333,6 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
}
@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) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public BigDecimal getAccountCBA(final UUID accountId, final InternalTenantContext context) {
return null;
}
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 695c1c0..1070c2b 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
@@ -821,7 +821,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
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 externalCharge = new InvoiceItemModelDao(new ExternalChargeInvoiceItem(invoiceForExternalCharge.getId(), accountId, bundleId, description, clock.getUTCToday(), new BigDecimal("15.0"), Currency.USD));
invoiceForExternalCharge.addInvoiceItem(externalCharge);
final InvoiceItemModelDao charge = invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceForExternalCharge), context).get(0);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java
index 313ffac..c66af70 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.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:
*
@@ -22,12 +24,15 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
-import org.mockito.Mockito;
-import org.testng.Assert;
-
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.TestInvoiceHelper;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
@@ -35,10 +40,9 @@ import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
-import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.clock.Clock;
-import org.killbill.billing.entity.EntityPersistenceException;
-import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.mockito.Mockito;
+import org.testng.Assert;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -47,28 +51,41 @@ public class InvoiceTestUtils {
private InvoiceTestUtils() {}
- public static Invoice createAndPersistInvoice(final InvoiceDao invoiceDao,
+ public static Invoice createAndPersistInvoice(final TestInvoiceHelper testInvoiceHelper,
+ final InvoiceDao invoiceDao,
final Clock clock,
final BigDecimal amount,
final Currency currency,
final InternalCallContext internalCallContext) {
try {
- return createAndPersistInvoice(invoiceDao, clock, ImmutableList.<BigDecimal>of(amount),
- currency, internalCallContext);
- } catch (EntityPersistenceException e) {
+ return createAndPersistInvoice(testInvoiceHelper,
+ invoiceDao,
+ clock,
+ ImmutableList.<BigDecimal>of(amount),
+ currency,
+ internalCallContext);
+ } catch (final EntityPersistenceException e) {
Assert.fail(e.getMessage());
return null;
}
}
- public static Invoice createAndPersistInvoice(final InvoiceDao invoiceDao,
+ public static Invoice createAndPersistInvoice(final TestInvoiceHelper testInvoiceHelper,
+ final InvoiceDao invoiceDao,
final Clock clock,
- final List<BigDecimal> amounts,
+ final Iterable<BigDecimal> amounts,
final Currency currency,
final InternalCallContext internalCallContext) throws EntityPersistenceException {
final Invoice invoice = Mockito.mock(Invoice.class);
final UUID invoiceId = UUID.randomUUID();
- final UUID accountId = UUID.randomUUID();
+ final UUID accountId;
+ try {
+ final Account account = testInvoiceHelper.createAccount(internalCallContext.toCallContext(null));
+ accountId = account.getId();
+ } catch (final AccountApiException e) {
+ Assert.fail(e.getMessage());
+ return null;
+ }
Mockito.when(invoice.getId()).thenReturn(invoiceId);
Mockito.when(invoice.getAccountId()).thenReturn(accountId);