killbill-memoizeit

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

invoice/pom.xml 2(+1 -1)

jaxrs/pom.xml 2(+1 -1)

junction/pom.xml 2(+1 -1)

overdue/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 14(+13 -1)

server/pom.xml 2(+1 -1)

usage/pom.xml 2(+1 -1)

util/pom.xml 2(+1 -1)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index 8247e9d..d21272b 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 78c11e2..5c029c2 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index a4123c4..a923e0e 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
index 545e04d..b65cf34 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoicePaymentApi.java
@@ -34,9 +34,9 @@ public interface InvoicePaymentApi {
      */
     public List<Invoice> getAllInvoicesByAccount(UUID accountId);
 
-    public Invoice getInvoice(UUID invoiceId);
+    public Invoice getInvoice(UUID invoiceId) throws InvoiceApiException;
 
-    public Invoice getInvoiceForPaymentId(UUID paymentId);
+    public Invoice getInvoiceForPaymentId(UUID paymentId) throws InvoiceApiException;
 
     public List<InvoicePayment> getInvoicePayments(UUID paymentId);
 
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 3b78afe..fa58913 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -62,7 +62,7 @@ public interface InvoiceUserApi {
      * @param invoiceId invoice id
      * @return the invoice
      */
-    public Invoice getInvoice(UUID invoiceId);
+    public Invoice getInvoice(UUID invoiceId) throws InvoiceApiException;
 
     /**
      * Retrieve an invoice by invoice number.
@@ -70,7 +70,7 @@ public interface InvoiceUserApi {
      * @param number invoice number
      * @return the invoice
      */
-    public Invoice getInvoiceByNumber(Integer number);
+    public Invoice getInvoiceByNumber(Integer number) throws InvoiceApiException;
 
     /**
      * Record a payment for an invoice.

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 99eb803..ae42097 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
index 1cc24d8..e3cca6f 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/InvoiceChecker.java
@@ -16,6 +16,11 @@
 
 package com.ning.billing.beatrix.util;
 
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
@@ -28,22 +33,17 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 
-import com.google.common.collect.ImmutableList;
-import com.google.inject.Inject;
-
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
-
 public class InvoiceChecker {
 
     private static final Logger log = LoggerFactory.getLogger(InvoiceChecker.class);
@@ -57,29 +57,29 @@ public class InvoiceChecker {
         this.entitlementApi = entitlementApi;
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final ExpectedItemCheck... expected) {
+    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final ExpectedItemCheck... expected) throws InvoiceApiException {
         checkInvoice(accountId, invoiceOrderingNumber, ImmutableList.<ExpectedItemCheck>copyOf(expected));
     }
 
-    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final List<ExpectedItemCheck> expected) {
+    public void checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
         Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
         final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
         checkInvoice(invoice.getId(), expected);
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final ExpectedItemCheck... expected) {
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final ExpectedItemCheck... expected) throws InvoiceApiException {
         checkRepairedInvoice(accountId, invoiceNb, ImmutableList.<ExpectedItemCheck>copyOf(expected));
     }
 
-    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final List<ExpectedItemCheck> expected) {
+    public void checkRepairedInvoice(final UUID accountId, final int invoiceNb, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
         Assert.assertTrue(invoices.size() > invoiceNb);
         final Invoice invoice = invoices.get(invoiceNb - 1);
         checkInvoice(invoice.getId(), expected);
     }
 
-    public void checkInvoice(final UUID invoiceId, final List<ExpectedItemCheck> expected) {
+    public void checkInvoice(final UUID invoiceId, final List<ExpectedItemCheck> expected) throws InvoiceApiException {
         final Invoice invoice = invoiceUserApi.getInvoice(invoiceId);
         Assert.assertNotNull(invoice);
 

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index c7ae798..12580bf 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index a9b7295..826233a 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 1534393..f59f789 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
index e6bb878..ab7cccc 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/invoice/DefaultInvoicePaymentApi.java
@@ -18,7 +18,6 @@
 package com.ning.billing.invoice.api.invoice;
 
 import java.math.BigDecimal;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -26,6 +25,9 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
@@ -37,10 +39,6 @@ import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.util.callcontext.CallContext;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.inject.Inject;
-
 public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
 
     private static final WithInvoiceApiException<InvoicePayment> invoicePaymentWithException = new WithInvoiceApiException<InvoicePayment>();
@@ -63,12 +61,12 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     }
 
     @Override
-    public Invoice getInvoice(final UUID invoiceId) {
+    public Invoice getInvoice(final UUID invoiceId) throws InvoiceApiException {
         return dao.getById(invoiceId);
     }
 
     @Override
-    public Invoice getInvoiceForPaymentId(final UUID paymentId) {
+    public Invoice getInvoiceForPaymentId(final UUID paymentId) throws InvoiceApiException {
         final UUID invoiceIdStr = dao.getInvoiceIdByPaymentId(paymentId);
         return invoiceIdStr == null ? null : dao.getById(invoiceIdStr);
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index bd524b2..11efa03 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -27,6 +27,7 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
+import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
@@ -49,8 +50,6 @@ import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
 
-import com.google.inject.Inject;
-
 public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
     private final InvoiceDao dao;
@@ -91,12 +90,12 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public Invoice getInvoice(final UUID invoiceId) {
+    public Invoice getInvoice(final UUID invoiceId) throws InvoiceApiException {
         return dao.getById(invoiceId);
     }
 
     @Override
-    public Invoice getInvoiceByNumber(final Integer number) {
+    public Invoice getInvoiceByNumber(final Integer number) throws InvoiceApiException {
         return dao.getByNumber(number);
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
index ba3ec0c..b03626e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/AuditedInvoiceDao.java
@@ -30,10 +30,17 @@ import org.joda.time.LocalDate;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.exceptions.TransactionFailedException;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
@@ -65,13 +72,6 @@ import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.tag.ControlTagType;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.inject.Inject;
-
 public class AuditedInvoiceDao implements InvoiceDao {
 
     private static final Logger log = LoggerFactory.getLogger(AuditedInvoiceDao.class);
@@ -150,25 +150,36 @@ public class AuditedInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public Invoice getById(final UUID invoiceId) {
-        return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
-            @Override
-            public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
-                final Invoice invoice = invoiceDao.getById(invoiceId.toString());
-
-                if (invoice != null) {
+    public Invoice getById(final UUID invoiceId) throws InvoiceApiException {
+        try {
+            return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
+                @Override
+                public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+                    final Invoice invoice = invoiceDao.getById(invoiceId.toString());
+                    if (invoice == null) {
+                        throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+                    }
                     populateChildren(invoice, invoiceDao);
+                    return invoice;
                 }
-
-                return invoice;
+            });
+        } catch (TransactionFailedException e) {
+            if (e.getCause() instanceof InvoiceApiException) {
+                throw (InvoiceApiException) e.getCause();
+            } else {
+                throw e;
             }
-        });
+        }
     }
 
     @Override
-    public Invoice getByNumber(final Integer number) {
+    public Invoice getByNumber(final Integer number) throws InvoiceApiException {
         // The invoice number is just the record id
-        return invoiceSqlDao.getByRecordId(number.longValue());
+        final Invoice result = invoiceSqlDao.getByRecordId(number.longValue());
+        if (result == null) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, number);
+        }
+        return result;
     }
 
     @Override
@@ -576,7 +587,8 @@ public class AuditedInvoiceDao implements InvoiceDao {
 
     @Override
     public InvoiceItem insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId, final String description,
-                                            final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final CallContext context) {
+                                            final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final CallContext context)
+                                                    throws InvoiceApiException {
         return invoiceSqlDao.inTransaction(new Transaction<InvoiceItem, InvoiceSqlDao>() {
             @Override
             public InvoiceItem inTransaction(final InvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
@@ -595,9 +607,22 @@ public class AuditedInvoiceDao implements InvoiceDao {
                 final InvoiceItemSqlDao transInvoiceItemDao = transactional.become(InvoiceItemSqlDao.class);
                 transInvoiceItemDao.create(externalCharge, context);
 
+                // At this point, reread the invoice and figure out if we need to consume some of the CBA
+                final Invoice invoice = transactional.getById(invoiceIdForExternalCharge.toString());
+                if (invoice == null) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceIdForExternalCharge);
+                }
+                populateChildren(invoice, transactional);
+
+                final BigDecimal accountCbaAvailable = getAccountCBAFromTransaction(invoice.getAccountId(), transactional);
+                if (accountCbaAvailable.compareTo(BigDecimal.ZERO) > 0 && invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+                    final BigDecimal cbaAmountToConsume = accountCbaAvailable.compareTo(invoice.getBalance()) > 0 ? invoice.getBalance().negate() : accountCbaAvailable.negate();
+                    final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate().toLocalDate(), cbaAmountToConsume, invoice.getCurrency());
+                    transInvoiceItemDao.create(cbaAdjItem, context);
+                }
+
                 // Notify the bus since the balance of the invoice changed
                 notifyBusOfInvoiceAdjustment(transactional, invoiceId, accountId, context.getUserToken());
-
                 return externalCharge;
             }
         });
@@ -742,7 +767,6 @@ public class AuditedInvoiceDao implements InvoiceDao {
         for (final Invoice cur : invoices) {
             cba = cba.add(cur.getCBAAmount());
         }
-
         return cba;
     }
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index dbf2edb..207c1de 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -37,9 +37,9 @@ public interface InvoiceDao {
 
     void create(final Invoice invoice, final int billCycleDayUTC, final boolean isRealInvoice, final CallContext context);
 
-    Invoice getById(final UUID id);
+    Invoice getById(final UUID id) throws InvoiceApiException;
 
-    Invoice getByNumber(final Integer number);
+    Invoice getByNumber(final Integer number) throws InvoiceApiException;
 
     List<Invoice> get();
 
@@ -119,7 +119,7 @@ public interface InvoiceDao {
      * @return the newly created external charge invoice item
      */
     InvoiceItem insertExternalCharge(final UUID accountId, @Nullable final UUID invoiceId, @Nullable final UUID bundleId, @Nullable final String description,
-                                     final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final CallContext context);
+                                     final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException;
 
     /**
      * Retrieve a credit by id.
diff --git a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
index 43a1d70..c1e3140 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.generator;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -56,6 +57,9 @@ import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
 import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.util.clock.Clock;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
 import com.google.inject.Inject;
 
 public class DefaultInvoiceGenerator implements InvoiceGenerator {
@@ -78,9 +82,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
      */
     @Override
     public Invoice generateInvoice(final UUID accountId, @Nullable final BillingEventSet events,
-                                   @Nullable final List<Invoice> existingInvoices,
-                                   final LocalDate targetDate, final DateTimeZone accountTimeZone,
-                                   final Currency targetCurrency) throws InvoiceApiException {
+            @Nullable final List<Invoice> existingInvoices,
+            final LocalDate targetDate, final DateTimeZone accountTimeZone,
+            final Currency targetCurrency) throws InvoiceApiException {
         if ((events == null) || (events.size() == 0) || events.isAccountAutoInvoiceOff()) {
             return null;
         }
@@ -94,8 +98,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
             for (final Invoice invoice : existingInvoices) {
                 for (final InvoiceItem item : invoice.getInvoiceItems()) {
                     if (item.getSubscriptionId() == null || // Always include migration invoices, credits, external charges etc.
-                        !events.getSubscriptionIdsWithAutoInvoiceOff()
-                               .contains(item.getSubscriptionId())) { //don't add items with auto_invoice_off tag
+                            !events.getSubscriptionIdsWithAutoInvoiceOff()
+                            .contains(item.getSubscriptionId())) { //don't add items with auto_invoice_off tag
                         existingItems.add(item);
                     }
                 }
@@ -127,7 +131,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     }
 
     void generateCBAForExistingInvoices(final UUID accountId, final List<Invoice> existingInvoices,
-                                        final List<InvoiceItem> proposedItems, final Currency currency) {
+            final List<InvoiceItem> proposedItems, final Currency currency) {
         // Determine most accurate invoice balances up to this point
         final Map<UUID, BigDecimal> amountOwedByInvoice = new HashMap<UUID, BigDecimal>();
 
@@ -159,16 +163,48 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     void addRepairedItems(final List<InvoiceItem> existingItems, final List<InvoiceItem> proposedItems) {
         for (final InvoiceItem existingItem : existingItems) {
             if (existingItem.getInvoiceItemType() == InvoiceItemType.RECURRING ||
-                existingItem.getInvoiceItemType() == InvoiceItemType.FIXED) {
-                final BigDecimal amountNegated = existingItem.getAmount() == null ? null : existingItem.getAmount().negate();
-                final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(existingItem.getInvoiceId(), existingItem.getAccountId(), existingItem.getStartDate(), existingItem.getEndDate(), amountNegated, existingItem.getCurrency(), existingItem.getId());
-                proposedItems.add(repairItem);
+                    existingItem.getInvoiceItemType() == InvoiceItemType.FIXED) {
+                final BigDecimal existingAdjustedPositiveAmount = getAdjustedPositiveAmount(existingItems, existingItem.getId());
+                final BigDecimal amountNegated = existingItem.getAmount() == null ? null : existingItem.getAmount().subtract(existingAdjustedPositiveAmount).negate();
+                if (amountNegated.compareTo(BigDecimal.ZERO) < 0) {
+                    final RepairAdjInvoiceItem repairItem = new RepairAdjInvoiceItem(existingItem.getInvoiceId(), existingItem.getAccountId(), existingItem.getStartDate(), existingItem.getEndDate(), amountNegated, existingItem.getCurrency(), existingItem.getId());
+                    proposedItems.add(repairItem);
+                }
             }
         }
+
+    }
+
+
+    // We check to see if there are any adjustments that point to the item we are trying to repair
+    // If we did any CREDIT_ADJ or REFUND_ADJ, then we unfortunately we can't know what is the intent
+    // was as it applies to the full Invoice, so we ignore it. That might result in an extra positive CBA
+    // that would have to be corrected manually. This is the best we can do, and administrators should always
+    // use ITEM_ADJUSTEMNT rather than CREDIT_ADJ or REFUND_ADJ when possible.
+    //
+    BigDecimal getAdjustedPositiveAmount(final List<InvoiceItem> existingItems, final UUID linkedItemId) {
+        BigDecimal totalAdjustedOnItem = BigDecimal.ZERO;
+        final Collection<InvoiceItem> c = Collections2.filter(existingItems, new Predicate<InvoiceItem>() {
+            @Override
+            public boolean apply(InvoiceItem item) {
+                if (item.getInvoiceItemType() == InvoiceItemType.ITEM_ADJ &&
+                        item.getLinkedItemId() != null && item.getLinkedItemId().equals(linkedItemId)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        });
+
+        final Iterator<InvoiceItem> it = c.iterator();
+        while (it.hasNext()) {
+            totalAdjustedOnItem = totalAdjustedOnItem.add(it.next().getAmount());
+        }
+        return totalAdjustedOnItem.negate();
     }
 
     void consumeExistingCredit(final UUID invoiceId, final UUID accountId, final List<InvoiceItem> existingItems,
-                               final List<InvoiceItem> proposedItems, final Currency targetCurrency) {
+            final List<InvoiceItem> proposedItems, final Currency targetCurrency) {
         BigDecimal totalUnusedCreditAmount = BigDecimal.ZERO;
         BigDecimal totalAmountOwed = BigDecimal.ZERO;
 
@@ -230,7 +266,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
      * Removes all matching items from both submitted collections
      */
     void removeDuplicatedInvoiceItems(final List<InvoiceItem> proposedItems,
-                                      final List<InvoiceItem> existingInvoiceItems) {
+            final List<InvoiceItem> existingInvoiceItems) {
         // We can't just use sets here as order matters (we want to keep duplicated in existingInvoiceItems)
         final Iterator<InvoiceItem> proposedItemIterator = proposedItems.iterator();
         while (proposedItemIterator.hasNext()) {
@@ -269,7 +305,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     }
 
     private List<InvoiceItem> generateInvoiceItems(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
-                                                   final LocalDate targetDate, final DateTimeZone accountTimeZone, final Currency currency) throws InvoiceApiException {
+            final LocalDate targetDate, final DateTimeZone accountTimeZone, final Currency currency) throws InvoiceApiException {
         final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
 
         if (events.size() == 0) {
@@ -307,7 +343,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
     // Turn a set of events into a list of invoice items. Note that the dates on the invoice items will be rounded (granularity of a day)
     private List<InvoiceItem> processEvents(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent, @Nullable final BillingEvent nextEvent,
-                                            final LocalDate targetDate, final DateTimeZone accountTimeZone, final Currency currency) throws InvoiceApiException {
+            final LocalDate targetDate, final DateTimeZone accountTimeZone, final Currency currency) throws InvoiceApiException {
         final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
 
         // Handle fixed price items
@@ -341,13 +377,13 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
                         final BigDecimal amount = itemDatum.getNumberOfCycles().multiply(rate).setScale(NUMBER_OF_DECIMALS, ROUNDING_MODE);
 
                         final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
-                                                                                            accountId,
-                                                                                            thisEvent.getSubscription().getBundleId(),
-                                                                                            thisEvent.getSubscription().getId(),
-                                                                                            thisEvent.getPlan().getName(),
-                                                                                            thisEvent.getPlanPhase().getName(),
-                                                                                            itemDatum.getStartDate(), itemDatum.getEndDate(),
-                                                                                            amount, rate, currency);
+                                accountId,
+                                thisEvent.getSubscription().getBundleId(),
+                                thisEvent.getSubscription().getId(),
+                                thisEvent.getPlan().getName(),
+                                thisEvent.getPlanPhase().getName(),
+                                itemDatum.getStartDate(), itemDatum.getEndDate(),
+                                amount, rate, currency);
                         items.add(recurringItem);
                     }
                 }
@@ -360,15 +396,15 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
     private BillingMode instantiateBillingMode(final BillingModeType billingMode) {
         switch (billingMode) {
-            case IN_ADVANCE:
-                return new InAdvanceBillingMode();
-            default:
-                throw new UnsupportedOperationException();
+        case IN_ADVANCE:
+            return new InAdvanceBillingMode();
+        default:
+            throw new UnsupportedOperationException();
         }
     }
 
     InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent,
-                                       final LocalDate targetDate, final Currency currency) {
+            final LocalDate targetDate, final Currency currency) {
         final LocalDate roundedStartDate = new LocalDate(thisEvent.getEffectiveDate(), thisEvent.getTimeZone());
 
         if (roundedStartDate.isAfter(targetDate)) {
@@ -378,9 +414,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
 
             if (fixedPrice != null) {
                 return new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(),
-                                                 thisEvent.getSubscription().getId(),
-                                                 thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
-                                                 roundedStartDate, fixedPrice, currency);
+                        thisEvent.getSubscription().getId(),
+                        thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
+                        roundedStartDate, fixedPrice, currency);
             } else {
                 return null;
             }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index 007f2f6..bb65134 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -32,11 +32,7 @@ import org.testng.annotations.Test;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceMigrationApi;
-import com.ning.billing.invoice.api.InvoicePaymentApi;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-
-import com.google.inject.Inject;
+import com.ning.billing.invoice.api.InvoiceApiException;
 
 public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
 
@@ -63,7 +59,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
         regularInvoiceId = generateRegularInvoice(account, date_regular);
     }
 
-    private UUID createAndCheckMigrationInvoice(final UUID accountId) {
+    private UUID createAndCheckMigrationInvoice(final UUID accountId) throws InvoiceApiException {
         final UUID migrationInvoiceId = migrationApi.createMigrationInvoice(accountId, date_migrated, MIGRATION_INVOICE_AMOUNT, MIGRATION_INVOICE_CURRENCY);
         Assert.assertNotNull(migrationInvoiceId);
         //Double check it was created and values are correct
@@ -108,7 +104,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceApiTestBase {
 
     // ACCOUNT balance should reflect total of migration and non-migration invoices
     @Test(groups = "slow")
-    public void testBalance() {
+    public void testBalance() throws InvoiceApiException{
         final Invoice migrationInvoice = invoiceDao.getById(migrationInvoiceId);
         final Invoice regularInvoice = invoiceDao.getById(regularInvoiceId);
         final BigDecimal balanceOfAllInvoices = migrationInvoice.getBalance().add(regularInvoice.getBalance());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 39ee3ec..c245e29 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -78,7 +78,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceApiTestBase {
     }
 
     private void verifyExternalChargeOnNewInvoice(final BigDecimal initialAccountBalance, @Nullable final UUID bundleId,
-                                                  final BigDecimal externalChargeAmount, final InvoiceItem externalChargeInvoiceItem) {
+                                                  final BigDecimal externalChargeAmount, final InvoiceItem externalChargeInvoiceItem) throws InvoiceApiException {
         Assert.assertNotNull(externalChargeInvoiceItem.getInvoiceId());
         Assert.assertNotEquals(externalChargeInvoiceItem.getInvoiceId(), invoiceId);
         Assert.assertEquals(externalChargeInvoiceItem.getBundleId(), bundleId);
@@ -135,7 +135,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceApiTestBase {
     }
 
     private void verifyExternalChargeOnExistingInvoice(final BigDecimal initialInvoiceBalance, @Nullable final UUID bundleId,
-                                                       final BigDecimal externalChargeAmount, final InvoiceItem externalChargeInvoiceItem) {
+                                                       final BigDecimal externalChargeAmount, final InvoiceItem externalChargeInvoiceItem) throws InvoiceApiException {
         Assert.assertEquals(externalChargeInvoiceItem.getInvoiceId(), invoiceId);
         Assert.assertEquals(externalChargeInvoiceItem.getBundleId(), bundleId);
         Assert.assertEquals(externalChargeInvoiceItem.getInvoiceItemType(), InvoiceItemType.EXTERNAL_CHARGE);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
index ab3b14d..0427972 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
@@ -29,6 +29,7 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableMap;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.invoice.InvoiceTestSuite;
 import com.ning.billing.invoice.api.Invoice;
@@ -48,8 +49,6 @@ import com.ning.billing.util.tag.dao.MockTagDefinitionDao;
 import com.ning.billing.util.tag.dao.TagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
 
-import com.google.common.collect.ImmutableMap;
-
 public class TestDefaultInvoiceDao extends InvoiceTestSuite {
 
     private InvoiceSqlDao invoiceSqlDao;
@@ -63,6 +62,7 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
         Mockito.when(idbi.onDemand(InvoiceSqlDao.class)).thenReturn(invoiceSqlDao);
         Mockito.when(invoiceSqlDao.getById(Mockito.anyString())).thenReturn(Mockito.mock(Invoice.class));
         Mockito.when(invoiceSqlDao.inTransaction(Mockito.<Transaction<Void, InvoiceSqlDao>>any())).thenAnswer(new Answer() {
+            @Override
             public Object answer(final InvocationOnMock invocation) {
                 final Object[] args = invocation.getArguments();
                 try {
@@ -125,7 +125,11 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
         Mockito.when(invoiceSqlDao.getByRecordId(number.longValue())).thenReturn(invoice);
 
         Assert.assertEquals(dao.getByNumber(number), invoice);
-        Assert.assertNull(dao.getByNumber(Integer.MIN_VALUE));
+        try {
+            dao.getByNumber(Integer.MIN_VALUE);
+            Assert.fail();
+        } catch (InvoiceApiException e) {
+        }
     }
 
     @Test(groups = "fast")
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
index 55a2e61..f7ba009 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDao.java
@@ -16,9 +16,15 @@
 
 package com.ning.billing.invoice.dao;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -27,8 +33,11 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.mockito.Mockito;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableMap;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.DefaultPrice;
 import com.ning.billing.catalog.MockInternationalPrice;
 import com.ning.billing.catalog.MockPlan;
@@ -59,19 +68,13 @@ import com.ning.billing.invoice.model.RecurringInvoiceItem;
 import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
 import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.dao.AuditedTagDao;
 import com.ning.billing.util.tag.dao.TagDao;
 
-import com.google.common.collect.ImmutableMap;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertNull;
-import static org.testng.Assert.assertTrue;
-
 public class TestInvoiceDao extends InvoiceDaoTestBase {
 
     @Test(groups = "slow")
@@ -94,7 +97,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testInvoicePayment() {
+    public void testInvoicePayment() throws InvoiceApiException {
         final UUID accountId = UUID.randomUUID();
         final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
         final UUID invoiceId = invoice.getId();
@@ -103,7 +106,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final LocalDate startDate = new LocalDate(2010, 1, 1);
         final LocalDate endDate = new LocalDate(2010, 4, 1);
         final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "test plan", "test phase", startDate, endDate,
-                                                                 new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
+                new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
 
         invoice.addInvoiceItem(invoiceItem);
         invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, context);
@@ -128,9 +131,15 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
     }
 
     @Test(groups = "slow")
-    public void testRetrievalForNonExistentInvoiceId() {
-        final Invoice invoice = invoiceDao.getById(UUID.randomUUID());
-        assertNull(invoice);
+    public void testRetrievalForNonExistentInvoiceId()  throws InvoiceApiException {
+        try {
+            invoiceDao.getById(UUID.randomUUID());
+            Assert.fail();
+        } catch (InvoiceApiException e) {
+            if (e.getCode() != ErrorCode.INVOICE_NOT_FOUND.getCode()) {
+                Assert.fail();
+            }
+        }
     }
 
     @Test(groups = "slow")
@@ -159,19 +168,19 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         LocalDate endDate = startDate.plusMonths(1);
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate, endDate,
-                                                                    rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate, endDate,
-                                                                    rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
 
         final RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate, endDate,
-                                                                    rate3, rate3, Currency.USD);
+                rate3, rate3, Currency.USD);
         invoiceItemSqlDao.create(item3, context);
 
         final RecurringInvoiceItem item4 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate, endDate,
-                                                                    rate4, rate4, Currency.USD);
+                rate4, rate4, Currency.USD);
         invoiceItemSqlDao.create(item4, context);
 
         // Create invoice 2 (subscriptions 1-3)
@@ -184,15 +193,15 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         endDate = startDate.plusMonths(1);
 
         final RecurringInvoiceItem item5 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate, endDate,
-                                                                    rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item5, context);
 
         final RecurringInvoiceItem item6 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate, endDate,
-                                                                    rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item6, context);
 
         final RecurringInvoiceItem item7 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
-                                                                    rate3, rate3, Currency.USD);
+                rate3, rate3, Currency.USD);
         invoiceItemSqlDao.create(item7, context);
 
         // Check that each subscription returns the correct number of invoices
@@ -235,19 +244,19 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         LocalDate endDate = startDate.plusMonths(1);
 
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate,
-                                                                      rate1, Currency.USD);
+                rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final FixedPriceInvoiceItem item2 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate,
-                                                                      rate2, Currency.USD);
+                rate2, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
 
         final FixedPriceInvoiceItem item3 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate,
-                                                                      rate3, Currency.USD);
+                rate3, Currency.USD);
         invoiceItemSqlDao.create(item3, context);
 
         final FixedPriceInvoiceItem item4 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate,
-                                                                      rate4, Currency.USD);
+                rate4, Currency.USD);
         invoiceItemSqlDao.create(item4, context);
 
         // create invoice 2 (subscriptions 1-3)
@@ -260,15 +269,15 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         endDate = startDate.plusMonths(1);
 
         final FixedPriceInvoiceItem item5 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate,
-                                                                      rate1, Currency.USD);
+                rate1, Currency.USD);
         invoiceItemSqlDao.create(item5, context);
 
         final FixedPriceInvoiceItem item6 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate,
-                                                                      rate2, Currency.USD);
+                rate2, Currency.USD);
         invoiceItemSqlDao.create(item6, context);
 
         final FixedPriceInvoiceItem item7 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate,
-                                                                      rate3, Currency.USD);
+                rate3, Currency.USD);
         invoiceItemSqlDao.create(item7, context);
 
         // check that each subscription returns the correct number of invoices
@@ -311,35 +320,35 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         LocalDate endDate = startDate.plusMonths(1);
 
         final RecurringInvoiceItem recurringItem1 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate, endDate,
-                                                                             rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(recurringItem1, context);
 
         final RecurringInvoiceItem recurringItem2 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate, endDate,
-                                                                             rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(recurringItem2, context);
 
         final RecurringInvoiceItem recurringItem3 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate, endDate,
-                                                                             rate3, rate3, Currency.USD);
+                rate3, rate3, Currency.USD);
         invoiceItemSqlDao.create(recurringItem3, context);
 
         final RecurringInvoiceItem recurringItem4 = new RecurringInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate, endDate,
-                                                                             rate4, rate4, Currency.USD);
+                rate4, rate4, Currency.USD);
         invoiceItemSqlDao.create(recurringItem4, context);
 
         final FixedPriceInvoiceItem fixedItem1 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId1, "test plan", "test A", startDate,
-                                                                           rate1, Currency.USD);
+                rate1, Currency.USD);
         invoiceItemSqlDao.create(fixedItem1, context);
 
         final FixedPriceInvoiceItem fixedItem2 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId2, "test plan", "test B", startDate,
-                                                                           rate2, Currency.USD);
+                rate2, Currency.USD);
         invoiceItemSqlDao.create(fixedItem2, context);
 
         final FixedPriceInvoiceItem fixedItem3 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId3, "test plan", "test C", startDate,
-                                                                           rate3, Currency.USD);
+                rate3, Currency.USD);
         invoiceItemSqlDao.create(fixedItem3, context);
 
         final FixedPriceInvoiceItem fixedItem4 = new FixedPriceInvoiceItem(invoiceId1, accountId, bundleId, subscriptionId4, "test plan", "test D", startDate,
-                                                                           rate4, Currency.USD);
+                rate4, Currency.USD);
         invoiceItemSqlDao.create(fixedItem4, context);
 
         // create invoice 2 (subscriptions 1-3)
@@ -352,26 +361,26 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         endDate = startDate.plusMonths(1);
 
         final RecurringInvoiceItem recurringItem5 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate, endDate,
-                                                                             rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(recurringItem5, context);
 
         final RecurringInvoiceItem recurringItem6 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate, endDate,
-                                                                             rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(recurringItem6, context);
 
         final RecurringInvoiceItem recurringItem7 = new RecurringInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate, endDate,
-                                                                             rate3, rate3, Currency.USD);
+                rate3, rate3, Currency.USD);
         invoiceItemSqlDao.create(recurringItem7, context);
         final FixedPriceInvoiceItem fixedItem5 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId1, "test plan", "test phase A", startDate,
-                                                                           rate1, Currency.USD);
+                rate1, Currency.USD);
         invoiceItemSqlDao.create(fixedItem5, context);
 
         final FixedPriceInvoiceItem fixedItem6 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId2, "test plan", "test phase B", startDate,
-                                                                           rate2, Currency.USD);
+                rate2, Currency.USD);
         invoiceItemSqlDao.create(fixedItem6, context);
 
         final FixedPriceInvoiceItem fixedItem7 = new FixedPriceInvoiceItem(invoiceId2, accountId, bundleId, subscriptionId3, "test plan", "test phase C", startDate,
-                                                                           rate3, Currency.USD);
+                rate3, Currency.USD);
         invoiceItemSqlDao.create(fixedItem7, context);
 
         // check that each subscription returns the correct number of invoices
@@ -431,11 +440,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final BigDecimal rate2 = new BigDecimal("42.0");
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
-                                                                    endDate, rate1, rate1, Currency.USD);
+                endDate, rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                    endDate, rate2, rate2, Currency.USD);
+                endDate, rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
 
         final BigDecimal payment1 = new BigDecimal("48.0");
@@ -460,7 +469,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final BigDecimal rate1 = new BigDecimal("17.0");
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
-                                                                    endDate, rate1, rate1, Currency.USD);
+                endDate, rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), rate1.negate(), Currency.USD);
@@ -485,11 +494,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final BigDecimal rate2 = new BigDecimal("42.0");
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
-                                                                    rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
-                                                                    rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId);
@@ -538,7 +547,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                    endDate, rate1, rate1, Currency.USD);
+                endDate, rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(new BigDecimal("20.00")), 0);
@@ -604,7 +613,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
-                                                                      amount1, Currency.USD);
+                amount1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
@@ -612,7 +621,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                    endDate, rate1, rate1, Currency.USD);
+                endDate, rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
         balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(new BigDecimal("25.00")), 0);
@@ -628,7 +637,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Repair previous item with rate 2
         final RepairAdjInvoiceItem item2Repair = new RepairAdjInvoiceItem(invoice1.getId(), accountId, startDate, endDate, rate1.negate(), Currency.USD, item2.getId());
         final RecurringInvoiceItem item2Replace = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                           endDate, rate2, rate2, Currency.USD);
+                endDate, rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2Repair, context);
         invoiceItemSqlDao.create(item2Replace, context);
         balance = invoiceDao.getAccountBalance(accountId);
@@ -655,6 +664,33 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
     }
 
     @Test(groups = "slow")
+    public void testExternalChargeWithCBA() throws InvoiceApiException {
+
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
+        final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, invoice1.getTargetDate().getDayOfMonth(), true, context);
+
+        // CREATE INVOICE WITH A (just) CBA. Should not happen, but that does not matter for that test
+        final CreditBalanceAdjInvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), new BigDecimal("20.0"), Currency.USD);
+        invoiceItemSqlDao.create(cbaItem, context);
+
+        final InvoiceItem charge =  invoiceDao.insertExternalCharge(accountId, null, bundleId, "bla", new BigDecimal("15.0"), clock.getUTCNow().toLocalDate(), Currency.USD, context);
+
+        final Invoice newInvoice = invoiceDao.getById(charge.getInvoiceId());
+        List<InvoiceItem> items = newInvoice.getInvoiceItems();
+        assertEquals(items.size(), 2);
+        for (InvoiceItem cur : items) {
+            if (!cur.getId().equals(charge.getId())) {
+                assertEquals(cur.getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+                assertTrue(cur.getAmount().compareTo(new BigDecimal("-15.00")) == 0);
+                break;
+            }
+        }
+    }
+
+    @Test(groups = "slow")
     public void testAccountBalanceWithAllSortsOfThings() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -671,7 +707,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
-                                                                      amount1, Currency.USD);
+                amount1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         BigDecimal balance = invoiceDao.getAccountBalance(accountId);
@@ -679,7 +715,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Recurring item
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                    endDate, rate1, rate1, Currency.USD);
+                endDate, rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
         balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(new BigDecimal("25.00")), 0);
@@ -694,7 +730,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         // Repair previous item with rate 2
         final RepairAdjInvoiceItem item2Repair = new RepairAdjInvoiceItem(invoice1.getId(), accountId, startDate, endDate, rate1.negate(), Currency.USD, item2.getId());
         final RecurringInvoiceItem item2Replace = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
-                                                                           endDate, rate2, rate2, Currency.USD);
+                endDate, rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2Repair, context);
         invoiceItemSqlDao.create(item2Replace, context);
         balance = invoiceDao.getAccountBalance(accountId);
@@ -725,7 +761,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, context);
 
         final RecurringInvoiceItem nextItem = new RecurringInvoiceItem(invoice2.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test bla", startDate.plusMonths(1),
-                                                                       endDate.plusMonths(1), rate2, rate2, Currency.USD);
+                endDate.plusMonths(1), rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(nextItem, context);
         balance = invoiceDao.getAccountBalance(accountId);
         assertEquals(balance.compareTo(new BigDecimal("10.00")), 0);
@@ -818,7 +854,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // Fixed Item
         final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
-                                                                      amount1, Currency.USD);
+                amount1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         // Create the credit item
@@ -862,11 +898,11 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final BigDecimal rate2 = new BigDecimal("42.0");
 
         final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
-                                                                    rate1, rate1, Currency.USD);
+                rate1, rate1, Currency.USD);
         invoiceItemSqlDao.create(item1, context);
 
         final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
-                                                                    rate2, rate2, Currency.USD);
+                rate2, rate2, Currency.USD);
         invoiceItemSqlDao.create(item2, context);
 
         LocalDate upToDate;
@@ -890,7 +926,7 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final BigDecimal rate3 = new BigDecimal("21.0");
 
         final RecurringInvoiceItem item3 = new RecurringInvoiceItem(invoice2.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase C", startDate2, endDate2,
-                                                                    rate3, rate3, Currency.USD);
+                rate3, rate3, Currency.USD);
         invoiceItemSqlDao.create(item3, context);
 
         upToDate = new LocalDate(2011, 1, 1);
@@ -924,8 +960,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final DateTime effectiveDate1 = new DateTime(2011, 2, 1, 0, 0, 0);
         final BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan1, phase1, null,
-                                                           recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CREATE);
+                recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CREATE);
 
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
@@ -942,8 +978,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final DateTime effectiveDate2 = new DateTime(2011, 2, 15, 0, 0, 0);
         final BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan2, phase2, null,
-                                                           recurringPrice2.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
-                                                           "testEvent2", 2L, SubscriptionTransitionType.CREATE);
+                recurringPrice2.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+                "testEvent2", 2L, SubscriptionTransitionType.CREATE);
         events.add(event2);
 
         // second invoice should be for one half (14/28 days) the difference between the rate plans
@@ -974,8 +1010,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final DateTime effectiveDate = buildDate(2011, 1, 1).toDateTimeAtStartOfDay();
 
         final BillingEvent event = createMockBillingEvent(null, subscription, effectiveDate, plan, phase, null,
-                                                          recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 15, BillingModeType.IN_ADVANCE,
-                                                          "testEvent", 1L, SubscriptionTransitionType.CREATE);
+                recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 15, BillingModeType.IN_ADVANCE,
+                "testEvent", 1L, SubscriptionTransitionType.CREATE);
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event);
 
@@ -1013,8 +1049,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final DateTime effectiveDate1 = buildDate(2011, 1, 1).toDateTimeAtStartOfDay();
 
         final BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1, fixedPrice.getPrice(currency),
-                                                           null, currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CREATE);
+                null, currency, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CREATE);
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
@@ -1031,8 +1067,8 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         final DateTime effectiveDate2 = effectiveDate1.plusDays(30);
         final BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
-                                                           recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent2", 2L, SubscriptionTransitionType.PHASE);
+                recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent2", 2L, SubscriptionTransitionType.PHASE);
         events.add(event2);
 
         final Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, new LocalDate(effectiveDate2), DateTimeZone.UTC, Currency.USD);
@@ -1078,16 +1114,16 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final DateTime effectiveDate1 = buildDate(2011, 1, 1).toDateTimeAtStartOfDay();
 
         final BillingEvent event1 = createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1,
-                                                           fixedPrice.getPrice(currency), null, currency,
-                                                           BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CREATE);
+                fixedPrice.getPrice(currency), null, currency,
+                BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CREATE);
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         final DateTime effectiveDate2 = effectiveDate1.plusDays(30);
         final BillingEvent event2 = createMockBillingEvent(null, subscription, effectiveDate2, plan, phase2, null,
-                                                           recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
+                recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
         events.add(event2);
 
         final Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, new LocalDate(effectiveDate2), DateTimeZone.UTC, Currency.USD);
@@ -1103,6 +1139,81 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertEquals(savedInvoice.getBalance().compareTo(cheapAmount), 0);
     }
 
+
+    @Test(groups = "slow")
+    public void testRefundedInvoiceWithInvoiceItemAdjustmentWithRepair() throws InvoiceApiException  {
+
+        final UUID accountId = UUID.randomUUID();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final LocalDate startDate = new LocalDate(2010, 1, 1);
+
+        ((ClockMock) clock).setDay(startDate);
+
+        final LocalDate recuringStartDate = clock.getUTCNow().plusDays(30).toLocalDate();
+        final LocalDate recuringEndDate = clock.getUTCNow().plusDays(30).toLocalDate();
+        final LocalDate targetDate = recuringStartDate.plusDays(1);
+
+
+        // FIRST CREATE INITIAL INVOICE WITH ONE RECURRING ITEM
+        final Invoice invoice = new DefaultInvoice(accountId, targetDate, targetDate, Currency.USD);
+        final UUID invoiceId = invoice.getId();
+
+        final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, "test-plan", "test-phase-rec",
+                recuringStartDate, recuringEndDate, new BigDecimal("239.00"), new BigDecimal("239.00"), Currency.USD);
+
+
+        invoice.addInvoiceItem(invoiceItem);
+        invoiceDao.create(invoice, invoice.getTargetDate().getDayOfMonth(), true, context);
+
+
+        ((ClockMock) clock).addDays(1);
+
+        // SECOND CREATE THE PAYMENT
+        final BigDecimal paymentAmount = new BigDecimal("239.00");
+        final UUID paymentId = UUID.randomUUID();
+        invoiceDao.notifyOfPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentId, invoiceId, clock.getUTCNow(), paymentAmount, Currency.USD), context);
+
+        // AND THEN THIRD THE REFUND
+        Map<UUID, BigDecimal> invoiceItemMap = new HashMap<UUID, BigDecimal>();
+        invoiceItemMap.put(invoiceItem.getId(), new BigDecimal("239.00"));
+        invoiceDao.createRefund(paymentId, paymentAmount, true, invoiceItemMap, UUID.randomUUID(), context);
+
+        final Invoice savedInvoice = invoiceDao.getById(invoiceId);
+        assertNotNull(savedInvoice);
+        assertEquals(savedInvoice.getInvoiceItems().size(), 2);
+
+        final List<Invoice> invoices = new ArrayList<Invoice>();
+        invoices.add(savedInvoice);
+
+
+        // NOW COMPUTE A DIFFERENT ITEM TO TRIGGER REPAIR
+        final BillingEventSet events = new MockBillingEventSet();
+        final Subscription subscription = getZombieSubscription();
+
+        final Plan plan = Mockito.mock(Plan.class);
+        Mockito.when(plan.getName()).thenReturn("plan");
+
+        final PlanPhase phase1 = Mockito.mock(PlanPhase.class);
+        Mockito.when(phase1.getName()).thenReturn("plan-phase1");
+
+        final PlanPhase phase2 = Mockito.mock(PlanPhase.class);
+        Mockito.when(phase2.getName()).thenReturn("plan-phase2");
+
+        final BillingEvent event1 = createMockBillingEvent(null, subscription, recuringStartDate.toDateTimeAtStartOfDay(), plan, phase1, null,
+                TEN, Currency.USD,
+                BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,
+                "new-event", 1L, SubscriptionTransitionType.CREATE);
+        events.add(event1);
+        Invoice newInvoice = generator.generateInvoice(UUID.randomUUID(), events, invoices, targetDate, DateTimeZone.UTC, Currency.USD);
+        invoiceDao.create(newInvoice, newInvoice.getTargetDate().getDayOfMonth(), true, context);
+
+        // VERIFY THAT WE STILL HAVE ONLY 2 ITEMS, MENAING THERE WERE NO REPAIR AND NO CBA GENERATED
+        final Invoice firstInvoice = invoiceDao.getById(invoiceId);
+        assertNotNull(firstInvoice);
+        assertEquals(firstInvoice.getInvoiceItems().size(), 2);
+    }
+
     @Test(groups = "slow")
     public void testInvoiceNumber() throws InvoiceApiException {
         final Currency currency = Currency.USD;
@@ -1124,9 +1235,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         final List<Invoice> invoices = new ArrayList<Invoice>();
 
         final BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
-                                                           TEN, currency,
-                                                           BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+                TEN, currency,
+                BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
         events.add(event1);
 
         Invoice invoice1 = generator.generateInvoice(UUID.randomUUID(), events, invoices, new LocalDate(targetDate1), DateTimeZone.UTC, Currency.USD);
@@ -1136,9 +1247,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
         assertNotNull(invoice1.getInvoiceNumber());
 
         final BillingEvent event2 = createMockBillingEvent(null, subscription, targetDate1, plan, phase2, null,
-                                                           TWENTY, currency,
-                                                           BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
+                TWENTY, currency,
+                BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent2", 2L, SubscriptionTransitionType.CHANGE);
         events.add(event2);
         Invoice invoice2 = generator.generateInvoice(UUID.randomUUID(), events, invoices, new LocalDate(targetDate2), DateTimeZone.UTC, Currency.USD);
         invoiceDao.create(invoice2, invoice2.getTargetDate().getDayOfMonth(), true, context);
@@ -1161,9 +1272,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // create pseudo-random invoice
         final BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
-                                                           TEN, currency,
-                                                           BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+                TEN, currency,
+                BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
@@ -1192,9 +1303,9 @@ public class TestInvoiceDao extends InvoiceDaoTestBase {
 
         // create pseudo-random invoice
         final BillingEvent event1 = createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
-                                                           TEN, currency,
-                                                           BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
-                                                           "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
+                TEN, currency,
+                BillingPeriod.MONTHLY, 31, BillingModeType.IN_ADVANCE,
+                "testEvent1", 1L, SubscriptionTransitionType.CHANGE);
         final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
index e76e890..1fac6f7 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestInvoiceDaoForItemAdjustment.java
@@ -101,7 +101,7 @@ public class TestInvoiceDaoForItemAdjustment extends InvoiceDaoTestBase {
         Assert.assertEquals(adjustedInvoiceItem.getAmount().compareTo(BigDecimal.TEN.negate()), 0);
     }
 
-    private InvoiceItem createAndCheckAdjustment(final Invoice invoice, final InvoiceItem invoiceItem, final BigDecimal amount) {
+    private InvoiceItem createAndCheckAdjustment(final Invoice invoice, final InvoiceItem invoiceItem, final BigDecimal amount) throws InvoiceApiException {
         final LocalDate effectiveDate = new LocalDate(2010, 1, 1);
         final InvoiceItem adjustedInvoiceItem = invoiceDao.insertInvoiceItemAdjustment(invoice.getAccountId(), invoice.getId(), invoiceItem.getId(),
                                                                                        effectiveDate, amount, null, context);

jaxrs/pom.xml 2(+1 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index acdb6d7..67a0511 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 97eb58c..dbf22b2 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -144,7 +144,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems) throws InvoiceApiException {
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId));
         if (invoice == null) {
-            throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND);
+            throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
         } else {
             final InvoiceJsonSimple json = withItems ? new InvoiceJsonWithItems(invoice) : new InvoiceJsonSimple(invoice);
             return Response.status(Status.OK).entity(json).build();

junction/pom.xml 2(+1 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index 1db0204..1b1d475 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>

overdue/pom.xml 2(+1 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 8c58169..c0336d2 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index 52bc21b..96765f3 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index 55b852f..241027b 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -15,24 +15,24 @@
  */
 package com.ning.billing.payment.core;
 
-import javax.annotation.Nullable;
-import javax.inject.Inject;
+import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.inject.name.Named;
@@ -42,6 +42,7 @@ import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.config.PaymentConfig;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.payment.api.DefaultPayment;
 import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
@@ -53,7 +54,6 @@ import com.ning.billing.payment.api.PaymentStatus;
 import com.ning.billing.payment.dao.PaymentAttemptModelDao;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
-import com.ning.billing.payment.dao.PaymentSqlDao;
 import com.ning.billing.payment.dao.RefundModelDao;
 import com.ning.billing.payment.dispatcher.PluginDispatcher;
 import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
@@ -63,7 +63,6 @@ import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
 import com.ning.billing.payment.retry.AutoPayRetryService.AutoPayRetryServiceScheduler;
 import com.ning.billing.payment.retry.FailedPaymentRetryService.FailedPaymentRetryServiceScheduler;
 import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
-import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.bus.Bus;
 import com.ning.billing.util.bus.BusEvent;
@@ -72,12 +71,7 @@ import com.ning.billing.util.callcontext.CallContextFactory;
 import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.clock.Clock;
-import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.globallocker.GlobalLocker;
-import com.ning.billing.util.tag.ControlTagType;
-import com.ning.billing.util.tag.Tag;
-
-import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
 
 public class PaymentProcessor extends ProcessorBase {
 
@@ -237,21 +231,25 @@ public class PaymentProcessor extends ProcessorBase {
                 public Payment doOperation() throws PaymentApiException {
 
 
-                    final Invoice invoice = invoicePaymentApi.getInvoice(invoiceId);
+                    try {
+                        final Invoice invoice = invoicePaymentApi.getInvoice(invoiceId);
 
-                    if (invoice.isMigrationInvoice()) {
-                        log.error("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
-                        return null;
-                    }
+                        if (invoice.isMigrationInvoice()) {
+                            log.error("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
+                            return null;
+                        }
 
-                    final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId());
-                    setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(account.getId(), paymentMethodId, isAccountAutoPayOff, context, isInstantPayment);
+                        final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId());
+                        setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(account.getId(), paymentMethodId, isAccountAutoPayOff, context, isInstantPayment);
 
-                    final BigDecimal requestedAmount = getAndValidatePaymentAmount(invoice, inputAmount, isInstantPayment);
-                    if (!isInstantPayment && isAccountAutoPayOff) {
-                        return processNewPaymentForAutoPayOffWithAccountLocked(paymentMethodId, account, invoice, requestedAmount, context);
-                    } else {
-                        return processNewPaymentWithAccountLocked(paymentMethodId, plugin, account, invoice, requestedAmount, isInstantPayment, context);
+                        final BigDecimal requestedAmount = getAndValidatePaymentAmount(invoice, inputAmount, isInstantPayment);
+                        if (!isInstantPayment && isAccountAutoPayOff) {
+                            return processNewPaymentForAutoPayOffWithAccountLocked(paymentMethodId, account, invoice, requestedAmount, context);
+                        } else {
+                            return processNewPaymentWithAccountLocked(paymentMethodId, plugin, account, invoice, requestedAmount, isInstantPayment, context);
+                        }
+                    } catch (InvoiceApiException e) {
+                        throw new PaymentApiException(e);
                     }
                 }
             }));
@@ -269,10 +267,6 @@ public class PaymentProcessor extends ProcessorBase {
         }
     }
 
-
-
-
-
     private void setUnsaneAccount_AUTO_PAY_OFFWithAccountLock(final UUID accountId, final UUID paymentMethodId, final boolean isAccountAutoPayOff, final CallContext context, final boolean isInstantPayment)
     throws PaymentApiException  {
 
@@ -347,32 +341,34 @@ public class PaymentProcessor extends ProcessorBase {
 
                 @Override
                 public Void doOperation() throws PaymentApiException {
-
-                    // Fetch again with account lock this time
-                    final PaymentModelDao payment = paymentDao.getPayment(paymentId);
-                    boolean foundExpectedState = false;
-                    for (final PaymentStatus cur : expectedPaymentStates) {
-                        if (payment.getPaymentStatus() == cur) {
-                            foundExpectedState = true;
-                            break;
+                    try {
+                        // Fetch again with account lock this time
+                        final PaymentModelDao payment = paymentDao.getPayment(paymentId);
+                        boolean foundExpectedState = false;
+                        for (final PaymentStatus cur : expectedPaymentStates) {
+                            if (payment.getPaymentStatus() == cur) {
+                                foundExpectedState = true;
+                                break;
+                            }
+                        }
+                        if (!foundExpectedState) {
+                            log.info("Aborted retry for payment {} because it is {} state", paymentId, payment.getPaymentStatus());
+                            return null;
                         }
-                    }
-                    if (!foundExpectedState) {
-                        log.info("Aborted retry for payment {} because it is {} state", paymentId, payment.getPaymentStatus());
-                        return null;
-                    }
 
-                    final Invoice invoice = invoicePaymentApi.getInvoice(payment.getInvoiceId());
-                    if (invoice.isMigrationInvoice()) {
-                        return null;
-                    }
-                    if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
-                        log.info("Aborted retry for payment {} because invoice has been paid", paymentId);
+                        final Invoice invoice = invoicePaymentApi.getInvoice(payment.getInvoiceId());
+                        if (invoice.isMigrationInvoice()) {
+                            return null;
+                        }
+                        if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
+                            log.info("Aborted retry for payment {} because invoice has been paid", paymentId);
+                            return null;
+                        }
+                        processRetryPaymentWithAccountLocked(plugin, account, invoice, payment, invoice.getBalance(), context);
                         return null;
+                    } catch (InvoiceApiException e) {
+                        throw new PaymentApiException(e);
                     }
-                    processRetryPaymentWithAccountLocked(plugin, account, invoice, payment, invoice.getBalance(), context);
-                    return null;
-
                 }
             }));
         } catch (AccountApiException e) {
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index a254ff2..3184081 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.payment.core;
 
+import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
+
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,6 +33,12 @@ import javax.inject.Inject;
 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.ImmutableMap;
+import com.google.inject.name.Named;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
@@ -56,15 +64,6 @@ import com.ning.billing.util.callcontext.CallOrigin;
 import com.ning.billing.util.callcontext.UserType;
 import com.ning.billing.util.globallocker.GlobalLocker;
 
-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.ImmutableMap;
-import com.google.inject.name.Named;
-
-import static com.ning.billing.payment.glue.PaymentModule.PLUGIN_EXECUTOR_NAMED;
-
 public class RefundProcessor extends ProcessorBase {
 
     private static final Logger log = LoggerFactory.getLogger(RefundProcessor.class);
@@ -188,21 +187,26 @@ public class RefundProcessor extends ProcessorBase {
      * @param invoiceItemIdsWithAmounts invoice item ids and associated amounts to adjust
      * @return the refund amount
      */
-    private BigDecimal computeRefundAmount(final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) {
-        final List<InvoiceItem> items = invoicePaymentApi.getInvoiceForPaymentId(paymentId).getInvoiceItems();
+    private BigDecimal computeRefundAmount(final UUID paymentId, @Nullable final BigDecimal specifiedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts)
+            throws PaymentApiException {
+        try {
+            final List<InvoiceItem> items = invoicePaymentApi.getInvoiceForPaymentId(paymentId).getInvoiceItems();
 
-        BigDecimal amountFromItems = BigDecimal.ZERO;
-        for (final UUID itemId : invoiceItemIdsWithAmounts.keySet()) {
-            amountFromItems = amountFromItems.add(Objects.firstNonNull(invoiceItemIdsWithAmounts.get(itemId),
-                                                                       getAmountFromItem(items, itemId)));
-        }
+            BigDecimal amountFromItems = BigDecimal.ZERO;
+            for (final UUID itemId : invoiceItemIdsWithAmounts.keySet()) {
+                amountFromItems = amountFromItems.add(Objects.firstNonNull(invoiceItemIdsWithAmounts.get(itemId),
+                        getAmountFromItem(items, itemId)));
+            }
 
-        // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
-        if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && specifiedRefundAmount != null && specifiedRefundAmount.compareTo(amountFromItems) != 0) {
-            throw new IllegalArgumentException("You can't specify a refund amount that doesn't match the invoice items amounts");
-        }
+            // Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
+            if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && specifiedRefundAmount != null && specifiedRefundAmount.compareTo(amountFromItems) != 0) {
+                throw new IllegalArgumentException("You can't specify a refund amount that doesn't match the invoice items amounts");
+            }
 
-        return Objects.firstNonNull(specifiedRefundAmount, amountFromItems);
+            return Objects.firstNonNull(specifiedRefundAmount, amountFromItems);
+        } catch (InvoiceApiException e) {
+            throw new PaymentApiException(e);
+        }
     }
 
     private BigDecimal getAmountFromItem(final List<InvoiceItem> items, final UUID itemId) {
diff --git a/payment/src/test/java/com/ning/billing/payment/TestHelper.java b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
index ed4c905..568e45d 100644
--- a/payment/src/test/java/com/ning/billing/payment/TestHelper.java
+++ b/payment/src/test/java/com/ning/billing/payment/TestHelper.java
@@ -18,7 +18,6 @@ package com.ning.billing.payment;
 
 import java.util.UUID;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.mockito.Mockito;
 
@@ -27,6 +26,7 @@ import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceCreationEvent;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
@@ -65,7 +65,7 @@ public class TestHelper {
     public Invoice createTestInvoice(final Account account,
                                      final LocalDate targetDate,
                                      final Currency currency,
-                                     final InvoiceItem... items) throws EventBusException {
+                                     final InvoiceItem... items) throws EventBusException, InvoiceApiException {
         final Invoice invoice = new MockInvoice(account.getId(), clock.getUTCToday(), targetDate, currency);
 
         for (final InvoiceItem item : items) {

pom.xml 14(+13 -1)

diff --git a/pom.xml b/pom.xml
index e4a5911..f10764c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.1.30-SNAPSHOT</version>
+    <version>0.1.31-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>
@@ -625,6 +625,18 @@
                             </execution>
                         </executions>
                     </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <version>2.11</version>
+                        <configuration>
+                            <groups>fast</groups>
+                            <systemPropertyVariables>
+                                <file.encoding>UTF-8</file.encoding>
+                                <user.timezone>GMT</user.timezone>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
                 </plugins>
             </build>
         </profile>

server/pom.xml 2(+1 -1)

diff --git a/server/pom.xml b/server/pom.xml
index 06bce5a..fdc002c 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-server</artifactId>

usage/pom.xml 2(+1 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index c69dae9..db9f947 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 79fba48..bf97252 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.1.30-SNAPSHOT</version>
+        <version>0.1.31-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>