Details
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.
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);
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/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 ee27de3..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
@@ -33,9 +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;
@@ -95,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();
@@ -129,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")
@@ -656,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();
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);
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) {