killbill-aplcache

Details

diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 39dc17f..bc20699 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -197,7 +197,7 @@ public enum ErrorCode {
     CHARGE_BACK_DOES_NOT_EXIST(4004, "Could not find chargeback for id %s."),
     INVOICE_PAYMENT_BY_ATTEMPT_NOT_FOUND(4905, "No invoice payment could be found for paymentAttempt id %s."),
     REFUND_AMOUNT_TOO_HIGH(4906, "Tried to refund %s of a %s payment."),
-    REFUND_AMOUNT_IS_NEGATIVE(4907, "Refund for negative amounts are not permitted"),
+    REFUND_AMOUNT_IS_POSITIVE(4907, "Refund for positve amounts are not permitted"),
 
     /*
      *
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 9bf4cc5..a71783a 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
@@ -42,8 +42,6 @@ public interface InvoicePaymentApi {
 
     public void notifyOfPaymentAttempt(UUID invoiceId, BigDecimal amountOutstanding, Currency currency, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
 
-    public void notifyOfPaymentAttempt(UUID invoiceId, UUID paymentAttemptId, DateTime paymentAttemptDate, CallContext context);
-
     public InvoicePayment createRefund(UUID paymentAttemptId, BigDecimal amount, boolean isInvoiceAdjusted, CallContext context) throws InvoiceApiException;
 
     public InvoicePayment createChargeback(UUID invoicePaymentId, BigDecimal amount, CallContext context) throws InvoiceApiException;
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 125e195..a73fab7 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
@@ -75,12 +75,6 @@ public class DefaultInvoicePaymentApi implements InvoicePaymentApi {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(final UUID invoiceId, final UUID paymentAttemptId, final DateTime paymentAttemptDate, final CallContext context) {
-        final InvoicePayment invoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentAttemptId, invoiceId, paymentAttemptDate);
-        dao.notifyOfPaymentAttempt(invoicePayment, context);
-    }
-
-    @Override
     public InvoicePayment createChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
         return dao.postChargeback(invoicePaymentId, amount, context);
     }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 4b10980..2cfec3f 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -270,7 +270,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
                 Collection<Invoice> unpaidInvoices = Collections2.filter(invoices, new Predicate<Invoice>() {
                     @Override
                     public boolean apply(Invoice in) {
-                        return (in.getBalance().compareTo(BigDecimal.ZERO) >= 1);
+                        return (in.getBalance().compareTo(BigDecimal.ZERO) >= 1) && !in.getTargetDate().isAfter(upToDate);
                     }
                 });
                 return new ArrayList<Invoice>(unpaidInvoices);
@@ -313,29 +313,46 @@ public class DefaultInvoiceDao implements InvoiceDao {
                 }
                 final BigDecimal maxRefundAmount = payment.getAmount() == null ? BigDecimal.ZERO : payment.getAmount();
                 final BigDecimal requestedAmount = amount == null ? maxRefundAmount : amount;
-                if (requestedAmount.compareTo(BigDecimal.ZERO) <= 0) {
-                    throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_IS_NEGATIVE);
+                if (requestedAmount.compareTo(BigDecimal.ZERO) >= 0) {
+                    throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_IS_POSITIVE);
                 }
-                if (requestedAmount.compareTo(maxRefundAmount) > 0) {
-                    throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedAmount, maxRefundAmount);
+
+                // No that we check signs, let's work with positive numbers, this makes things simpler
+                final BigDecimal requestedPositiveAmount = requestedAmount.negate();
+                if (requestedPositiveAmount.compareTo(maxRefundAmount) > 0) {
+                    throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_TOO_HIGH, requestedPositiveAmount, maxRefundAmount);
                 }
 
                 final InvoicePayment refund = new DefaultInvoicePayment(UUID.randomUUID(), InvoicePaymentType.REFUND, paymentAttemptId,
-                        payment.getInvoiceId(), context.getCreatedDate(), requestedAmount, payment.getCurrency(), payment.getId());
+                        payment.getInvoiceId(), context.getCreatedDate(), requestedPositiveAmount.negate(), payment.getCurrency(), payment.getId());
                 transactional.create(refund, context);
 
+                // Retrieve invoice after the Refund
+                InvoiceSqlDao transInvoiceDao = transactional.become(InvoiceSqlDao.class);
+                Invoice invoice = transInvoiceDao.getById(payment.getInvoiceId().toString());
+                if (invoice != null) {
+                    populateChildren(invoice, transInvoiceDao);
+                }
+
+                final BigDecimal invoiceBalanceAfterRefund = invoice.getBalance();
+                InvoiceItemSqlDao transInvoiceItemDao = transInvoiceDao.become(InvoiceItemSqlDao.class);
+
+                // If we have an existing CBA > 0, we need to adjust it
+                final BigDecimal cbaAmountAfterRefund = invoice.getCBAAmount();
+                BigDecimal cbaAdjAmount = BigDecimal.ZERO;
+                if (cbaAmountAfterRefund.compareTo(BigDecimal.ZERO) > 0) {
+                    cbaAdjAmount = (requestedPositiveAmount.compareTo(cbaAmountAfterRefund) > 0) ?  cbaAmountAfterRefund.negate() : requestedPositiveAmount.negate();
+                    final InvoiceItem cbaAdjItem = new CreditBalanceAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate(), cbaAdjAmount, invoice.getCurrency());
+                    transInvoiceItemDao.create(cbaAdjItem, context);
+                }
+                final BigDecimal requestedPositiveAmountAfterCbaAdj = requestedPositiveAmount.subtract(cbaAdjAmount);
+
                 if (isInvoiceAdjusted) {
-                    InvoiceSqlDao transInvoiceDao = transactional.become(InvoiceSqlDao.class);
-                    Invoice invoice = transInvoiceDao.getById(payment.getInvoiceId().toString());
-                    if (invoice != null) {
-                        populateChildren(invoice, transInvoiceDao);
-                    }
 
-                    BigDecimal maxBalanceToAdjust = (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) ? BigDecimal.ZERO : invoice.getBalance();
-                    BigDecimal requestedAmountToAdjust = requestedAmount.compareTo(maxBalanceToAdjust)  > 0 ? maxBalanceToAdjust : requestedAmount;
-                    if (requestedAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
-                        final InvoiceItem adjItem = new RefundAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate(), requestedAmountToAdjust, invoice.getCurrency());
-                        InvoiceItemSqlDao transInvoiceItemDao = transInvoiceDao.become(InvoiceItemSqlDao.class);
+                    BigDecimal maxBalanceToAdjust = (invoiceBalanceAfterRefund.compareTo(BigDecimal.ZERO) <= 0) ? BigDecimal.ZERO : invoiceBalanceAfterRefund;
+                    BigDecimal requestedPositiveAmountToAdjust = requestedPositiveAmountAfterCbaAdj.compareTo(maxBalanceToAdjust)  > 0 ? maxBalanceToAdjust : requestedPositiveAmountAfterCbaAdj;
+                    if (requestedPositiveAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
+                        final InvoiceItem adjItem = new RefundAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), context.getCreatedDate(), requestedPositiveAmountToAdjust.negate(), invoice.getCurrency());
                         transInvoiceItemDao.create(adjItem, context);
                     }
                 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
index 852660d..ed5b2c3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -62,7 +62,7 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePayment>, Tran
     List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
 
     @SqlQuery
-    public InvoicePayment getByPaymentAttemptId(@Bind("paymentAttempt") final String paymentAttemptId);
+    public InvoicePayment getByPaymentAttemptId(@Bind("paymentAttemptId") final String paymentAttemptId);
 
     @Override
     @SqlQuery
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
index 99e0bde..08371ed 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoicePayment.java
@@ -22,9 +22,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
-import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.util.entity.EntityBase;
 
@@ -37,10 +35,6 @@ public class DefaultInvoicePayment extends EntityBase implements InvoicePayment 
     private final Currency currency;
     private final UUID reversedInvoicePaymentId;
 
-    public DefaultInvoicePayment(final InvoicePaymentType type, final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate) {
-        this(UUID.randomUUID(), type, paymentAttemptId, invoiceId, paymentDate, null, null, null);
-    }
-
     public DefaultInvoicePayment(final InvoicePaymentType type, final UUID paymentAttemptId, final UUID invoiceId, final DateTime paymentDate,
                                  final BigDecimal amount, final Currency currency) {
         this(UUID.randomUUID(), type, paymentAttemptId, invoiceId, paymentDate, amount, currency, null);
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 90e66bc..416fb5e 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -20,7 +20,7 @@ get() ::= <<
 getInvoicesByAccount() ::= <<
   SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
-  WHERE account_id = :accountId AND migrated = 'FALSE'
+  WHERE account_id = :accountId AND migrated = '0'
   ORDER BY target_date ASC;
 >>
 
@@ -34,16 +34,15 @@ getAllInvoicesByAccount() ::= <<
 getInvoicesByAccountAfterDate() ::= <<
   SELECT record_id as invoice_number, <invoiceFields()>
   FROM invoices
-  WHERE account_id = :accountId AND target_date >= :fromDate AND migrated = 'FALSE'
+  WHERE account_id = :accountId AND target_date >= :fromDate AND migrated = '0'
   ORDER BY target_date ASC;
 >>
 
 getInvoicesBySubscription() ::= <<
   SELECT i.record_id as invoice_number, <invoiceFields("i.")>
   FROM invoices i
-  LEFT JOIN invoice_items rii ON i.id = rii.invoice_id
-  WHERE rii.subscription_id = :subscriptionId AND migrated = 'FALSE'
-  GROUP BY i.record_id, <invoiceFields("i.")>
+  JOIN invoice_items ii ON i.id = ii.invoice_id
+  WHERE ii.subscription_id = :subscriptionId AND i.migrated = '0';
 >>
 
 getById() ::= <<
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
index fa5d431..af2c854 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/MockInvoicePaymentApi.java
@@ -97,12 +97,6 @@ public class MockInvoicePaymentApi implements InvoicePaymentApi {
     }
 
     @Override
-    public void notifyOfPaymentAttempt(final UUID invoiceId, final UUID paymentAttemptId, final DateTime paymentAttemptDate, final CallContext context) {
-        final InvoicePayment invoicePayment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentAttemptId, invoiceId, paymentAttemptDate);
-        notifyOfPaymentAttempt(invoicePayment, context);
-    }
-
-    @Override
     public InvoicePayment createChargeback(final UUID invoicePaymentId, final BigDecimal amount, final CallContext context) throws InvoiceApiException {
         InvoicePayment existingPayment = null;
         for (final InvoicePayment payment : invoicePayments) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index 304b4e8..6a5631d 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -98,7 +98,7 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
         }
     }
 
-    @BeforeClass(alwaysRun = true)
+    @BeforeClass(groups={"slow"})
     protected void setup() throws IOException {
 
         loadSystemPropertiesFromClasspath("/resource.properties");
@@ -109,10 +109,15 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
         final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
         final String utilDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
+        clock = new ClockMock();
+
         mysqlTestingHelper.startMysql();
         mysqlTestingHelper.initDb(invoiceDdl);
         mysqlTestingHelper.initDb(utilDdl);
 
+        bus = new InMemoryBus();
+        bus.start();
+
         final NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
         final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
         final TagDao tagDao = new AuditedTagDao(dbi, tagEventBuilder, bus);
@@ -123,16 +128,17 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
         invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
         invoicePaymentDao = dbi.onDemand(InvoicePaymentSqlDao.class);
 
-        clock = new ClockMock();
+
+
         context = new TestCallContext("Invoice Dao Tests");
         generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
-        bus = new InMemoryBus();
-        bus.start();
+
+
 
         assertTrue(true);
     }
 
-    @BeforeMethod(alwaysRun = true)
+    @BeforeMethod(groups={"slow"})
     public void cleanupData() {
         dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
@@ -146,7 +152,7 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
         });
     }
 
-    @AfterClass(alwaysRun = true)
+    @AfterClass(groups={"slow"})
     protected void tearDown() {
         bus.stop();
         mysqlTestingHelper.stopMysql();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 098f653..51e9211 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -52,7 +52,6 @@ import com.ning.billing.invoice.model.DefaultInvoice;
 import com.ning.billing.invoice.model.DefaultInvoicePayment;
 import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
 import com.ning.billing.invoice.model.RecurringInvoiceItem;
-import com.ning.billing.invoice.model.RefundAdjInvoiceItem;
 import com.ning.billing.invoice.model.RepairAdjInvoiceItem;
 import com.ning.billing.junction.api.BillingEventSet;
 import com.ning.billing.mock.BrainDeadProxyFactory;
@@ -69,9 +68,9 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
-@Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
 public class InvoiceDaoTests extends InvoiceDaoTestBase {
-    @Test
+
+    @Test(groups = {"slow"})
     public void testCreationAndRetrievalByAccount() {
         final UUID accountId = UUID.randomUUID();
         final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), clock.getUTCNow(), Currency.USD);
@@ -90,7 +89,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertTrue(thisInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoicePayment() {
         final UUID accountId = UUID.randomUUID();
         final Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), clock.getUTCNow(), Currency.USD);
@@ -124,13 +123,13 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(retrievedInvoice.getPaidAmount().compareTo(new BigDecimal("11.00")), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testRetrievalForNonExistentInvoiceId() {
         final Invoice invoice = invoiceDao.getById(UUID.randomUUID());
         assertNull(invoice);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAddPayment() {
         final UUID accountId = UUID.randomUUID();
         final DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
@@ -149,7 +148,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoice.getNumberOfPayments(), 1);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAddPaymentAttempt() {
         final UUID accountId = UUID.randomUUID();
         final DateTime targetDate = new DateTime(2011, 10, 6, 0, 0, 0, 0);
@@ -158,13 +157,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         final DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
         invoiceDao.create(invoice, context);
-        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice.getId(), paymentAttemptDate), context);
-
+        invoiceDao.notifyOfPaymentAttempt(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice.getId(), paymentAttemptDate, invoice.getBalance(), Currency.USD), context);
         invoice = invoiceDao.getById(invoice.getId());
         assertEquals(invoice.getLastPaymentAttempt().compareTo(paymentAttemptDate), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testGetInvoicesBySubscriptionForRecurringItems() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -240,7 +238,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(items4.size(), 1);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testGetInvoicesBySubscriptionForFixedItems() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -316,7 +314,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(items4.size(), 1);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testGetInvoicesBySubscriptionForRecurringAndFixedItems() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -419,7 +417,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(items4.size(), 2);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testGetInvoicesForAccountAfterDate() {
         final UUID accountId = UUID.randomUUID();
         final DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
@@ -448,7 +446,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(invoices.size(), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAccountBalance() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -479,7 +477,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
     }
 
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAccountBalanceWithCredit() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -504,7 +502,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAccountBalanceWithNoPayments() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -530,7 +528,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(balance.compareTo(rate1.add(rate2)), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAccountBalanceWithNoInvoiceItems() {
         final UUID accountId = UUID.randomUUID();
         final DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
@@ -545,7 +543,153 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(balance.compareTo(BigDecimal.ZERO.subtract(payment1)), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithRefundNoAdj() throws InvoiceApiException {
+        testAccountBalanceWithRefundInternal(false);
+    }
+
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithRefundAndAdj() throws InvoiceApiException {
+        testAccountBalanceWithRefundInternal(true);
+        }
+
+    private void testAccountBalanceWithRefundInternal(boolean withAdjustment) throws InvoiceApiException {
+
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
+
+        final DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        final DateTime endDate = startDate.plusMonths(1);
+
+        final BigDecimal rate1 = new BigDecimal("20.0");
+        final BigDecimal refund1 = new BigDecimal("-7.00");
+        final BigDecimal rate2 = new BigDecimal("10.0");
+
+
+        // Recurring item
+        final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
+                endDate, rate1, rate1, Currency.USD);
+        invoiceItemSqlDao.create(item2, context);
+        BigDecimal balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("20.00")), 0);
+
+        // Pay the whole thing
+        final UUID paymentAttemptId = UUID.randomUUID();
+        final BigDecimal payment1 = rate1;
+        final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentAttemptId, invoice1.getId(), new DateTime(), payment1, Currency.USD);
+        invoicePaymentDao.create(payment, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
+
+        invoiceDao.createRefund(paymentAttemptId, refund1, withAdjustment, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        if (withAdjustment) {
+            assertEquals(balance.compareTo(BigDecimal.ZERO), 0);
+        } else {
+            assertEquals(balance.compareTo(new BigDecimal("7.00")), 0);
+        }
+    }
+
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithSmallRefundAndCBANoAdj() throws InvoiceApiException {
+        BigDecimal refundAmount =  new BigDecimal("-7.00");
+        BigDecimal expectedBalance =  new BigDecimal("-3.00");
+        testAccountBalanceWithRefundAndCBAInternal(false, refundAmount, expectedBalance);
+    }
+
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithSmallRefundAndCBAWithAdj() throws InvoiceApiException {
+        BigDecimal refundAmount =  new BigDecimal("-7.00");
+        BigDecimal expectedBalance =  new BigDecimal("-3.00");
+        testAccountBalanceWithRefundAndCBAInternal(true, refundAmount, expectedBalance);
+    }
+
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithLargeRefundAndCBANoAdj() throws InvoiceApiException {
+        BigDecimal refundAmount =  new BigDecimal("-20.00");
+        BigDecimal expectedBalance =  new BigDecimal("10.00");
+        testAccountBalanceWithRefundAndCBAInternal(false, refundAmount, expectedBalance);
+    }
+
+    @Test(groups = {"slow"})
+    public void testAccountBalanceWithLargeRefundAndCBAWithAdj() throws InvoiceApiException {
+        BigDecimal refundAmount =  new BigDecimal("-20.00");
+        BigDecimal expectedBalance =  BigDecimal.ZERO;
+        testAccountBalanceWithRefundAndCBAInternal(true, refundAmount, expectedBalance);
+    }
+
+
+    private void testAccountBalanceWithRefundAndCBAInternal(boolean withAdjustment, final BigDecimal refundAmount, final BigDecimal expectedFinalBalance) throws InvoiceApiException {
+
+        final UUID accountId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final DateTime targetDate1 = new DateTime(2011, 10, 6, 0, 0, 0, 0);
+        final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate1, Currency.USD);
+        invoiceDao.create(invoice1, context);
+
+        final DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
+        final DateTime endDate = startDate.plusMonths(1);
+
+        final BigDecimal amount1 = new BigDecimal("5.0");
+        final BigDecimal rate1 = new BigDecimal("20.0");
+        final BigDecimal rate2 = new BigDecimal("10.0");
+
+        // Fixed Item
+        final FixedPriceInvoiceItem item1 = new FixedPriceInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate,
+                endDate, amount1, Currency.USD);
+        invoiceItemSqlDao.create(item1, context);
+
+        BigDecimal balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("5.00")), 0);
+
+        // Recurring item
+        final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate,
+                endDate, rate1, rate1, Currency.USD);
+        invoiceItemSqlDao.create(item2, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("25.00")), 0);
+
+        // Pay the whole thing
+        final UUID paymentAttemptId = UUID.randomUUID();
+        final BigDecimal payment1 = amount1.add(rate1);
+        final InvoicePayment payment = new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, paymentAttemptId, invoice1.getId(), new DateTime(), payment1, Currency.USD);
+        invoicePaymentDao.create(payment, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
+
+        // 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);
+        invoiceItemSqlDao.create(item2Repair, context);
+        invoiceItemSqlDao.create(item2Replace, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
+
+        // CBA
+        final CreditBalanceAdjInvoiceItem cbaItem = new CreditBalanceAdjInvoiceItem(invoice1.getId(), accountId, new DateTime(), balance.negate(), Currency.USD);
+        invoiceItemSqlDao.create(cbaItem, context);
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(new BigDecimal("-10.00")), 0);
+        BigDecimal cba =  invoiceDao.getAccountCBA(accountId);
+        assertEquals(cba.compareTo(new BigDecimal("10.00")), 0);
+
+        // PARTIAL REFUND on the payment
+        invoiceDao.createRefund(paymentAttemptId, refundAmount, withAdjustment, context);
+
+        balance = invoiceDao.getAccountBalance(accountId);
+        assertEquals(balance.compareTo(expectedFinalBalance), 0);
+        cba =  invoiceDao.getAccountCBA(accountId);
+
+        final BigDecimal expectedFinalCBA = (expectedFinalBalance.compareTo(BigDecimal.ZERO) < 0) ? expectedFinalBalance.negate() : BigDecimal.ZERO;
+        assertEquals(cba.compareTo(expectedFinalCBA), 0);
+
+    }
+
+    @Test(groups = {"slow"})
     public void testAccountBalanceWithAllSortsOfThings() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -638,7 +782,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testGetUnpaidInvoicesByAccountId() {
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -699,7 +843,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
      * this test verifies that immediate changes give the correct results
      *
      */
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoiceGenerationForImmediateChanges() throws InvoiceApiException, CatalogApiException {
         final UUID accountId = UUID.randomUUID();
         final List<Invoice> invoiceList = new ArrayList<Invoice>();
@@ -754,7 +898,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(savedInvoice2.getBalance(), FIFTEEN);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoiceForFreeTrial() throws InvoiceApiException, CatalogApiException {
         final Currency currency = Currency.USD;
         final DefaultPrice price = new DefaultPrice(BigDecimal.ZERO, Currency.USD);
@@ -786,7 +930,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         return subscription;
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoiceForFreeTrialWithRecurringDiscount() throws InvoiceApiException, CatalogApiException {
         final Currency currency = Currency.USD;
 
@@ -845,14 +989,14 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         //invoiceDao.create(invoice3, context);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoiceForEmptyEventSet() throws InvoiceApiException {
         final BillingEventSet events = new MockBillingEventSet();
         final Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, new DateTime(), Currency.USD);
         assertNull(invoice);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testMixedModeInvoicePersistence() throws InvoiceApiException, CatalogApiException {
         final Currency currency = Currency.USD;
         final DefaultPrice zeroPrice = new DefaultPrice(BigDecimal.ZERO, Currency.USD);
@@ -895,7 +1039,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertEquals(savedInvoice.getBalance().compareTo(cheapAmount), 0);
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testInvoiceNumber() throws InvoiceApiException {
         final Currency currency = Currency.USD;
         final DateTime targetDate1 = DateTime.now().plusMonths(1);
@@ -938,7 +1082,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertNotNull(invoice2.getInvoiceNumber());
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testAddingWrittenOffTag() throws InvoiceApiException, TagApiException {
         final Subscription subscription = getZombieSubscription();
 
@@ -969,7 +1113,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         assertTrue(tags.containsKey(ControlTagType.WRITTEN_OFF.toString()));
     }
 
-    @Test
+    @Test(groups = {"slow"})
     public void testRemoveWrittenOffTag() throws InvoiceApiException, TagApiException {
         final Subscription subscription = getZombieSubscription();
 
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 20e30b5..03c38ef 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright 2010-2011 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
@@ -303,11 +303,11 @@ public class PaymentProcessor extends ProcessorBase {
                 payment = paymentDao.getPayment(paymentInput.getId());
 
                 invoicePaymentApi.notifyOfPaymentAttempt(invoice.getId(),
-                        paymentStatus == PaymentStatus.SUCCESS ? payment.getAmount() : null,
-                                paymentStatus == PaymentStatus.SUCCESS ? payment.getCurrency() : null,
-                                        lastAttempt.getId(),
-                                        lastAttempt.getEffectiveDate(),
-                                        context);
+                        payment.getAmount(),
+                        paymentStatus == PaymentStatus.SUCCESS ? payment.getCurrency() : null,
+                                lastAttempt.getId(),
+                                lastAttempt.getEffectiveDate(),
+                                context);
 
                 // Create Bus event
                 event = new DefaultPaymentInfoEvent(account.getId(),