Details
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 bcddd64..1a2585b 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -57,11 +57,13 @@ public class AnalyticsListener {
@Subscribe
public void handleEffectiveSubscriptionTransitionChange(final EffectiveSubscriptionEvent eventEffective) throws AccountApiException, EntitlementUserApiException {
+ // The event is used as a trigger to rebuild all transitions for this bundle
bstRecorder.rebuildTransitionsForBundle(eventEffective.getBundleId());
}
@Subscribe
public void handleRequestedSubscriptionTransitionChange(final RequestedSubscriptionEvent eventRequested) throws AccountApiException, EntitlementUserApiException {
+ // The event is used as a trigger to rebuild all transitions for this bundle
bstRecorder.rebuildTransitionsForBundle(eventRequested.getBundleId());
}
@@ -87,8 +89,8 @@ public class AnalyticsListener {
@Subscribe
public void handleInvoiceCreation(final InvoiceCreationEvent event) {
- // TODO - follow same logic as entitlements to support repair
- invoiceRecorder.invoiceCreated(event.getInvoiceId());
+ // The event is used as a trigger to rebuild all invoices and invoice items for this account
+ invoiceRecorder.rebuildInvoicesForAccount(event.getAccountId());
}
@Subscribe
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
index 21693f3..59912a3 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessInvoiceRecorder.java
@@ -21,13 +21,18 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
import org.slf4j.Logger;
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.AnalyticsDao;
+import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceItemSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.analytics.model.BusinessAccount;
import com.ning.billing.analytics.model.BusinessInvoice;
import com.ning.billing.analytics.model.BusinessInvoiceItem;
import com.ning.billing.catalog.api.Plan;
@@ -39,46 +44,73 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.util.clock.Clock;
public class BusinessInvoiceRecorder {
private static final Logger log = LoggerFactory.getLogger(BusinessInvoiceRecorder.class);
- private final AnalyticsDao analyticsDao;
private final AccountUserApi accountApi;
private final EntitlementUserApi entitlementApi;
private final InvoiceUserApi invoiceApi;
+ private final BusinessInvoiceSqlDao sqlDao;
+ private final Clock clock;
@Inject
- public BusinessInvoiceRecorder(final AnalyticsDao analyticsDao,
- final AccountUserApi accountApi,
+ public BusinessInvoiceRecorder(final AccountUserApi accountApi,
final EntitlementUserApi entitlementApi,
- final InvoiceUserApi invoiceApi) {
- this.analyticsDao = analyticsDao;
+ final InvoiceUserApi invoiceApi,
+ final BusinessInvoiceSqlDao sqlDao,
+ final Clock clock) {
this.accountApi = accountApi;
this.entitlementApi = entitlementApi;
this.invoiceApi = invoiceApi;
+ this.sqlDao = sqlDao;
+ this.clock = clock;
}
- public void invoiceCreated(final UUID invoiceId) {
- // Lookup the invoice object
- final Invoice invoice = invoiceApi.getInvoice(invoiceId);
- if (invoice == null) {
- log.warn("Ignoring invoice creation for invoice id {} (invoice does not exist)", invoiceId.toString());
- return;
- }
-
+ public void rebuildInvoicesForAccount(final UUID accountId) {
// Lookup the associated account
final String accountKey;
try {
- final Account account = accountApi.getAccountById(invoice.getAccountId());
+ final Account account = accountApi.getAccountById(accountId);
accountKey = account.getExternalKey();
} catch (AccountApiException e) {
- log.warn("Ignoring invoice creation for invoice id {} and account id {} (account does not exist)",
- invoice.getId().toString(),
- invoice.getAccountId().toString());
+ log.warn("Ignoring invoice update for account id {} (account does not exist)", accountId);
return;
}
+ sqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
+ @Override
+ public Void inTransaction(final BusinessInvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
+ log.info("Started rebuilding transitions for account id {}", accountId);
+ deleteInvoicesAndInvoiceItemsForAccountInTransaction(transactional, accountKey);
+
+ for (final Invoice invoice : invoiceApi.getInvoicesByAccount(accountId)) {
+ createInvoiceInTransaction(transactional, accountKey, invoice);
+ }
+
+ log.info("Finished rebuilding transitions for account id {}", accountId);
+ return null;
+ }
+ });
+ }
+
+ private void deleteInvoicesAndInvoiceItemsForAccountInTransaction(final BusinessInvoiceSqlDao transactional, final String accountKey) {
+ // We don't use on cascade delete here as we don't want the database layer to be generic - hence we have
+ // to delete the invoice items manually.
+ final List<BusinessInvoice> invoicesToDelete = transactional.getInvoicesForAccount(accountKey);
+ final BusinessInvoiceItemSqlDao invoiceItemSqlDao = transactional.become(BusinessInvoiceItemSqlDao.class);
+ for (final BusinessInvoice businessInvoice : invoicesToDelete) {
+ final List<BusinessInvoiceItem> invoiceItemsForInvoice = invoiceItemSqlDao.getInvoiceItemsForInvoice(businessInvoice.getInvoiceId().toString());
+ for (final BusinessInvoiceItem invoiceItemToDelete : invoiceItemsForInvoice) {
+ invoiceItemSqlDao.deleteInvoiceItem(invoiceItemToDelete.getItemId().toString());
+ }
+ }
+
+ transactional.deleteInvoicesForAccount(accountKey);
+ }
+
+ private void createInvoiceInTransaction(final BusinessInvoiceSqlDao transactional, final String accountKey, final Invoice invoice) {
// Create the invoice
final BusinessInvoice businessInvoice = new BusinessInvoice(accountKey, invoice);
@@ -91,8 +123,33 @@ public class BusinessInvoiceRecorder {
}
}
- // Update the Analytics tables
- analyticsDao.createInvoice(accountKey, businessInvoice, businessInvoiceItems);
+ createInvoiceInTransaction(transactional, accountKey, businessInvoice, businessInvoiceItems);
+ }
+
+ private void createInvoiceInTransaction(final BusinessInvoiceSqlDao transactional, final String accountKey,
+ final BusinessInvoice invoice, final Iterable<BusinessInvoiceItem> invoiceItems) {
+ // Create the invoice
+ log.info("Adding invoice {}", invoice);
+ transactional.createInvoice(invoice);
+
+ // Add associated invoice items
+ final BusinessInvoiceItemSqlDao invoiceItemSqlDao = transactional.become(BusinessInvoiceItemSqlDao.class);
+ for (final BusinessInvoiceItem invoiceItem : invoiceItems) {
+ log.info("Adding invoice item {}", invoiceItem);
+ invoiceItemSqlDao.createInvoiceItem(invoiceItem);
+ }
+
+ // Update BAC
+ final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
+ final BusinessAccount account = accountSqlDao.getAccount(accountKey);
+ if (account == null) {
+ throw new IllegalStateException("Account does not exist for key " + accountKey);
+ }
+ account.setBalance(account.getBalance().add(invoice.getBalance()));
+ account.setLastInvoiceDate(invoice.getInvoiceDate());
+ account.setTotalInvoiceBalance(account.getTotalInvoiceBalance().add(invoice.getBalance()));
+ account.setUpdatedDt(clock.getUTCNow());
+ accountSqlDao.saveAccount(account);
}
private BusinessInvoiceItem createBusinessInvoiceItem(final InvoiceItem invoiceItem) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
index 8dcc6c8..2f0b6db 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
@@ -34,6 +34,4 @@ public interface AnalyticsDao {
List<BusinessAccountTag> getTagsForAccount(final String accountKey);
List<BusinessInvoiceItem> getInvoiceItemsForInvoice(final String invoiceId);
-
- void createInvoice(final String accountKey, final BusinessInvoice invoice, final Iterable<BusinessInvoiceItem> invoiceItems);
}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.java
index d3c8c6e..7348fe3 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.java
@@ -47,5 +47,8 @@ public interface BusinessInvoiceSqlDao extends Transactional<BusinessInvoiceSqlD
int deleteInvoice(@Bind("invoice_id") final String invoiceId);
@SqlUpdate
+ void deleteInvoicesForAccount(@Bind("account_key") final String accountKey);
+
+ @SqlUpdate
void test();
}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
index d5f8627..410ee6e 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
@@ -74,35 +74,4 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
public List<BusinessInvoiceItem> getInvoiceItemsForInvoice(final String invoiceId) {
return invoiceItemSqlDao.getInvoiceItemsForInvoice(invoiceId);
}
-
- @Override
- public void createInvoice(final String accountKey, final BusinessInvoice invoice, final Iterable<BusinessInvoiceItem> invoiceItems) {
- invoiceSqlDao.inTransaction(new Transaction<Void, BusinessInvoiceSqlDao>() {
- @Override
- public Void inTransaction(final BusinessInvoiceSqlDao transactional, final TransactionStatus status) throws Exception {
- // Create the invoice
- transactional.createInvoice(invoice);
-
- // Add associated invoice items
- final BusinessInvoiceItemSqlDao invoiceItemSqlDao = transactional.become(BusinessInvoiceItemSqlDao.class);
- for (final BusinessInvoiceItem invoiceItem : invoiceItems) {
- invoiceItemSqlDao.createInvoiceItem(invoiceItem);
- }
-
- // Update BAC
- final BusinessAccountSqlDao accountSqlDao = transactional.become(BusinessAccountSqlDao.class);
- final BusinessAccount account = accountSqlDao.getAccount(accountKey);
- if (account == null) {
- throw new IllegalStateException("Account does not exist for key " + accountKey);
- }
- account.setBalance(account.getBalance().add(invoice.getBalance()));
- account.setLastInvoiceDate(invoice.getInvoiceDate());
- account.setTotalInvoiceBalance(account.getTotalInvoiceBalance().add(invoice.getBalance()));
- account.setUpdatedDt(new DateTime(DateTimeZone.UTC));
- accountSqlDao.saveAccount(account);
-
- return null;
- }
- });
- }
}
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
index 83d3ffc..ed8d685 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessInvoiceSqlDao.sql.stg
@@ -85,6 +85,10 @@ deleteInvoice(invoice_id) ::= <<
delete from bin where invoice_id = :invoice_id;
>>
+deleteInvoicesForAccount(account_key) ::= <<
+delete from bin where account_key = :account_key;
+>>
+
test() ::= <<
select 1 from bin;
->>
\ No newline at end of file
+>>