diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 184ea75..d196729 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -50,7 +50,7 @@ public class AnalyticsListener {
private final BusinessSubscriptionTransitionRecorder bstRecorder;
private final BusinessAccountRecorder bacRecorder;
- private final BusinessInvoiceRecorder invoiceRecorder;
+ private final BusinessInvoiceDao invoiceDao;
private final BusinessOverdueStatusRecorder bosRecorder;
private final BusinessInvoicePaymentRecorder bipRecorder;
private final BusinessTagRecorder tagRecorder;
@@ -59,14 +59,14 @@ public class AnalyticsListener {
@Inject
public AnalyticsListener(final BusinessSubscriptionTransitionRecorder bstRecorder,
final BusinessAccountRecorder bacRecorder,
- final BusinessInvoiceRecorder invoiceRecorder,
+ final BusinessInvoiceDao invoiceDao,
final BusinessOverdueStatusRecorder bosRecorder,
final BusinessInvoicePaymentRecorder bipRecorder,
final BusinessTagRecorder tagRecorder,
final InternalCallContextFactory internalCallContextFactory) {
this.bstRecorder = bstRecorder;
this.bacRecorder = bacRecorder;
- this.invoiceRecorder = invoiceRecorder;
+ this.invoiceDao = invoiceDao;
this.bosRecorder = bosRecorder;
this.bipRecorder = bipRecorder;
this.tagRecorder = tagRecorder;
@@ -108,7 +108,7 @@ public class AnalyticsListener {
@Subscribe
public void handleInvoiceCreation(final InvoiceCreationEvent event) {
// The event is used as a trigger to rebuild all invoices and invoice items for this account
- invoiceRecorder.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+ invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
}
@Subscribe
@@ -119,7 +119,7 @@ public class AnalyticsListener {
@Subscribe
public void handleInvoiceAdjustment(final InvoiceAdjustmentEvent event) {
// The event is used as a trigger to rebuild all invoices and invoice items for this account
- invoiceRecorder.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
+ invoiceDao.rebuildInvoicesForAccount(event.getAccountId(), createCallContext(event));
}
@Subscribe
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java
index 42ec7d9..0f551d7 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoicePaymentRecorder.java
@@ -28,11 +28,12 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
-import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
import com.ning.billing.analytics.dao.BusinessInvoicePaymentSqlDao;
import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
import com.ning.billing.analytics.model.BusinessInvoicePayment;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoicePayment;
import com.ning.billing.invoice.api.InvoicePaymentApi;
import com.ning.billing.payment.api.Payment;
@@ -42,29 +43,33 @@ import com.ning.billing.payment.api.PaymentMethod;
import com.ning.billing.payment.api.PaymentMethodPlugin;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.svcapi.account.AccountInternalApi;
+import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
public class BusinessInvoicePaymentRecorder {
private static final Logger log = LoggerFactory.getLogger(BusinessInvoicePaymentRecorder.class);
private final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
- private final AccountUserApi accountApi;
+ private final AccountInternalApi accountApi;
private final InvoicePaymentApi invoicePaymentApi;
+ private final InvoiceInternalApi invoiceApi;
private final PaymentApi paymentApi;
private final Clock clock;
- private final BusinessInvoiceRecorder invoiceRecorder;
+ private final BusinessInvoiceDao invoiceDao;
private final BusinessAccountRecorder accountRecorder;
@Inject
- public BusinessInvoicePaymentRecorder(final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao, final AccountUserApi accountApi,
- final InvoicePaymentApi invoicePaymentApi, final PaymentApi paymentApi, final Clock clock,
- final BusinessInvoiceRecorder invoiceRecorder, final BusinessAccountRecorder accountRecorder) {
+ public BusinessInvoicePaymentRecorder(final BusinessInvoicePaymentSqlDao invoicePaymentSqlDao, final AccountInternalApi accountApi,
+ final InvoicePaymentApi invoicePaymentApi, final InvoiceInternalApi invoiceApi, final PaymentApi paymentApi,
+ final Clock clock, final BusinessInvoiceDao invoiceDao, final BusinessAccountRecorder accountRecorder) {
this.invoicePaymentSqlDao = invoicePaymentSqlDao;
this.accountApi = accountApi;
this.invoicePaymentApi = invoicePaymentApi;
+ this.invoiceApi = invoiceApi;
this.paymentApi = paymentApi;
this.clock = clock;
- this.invoiceRecorder = invoiceRecorder;
+ this.invoiceDao = invoiceDao;
this.accountRecorder = accountRecorder;
}
@@ -77,7 +82,7 @@ public class BusinessInvoicePaymentRecorder {
final Account account;
try {
- account = accountApi.getAccountById(accountId, context.toCallContext());
+ account = accountApi.getAccountById(accountId, context);
} catch (AccountApiException e) {
log.warn("Ignoring payment {}: account {} does not exist", paymentId, accountId);
return;
@@ -91,7 +96,6 @@ public class BusinessInvoicePaymentRecorder {
return;
}
- final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePaymentForAttempt(paymentId, context.toCallContext());
final PaymentMethod paymentMethod;
try {
paymentMethod = paymentApi.getPaymentMethod(account, payment.getPaymentMethodId(), true, context.toCallContext());
@@ -100,10 +104,20 @@ public class BusinessInvoicePaymentRecorder {
return;
}
- createPayment(account, invoicePayment, payment, paymentMethod, extFirstPaymentRefId, extSecondPaymentRefId, message, context);
+ final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePaymentForAttempt(paymentId, context.toCallContext());
+ Invoice invoice = null;
+ if (invoicePayment != null) {
+ try {
+ invoice = invoiceApi.getInvoiceById(invoicePayment.getInvoiceId(), context);
+ } catch (InvoiceApiException e) {
+ log.warn("Unable to find invoice {} for payment {}", invoicePayment.getInvoiceId(), paymentId);
+ }
+ }
+
+ createPayment(account, invoice, invoicePayment, payment, paymentMethod, extFirstPaymentRefId, extSecondPaymentRefId, message, context);
}
- private void createPayment(final Account account, @Nullable final InvoicePayment invoicePayment, final Payment payment,
+ private void createPayment(final Account account, @Nullable final Invoice invoice, @Nullable final InvoicePayment invoicePayment, final Payment payment,
final PaymentMethod paymentMethod, final String extFirstPaymentRefId, final String extSecondPaymentRefId,
final String message, final InternalCallContext context) {
final PaymentMethodPlugin pluginDetail = paymentMethod.getPluginDetail();
@@ -111,50 +125,54 @@ public class BusinessInvoicePaymentRecorder {
final String cardType = PaymentMethodUtils.getCardType(pluginDetail);
final String paymentMethodString = PaymentMethodUtils.getPaymentMethodType(pluginDetail);
+ // invoicePayment may be null on payment failures
+ final String invoicePaymentType;
+ final UUID linkedInvoicePaymentId;
+ if (invoicePayment != null) {
+ invoicePaymentType = invoicePayment.getType().toString();
+ linkedInvoicePaymentId = invoicePayment.getLinkedInvoicePaymentId();
+ } else {
+ invoicePaymentType = null;
+ linkedInvoicePaymentId = null;
+ }
+
+ final BusinessInvoicePayment businessInvoicePayment = new BusinessInvoicePayment(
+ account.getExternalKey(),
+ payment.getAmount(),
+ extFirstPaymentRefId,
+ extSecondPaymentRefId,
+ cardCountry,
+ cardType,
+ clock.getUTCNow(),
+ payment.getCurrency(),
+ payment.getEffectiveDate(),
+ payment.getInvoiceId(),
+ message,
+ payment.getId(),
+ paymentMethodString,
+ "Electronic",
+ paymentMethod.getPluginName(),
+ payment.getPaymentStatus().toString(),
+ payment.getAmount(),
+ clock.getUTCNow(),
+ invoicePaymentType,
+ linkedInvoicePaymentId);
+
+ // Make sure to limit the scope of the transaction to avoid InnoDB deadlocks
invoicePaymentSqlDao.inTransaction(new Transaction<Void, BusinessInvoicePaymentSqlDao>() {
@Override
public Void inTransaction(final BusinessInvoicePaymentSqlDao transactional, final TransactionStatus status) throws Exception {
// Delete the existing payment if it exists - this is to make the call idempotent
transactional.deleteInvoicePayment(payment.getId().toString(), context);
- // invoicePayment may be null on payment failures
- final String invoicePaymentType;
- final UUID linkedInvoicePaymentId;
- if (invoicePayment != null) {
- invoicePaymentType = invoicePayment.getType().toString();
- linkedInvoicePaymentId = invoicePayment.getLinkedInvoicePaymentId();
- } else {
- invoicePaymentType = null;
- linkedInvoicePaymentId = null;
- }
-
// Create the bip record
- final BusinessInvoicePayment businessInvoicePayment = new BusinessInvoicePayment(
- account.getExternalKey(),
- payment.getAmount(),
- extFirstPaymentRefId,
- extSecondPaymentRefId,
- cardCountry,
- cardType,
- clock.getUTCNow(),
- payment.getCurrency(),
- payment.getEffectiveDate(),
- payment.getInvoiceId(),
- message,
- payment.getId(),
- paymentMethodString,
- "Electronic",
- paymentMethod.getPluginName(),
- payment.getPaymentStatus().toString(),
- payment.getAmount(),
- clock.getUTCNow(),
- invoicePaymentType,
- linkedInvoicePaymentId);
transactional.createInvoicePayment(businessInvoicePayment, context);
- // Update bin to get the latest invoice(s) balance(s)
- final BusinessInvoiceSqlDao invoiceSqlDao = transactional.become(BusinessInvoiceSqlDao.class);
- invoiceRecorder.rebuildInvoicesForAccountInTransaction(account.getId(), invoiceSqlDao, context);
+ if (invoice != null) {
+ // Update bin to get the latest invoice balance
+ final BusinessInvoiceSqlDao invoiceSqlDao = transactional.become(BusinessInvoiceSqlDao.class);
+ invoiceDao.rebuildInvoiceInTransaction(account.getExternalKey(), invoice, invoiceSqlDao, context);
+ }
// Update bac to get the latest account balance, total invoice balance, etc.
final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java
index fce78b9..25d5e01 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestBusinessInvoiceRecorder.java
@@ -24,29 +24,29 @@ import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;
-import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
import com.ning.billing.analytics.model.BusinessInvoiceItem;
import com.ning.billing.catalog.MockCatalog;
import com.ning.billing.catalog.MockCatalogService;
import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.api.InvoiceItemType;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.svcapi.account.AccountInternalApi;
+import com.ning.billing.util.svcapi.entitlement.EntitlementInternalApi;
+import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
public class TestBusinessInvoiceRecorder extends AnalyticsTestSuite {
- private final AccountUserApi accountApi = Mockito.mock(AccountUserApi.class);
- private final EntitlementUserApi entitlementApi = Mockito.mock(EntitlementUserApi.class);
- private final InvoiceUserApi invoiceApi = Mockito.mock(InvoiceUserApi.class);
+ private final AccountInternalApi accountApi = Mockito.mock(AccountInternalApi.class);
+ private final EntitlementInternalApi entitlementApi = Mockito.mock(EntitlementInternalApi.class);
+ private final InvoiceInternalApi invoiceApi = Mockito.mock(InvoiceInternalApi.class);
+ private final BusinessAccountRecorder bacDao = Mockito.mock(BusinessAccountRecorder.class);
private final BusinessInvoiceSqlDao sqlDao = Mockito.mock(BusinessInvoiceSqlDao.class);
- private final Clock clock = Mockito.mock(Clock.class);
@Test(groups = "fast")
public void testShouldBeAbleToHandleNullFieldsInInvoiceItem() throws Exception {
- final BusinessInvoiceRecorder recorder = new BusinessInvoiceRecorder(accountApi, entitlementApi, invoiceApi, sqlDao, new MockCatalogService(new MockCatalog()), clock);
+ final BusinessInvoiceDao dao = new BusinessInvoiceDao(accountApi, entitlementApi, invoiceApi, bacDao,
+ sqlDao, new MockCatalogService(new MockCatalog()));
final InvoiceItem invoiceItem = Mockito.mock(InvoiceItem.class);
Mockito.when(invoiceItem.getAmount()).thenReturn(BigDecimal.TEN);
@@ -59,7 +59,7 @@ public class TestBusinessInvoiceRecorder extends AnalyticsTestSuite {
Mockito.when(invoiceItem.getStartDate()).thenReturn(new LocalDate(1985, 9, 10));
Mockito.when(invoiceItem.getInvoiceItemType()).thenReturn(InvoiceItemType.CREDIT_ADJ);
- final BusinessInvoiceItem bii = recorder.createBusinessInvoiceItem(invoiceItem, internalCallContext);
+ final BusinessInvoiceItem bii = dao.createBusinessInvoiceItem(invoiceItem, internalCallContext);
Assert.assertNotNull(bii);
Assert.assertEquals(bii.getAmount(), invoiceItem.getAmount());
Assert.assertEquals(bii.getCurrency(), invoiceItem.getCurrency());
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index d687736..70833fc 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -13,6 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.invoice.api.svcs;
import java.util.Collection;
@@ -23,6 +24,7 @@ import javax.inject.Inject;
import org.joda.time.LocalDate;
import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.dao.InvoiceDao;
import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
@@ -33,12 +35,21 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
@Inject
public DefaultInvoiceInternalApi(final InvoiceDao dao) {
- this.dao = dao;
+ this.dao = dao;
+ }
+
+ @Override
+ public Invoice getInvoiceById(final UUID invoiceId, final InternalTenantContext context) throws InvoiceApiException {
+ return dao.getById(invoiceId, context);
}
@Override
- public Collection<Invoice> getUnpaidInvoicesByAccountId(UUID accountId,
- LocalDate upToDate, InternalTenantContext context) {
+ public Collection<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final InternalTenantContext context) {
return dao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
}
+
+ @Override
+ public Collection<Invoice> getInvoicesByAccountId(final UUID accountId, final InternalTenantContext context) {
+ return dao.getInvoicesByAccount(accountId, context);
+ }
}