killbill-memoizeit

Changes

NEWS 2(+2 -0)

pom.xml 2(+1 -1)

util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditLogsForAccount.java 36(+0 -36)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForBundles.java 94(+0 -94)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoicePayments.java 67(+0 -67)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForPayments.java 67(+0 -67)

util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForRefunds.java 67(+0 -67)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForBundles.java 49(+0 -49)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoices.java 42(+0 -42)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForPayments.java 34(+0 -34)

util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForRefunds.java 34(+0 -34)

Details

diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index 9fd099b..c26cb32 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -33,6 +33,8 @@ import com.ning.billing.account.api.AccountEmail;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.api.DefaultAccountEmail;
 import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.CustomFieldApiException;
@@ -40,6 +42,7 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
 import com.ning.billing.util.customfield.CustomField;
 import com.ning.billing.util.customfield.StringCustomField;
 import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
@@ -83,6 +86,39 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
         Assert.assertEquals(auditLogsForAccount.get(0).getChangeType(), ChangeType.INSERT);
     }
 
+    @Test(groups = "slow", description = "Test Account: verify audits")
+    public void testAudits() throws AccountApiException {
+        // Special test to verify audits - they are handled a bit differently due to the account record id (see EntitySqlDaoWrapperInvocationHandler#insertAudits)
+        final AccountModelDao account1 = createTestAccount();
+        accountDao.create(account1, internalCallContext);
+        final Long account1RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account1.getId(), ObjectType.ACCOUNT, null);
+        final InternalCallContext internalCallContext1 = new InternalCallContext(internalCallContext, account1RecordId);
+
+        // Verify audits via account record id
+        final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId1 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext1);
+        Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId1.getAuditLogsForAccount().size(), 1);
+        Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId1.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
+
+        // Add an entry in the account_history table to make sure we pick up the right
+        // record id / target record id / account record id in the audit_log table
+        accountDao.updatePaymentMethod(account1.getId(), UUID.randomUUID(), internalCallContext1);
+
+        final AccountModelDao account2 = createTestAccount();
+        accountDao.create(account2, internalCallContext);
+        final Long account2RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account2.getId(), ObjectType.ACCOUNT, null);
+        final InternalTenantContext internalTenantContext2 = new InternalCallContext(internalCallContext, account2RecordId);
+
+        // Verify audits via account record id
+        final DefaultAccountAuditLogs auditLogsForAccount2ViaAccountRecordId = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalTenantContext2);
+        Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().size(), 1);
+        Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
+
+        final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId2 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext1);
+        Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().size(), 2);
+        Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
+        Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().get(1).getChangeType(), ChangeType.UPDATE);
+    }
+
     // Simple test to ensure long phone numbers can be stored
     @Test(groups = "slow", description = "Test Account DAO: long numbers")
     public void testLongPhoneNumber() throws AccountApiException {
@@ -129,7 +165,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
         final Tag tag = new DescriptiveTag(tagDefinition.getId(), ObjectType.ACCOUNT, account.getId(), internalCallContext.getCreatedDate());
         tagDao.create(new TagModelDao(tag), internalCallContext);
 
-        final List<TagModelDao> tags = tagDao.getTagsForObject(account.getId(), ObjectType.ACCOUNT, internalCallContext);
+        final List<TagModelDao> tags = tagDao.getTagsForObject(account.getId(), ObjectType.ACCOUNT, false, internalCallContext);
         Assert.assertEquals(tags.size(), 1);
         Assert.assertEquals(tags.get(0).getTagDefinitionId(), tagDefinition.getId());
         Assert.assertEquals(tags.get(0).getObjectId(), account.getId());
diff --git a/api/src/main/java/com/ning/billing/callcontext/InternalCallContext.java b/api/src/main/java/com/ning/billing/callcontext/InternalCallContext.java
index fb83e76..ae6e1c9 100644
--- a/api/src/main/java/com/ning/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/com/ning/billing/callcontext/InternalCallContext.java
@@ -62,6 +62,12 @@ public class InternalCallContext extends InternalTenantContext {
              callContext.getUpdatedDate());
     }
 
+    public InternalCallContext(final InternalCallContext context, final Long accountRecordId) {
+        this(context.getTenantRecordId(), accountRecordId, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
+             context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
+             context.getUpdatedDate());
+    }
+
     // TODO should not be needed if all services are using internal API
     // Unfortunately not true as some APIs ae hidden in object -- e.g OverdueStateApplicator is doing subscription.cancelEntitlementWithDateOverrideBillingPolicy
     public CallContext toCallContext(final UUID tenantId) {
diff --git a/api/src/main/java/com/ning/billing/entity/EntityBase.java b/api/src/main/java/com/ning/billing/entity/EntityBase.java
index b0f4fea..06c2cc2 100644
--- a/api/src/main/java/com/ning/billing/entity/EntityBase.java
+++ b/api/src/main/java/com/ning/billing/entity/EntityBase.java
@@ -45,7 +45,7 @@ public abstract class EntityBase implements Entity {
     }
 
     public EntityBase(final EntityBase target) {
-        this.id = UUID.randomUUID();
+        this.id = target.getId();
         this.createdDate = target.getCreatedDate();
         this.updatedDate = target.getUpdatedDate();
     }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestJrubyNotificationPlugin.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestJrubyNotificationPlugin.java
index 77eb9b2..1f12d13 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestJrubyNotificationPlugin.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/osgi/TestJrubyNotificationPlugin.java
@@ -57,7 +57,7 @@ public class TestJrubyNotificationPlugin extends TestOSGIBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(4));
         assertTrue(busHandler.isCompleted(2 * DELAY));
 
-        final List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), callContext);
+        final List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), false, callContext);
         Assert.assertEquals(tags.size(), 1);
         //final Tag tag = tags.get(0);
     }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 80fbf5a..ab97c53 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -147,7 +147,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         busHandler.pushExpectedEvent(NextEvent.TAG);
         tagApi.addTag(id, type, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
         assertListenerStatus();
-        final List<Tag> tags = tagApi.getTagsForObject(id, type, callContext);
+        final List<Tag> tags = tagApi.getTagsForObject(id, type, false, callContext);
         assertEquals(tags.size(), 1);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 06bae39..7816769 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -270,7 +270,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         tagApi.addTag(id, type, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
         assertListenerStatus();
 
-        final List<Tag> tags = tagApi.getTagsForObject(id, type, callContext);
+        final List<Tag> tags = tagApi.getTagsForObject(id, type, false, callContext);
         assertEquals(tags.size(), 1);
     }
 
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestTagApi.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestTagApi.java
index 6fe6a54..663612b 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestTagApi.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestTagApi.java
@@ -59,15 +59,15 @@ public class TestTagApi extends TestIntegrationBase {
         tagUserApi.addTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
         assertListenerStatus();
 
-        List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), callContext);
+        List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForObject(account.getId(), ObjectType.ACCOUNT, callContext);
+        tags = tagUserApi.getTagsForObject(account.getId(), ObjectType.ACCOUNT, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.ACCOUNT, callContext);
+        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.ACCOUNT, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
     }
@@ -109,15 +109,15 @@ public class TestTagApi extends TestIntegrationBase {
         tagUserApi.addTag(invoice.getId(), ObjectType.INVOICE, tagDefinition.getId(), callContext);
         assertListenerStatus();
 
-        List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), callContext);
+        List<Tag> tags = tagUserApi.getTagsForAccount(account.getId(), false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForObject(invoice.getId(), ObjectType.INVOICE, callContext);
+        tags = tagUserApi.getTagsForObject(invoice.getId(), ObjectType.INVOICE, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.INVOICE, callContext);
+        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.INVOICE, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
@@ -128,15 +128,15 @@ public class TestTagApi extends TestIntegrationBase {
         tagUserApi.addTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.AUTO_PAY_OFF.getId(), callContext);
         assertListenerStatus();
 
-        tags = tagUserApi.getTagsForAccount(account.getId(), callContext);
+        tags = tagUserApi.getTagsForAccount(account.getId(), false, callContext);
         Assert.assertEquals(tags.size(), 3);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForObject(invoice.getId(), ObjectType.INVOICE, callContext);
+        tags = tagUserApi.getTagsForObject(invoice.getId(), ObjectType.INVOICE, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
 
-        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.INVOICE, callContext);
+        tags = tagUserApi.getTagsForAccountType(account.getId(), ObjectType.INVOICE, false, callContext);
         Assert.assertEquals(tags.size(), 2);
         checkTagsExists(tags);
     }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
index 1099bf6..9d71eb5 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/util/AuditChecker.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.beatrix.util;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -46,14 +45,12 @@ import com.ning.billing.subscription.engine.dao.SubscriptionEventSqlDao;
 import com.ning.billing.subscription.engine.dao.SubscriptionSqlDao;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForAccount;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.Entity;
 import com.ning.billing.util.entity.dao.EntityModelDao;
@@ -85,15 +82,14 @@ public class AuditChecker {
      */
 
     public void checkAccountCreated(final Account account, final CallContext context) {
-        AuditLogsForAccount result = auditUserApi.getAuditLogsForAccount(account.getId(), AuditLevel.FULL, context);
-        checkAuditLog(ChangeType.INSERT, context, result.getAccountAuditLogs().get(0), account.getId(), AccountSqlDao.class, true, true);
-        checkAuditLog(ChangeType.UPDATE, context, result.getAccountAuditLogs().get(1), account.getId(), AccountSqlDao.class, true, true);
+        final AccountAuditLogs result = auditUserApi.getAccountAuditLogs(account.getId(), AuditLevel.FULL, context);
+        checkAuditLog(ChangeType.INSERT, context, result.getAuditLogsForAccount().get(0), account.getId(), AccountSqlDao.class, true, true);
+        checkAuditLog(ChangeType.UPDATE, context, result.getAuditLogsForAccount().get(1), account.getId(), AccountSqlDao.class, true, true);
     }
 
     public void checkPaymentCreated(final Payment payment, final CallContext context) {
-        AuditLogsForPayments result = getAuditLogsForPayment(payment, context);
-        Assert.assertEquals(result.getPaymentsAuditLogs().size(), 1);
-        final List<AuditLog> paymentLogs = result.getPaymentsAuditLogs().get(payment.getId());
+        final AccountAuditLogs result = auditUserApi.getAccountAuditLogs(payment.getAccountId(), AuditLevel.FULL, context);
+        final List<AuditLog> paymentLogs = result.getAuditLogsForPayment(payment.getId());
         Assert.assertEquals(paymentLogs.size(), 2);
         checkAuditLog(ChangeType.INSERT, context, paymentLogs.get(0), payment.getId(), PaymentSqlDao.class, true, false);
         checkAuditLog(ChangeType.UPDATE, context, paymentLogs.get(1), payment.getId(), PaymentSqlDao.class, true, false);
@@ -105,16 +101,16 @@ public class AuditChecker {
 
     // Pass the call callcontext used to create the bundle
     public void checkBundleCreated(final UUID bundleId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(0), bundleId, BundleSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertTrue(auditLogsForBundles.size() >= 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.get(0), bundleId, BundleSqlDao.class, false, false);
     }
 
     // Pass the call callcontext used to update the bundle
     public void checkBundleUpdated(final UUID bundleId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        Assert.assertEquals(auditLogsForBundles.getBundlesAuditLogs().keySet().size(), 1);
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getBundlesAuditLogs().get(bundleId).get(auditLogsForBundles.getBundlesAuditLogs().get(bundleId).size() - 1), bundleId, BundleSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
+        Assert.assertTrue(auditLogsForBundles.size() > 1);
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.get(auditLogsForBundles.size() - 1), bundleId, BundleSqlDao.class, false, false);
     }
 
     /**
@@ -123,19 +119,17 @@ public class AuditChecker {
 
     // Pass the call callcontext used to create the subscription
     public void checkSubscriptionCreated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0), subscriptionId, SubscriptionSqlDao.class, false, true);
+        final List<AuditLog> auditLogsForSubscription = getAuditLogsForSubscription(bundleId, subscriptionId, context);
+        Assert.assertTrue(auditLogsForSubscription.size() >= 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForSubscription.get(0), subscriptionId, SubscriptionSqlDao.class, false, true);
     }
 
     // Pass the call callcontext used to update the subscription
     public void checkSubscriptionUpdated(final UUID bundleId, final UUID subscriptionId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-
-        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().keySet().size(), 1);
-        Assert.assertEquals(auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).size(), 2);
-        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionsAuditLogs().get(subscriptionId).get(1), subscriptionId, SubscriptionSqlDao.class, false, false);
+        final List<AuditLog> auditLogsForSubscription = getAuditLogsForSubscription(bundleId, subscriptionId, context);
+        Assert.assertEquals(auditLogsForSubscription.size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForSubscription.get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForSubscription.get(1), subscriptionId, SubscriptionSqlDao.class, false, false);
     }
 
     /**
@@ -144,24 +138,17 @@ public class AuditChecker {
 
     // Pass the call callcontext used to create the subscription event
     public void checkSubscriptionEventCreated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        checkAuditLog(ChangeType.INSERT, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
+        final List<AuditLog> auditLogsForSubscriptionEvent = getAuditLogsForSubscriptionEvent(bundleId, subscriptionEventId, context);
+        Assert.assertEquals(auditLogsForSubscriptionEvent.size(), 1);
+        checkAuditLog(ChangeType.INSERT, context, auditLogsForSubscriptionEvent.get(0), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
     }
 
     // Pass the call callcontext used to update the subscription event
     public void checkSubscriptionEventUpdated(final UUID bundleId, final UUID subscriptionEventId, final CallContext context) {
-        final AuditLogsForBundles auditLogsForBundles = getAuditLogsForBundle(bundleId, context);
-        checkAuditLog(ChangeType.INSERT, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(0));
-        checkAuditLog(ChangeType.UPDATE, context, auditLogsForBundles.getSubscriptionEventsAuditLogs().get(subscriptionEventId).get(1), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
-    }
-
-    /**
-     * ********************************************  PAYMENT *******************************************************
-     */
-
-    private AuditLogsForPayments getAuditLogsForPayment(final Payment payment, final CallContext context) {
-        AuditLogsForPayments results = auditUserApi.getAuditLogsForPayments(Collections.singletonList(payment), AuditLevel.FULL, context);
-        return results;
+        final List<AuditLog> auditLogsForSubscriptionEvent = getAuditLogsForSubscriptionEvent(bundleId, subscriptionEventId, context);
+        Assert.assertEquals(auditLogsForSubscriptionEvent.size(), 2);
+        checkAuditLog(ChangeType.INSERT, auditLogsForSubscriptionEvent.get(0));
+        checkAuditLog(ChangeType.UPDATE, context, auditLogsForSubscriptionEvent.get(1), subscriptionEventId, SubscriptionEventSqlDao.class, false, true);
     }
 
     /**
@@ -169,32 +156,53 @@ public class AuditChecker {
      */
 
     public void checkInvoiceCreated(final Invoice invoice, final CallContext context) {
-        AuditLogsForInvoices result = getAuditLogForInvoice(invoice, context);
-        Assert.assertEquals(result.getInvoiceAuditLogs().keySet().size(), 1);
-        final List<AuditLog> invoiceLogs = result.getInvoiceAuditLogs().get(invoice.getId());
+        final List<AuditLog> invoiceLogs = getAuditLogForInvoice(invoice, context);
         Assert.assertEquals(invoiceLogs.size(), 1);
         checkAuditLog(ChangeType.INSERT, context, invoiceLogs.get(0), invoice.getId(), InvoiceSqlDao.class, false, false);
 
-        Assert.assertEquals(result.getInvoiceItemsAuditLogs().keySet().size(), invoice.getInvoiceItems().size());
         for (InvoiceItem cur : invoice.getInvoiceItems()) {
-            final List<AuditLog> auditLogs = result.getInvoiceItemsAuditLogs().get(cur.getId());
+            final List<AuditLog> auditLogs = getAuditLogForInvoiceItem(cur, context);
             Assert.assertEquals(auditLogs.size(), 1);
             checkAuditLog(ChangeType.INSERT, context, auditLogs.get(0), cur.getId(), InvoiceItemSqlDao.class, false, false);
         }
     }
 
-    private AuditLogsForBundles getAuditLogsForBundle(final UUID bundleId, final CallContext context) {
+    private List<AuditLog> getAuditLogsForBundle(final UUID bundleId, final TenantContext context) {
+        try {
+            final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForBundle(bundle.getId());
+        } catch (final SubscriptionApiException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private List<AuditLog> getAuditLogsForSubscription(final UUID bundleId, final UUID subscriptionId, final TenantContext context) {
         try {
             final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
-            return auditUserApi.getAuditLogsForBundles(Collections.singletonList(bundle), AuditLevel.FULL, context);
-        } catch (SubscriptionApiException e) {
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForSubscription(subscriptionId);
+        } catch (final SubscriptionApiException e) {
             Assert.fail(e.toString());
             return null;
         }
     }
 
-    private AuditLogsForInvoices getAuditLogForInvoice(final Invoice invoice, final CallContext context) {
-        return auditUserApi.getAuditLogsForInvoices(Collections.singletonList(invoice), AuditLevel.FULL, context);
+    private List<AuditLog> getAuditLogsForSubscriptionEvent(final UUID bundleId, final UUID subscriptionEventId, final TenantContext context) {
+        try {
+            final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context);
+            return auditUserApi.getAccountAuditLogs(bundle.getAccountId(), AuditLevel.FULL, context).getAuditLogsForSubscriptionEvent(subscriptionEventId);
+        } catch (final SubscriptionApiException e) {
+            Assert.fail(e.toString());
+            return null;
+        }
+    }
+
+    private List<AuditLog> getAuditLogForInvoice(final Invoice invoice, final TenantContext context) {
+        return auditUserApi.getAccountAuditLogs(invoice.getAccountId(), AuditLevel.FULL, context).getAuditLogsForInvoice(invoice.getId());
+    }
+
+    private List<AuditLog> getAuditLogForInvoiceItem(final InvoiceItem invoiceItem, final TenantContext context) {
+        return auditUserApi.getAccountAuditLogs(invoiceItem.getAccountId(), AuditLevel.FULL, context).getAuditLogsForInvoiceItem(invoiceItem.getId());
     }
 
     private void checkAuditLog(final ChangeType insert, final AuditLog auditLog) {
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 6d0feb4..d3b627f 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
@@ -147,7 +147,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
     @Override
     public Invoice getInvoiceByNumber(final Integer number, final TenantContext context) throws InvoiceApiException {
-        // TODO (PIERRE) Populate accountRecordId in callcontext
+        // The account record id will be populated in the DAO
         return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContext(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 1ea4350..754b74d 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
@@ -43,6 +43,7 @@ import com.ning.billing.invoice.api.InvoicePaymentType;
 import com.ning.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
 import com.ning.billing.invoice.notification.NextBillingDatePoster;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.dao.EntityDaoBase;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
@@ -71,6 +72,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
     private final NextBillingDatePoster nextBillingDatePoster;
     private final PersistentBus eventBus;
+    private final InternalCallContextFactory internalCallContextFactory;
     private final InvoiceDaoHelper invoiceDaoHelper;
     private final CBADao cbaDao;
 
@@ -80,10 +82,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                              final PersistentBus eventBus,
                              final Clock clock,
                              final CacheControllerDispatcher cacheControllerDispatcher,
-                             final NonEntityDao nonEntityDao) {
+                             final NonEntityDao nonEntityDao,
+                             final InternalCallContextFactory internalCallContextFactory) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), InvoiceSqlDao.class);
         this.nextBillingDatePoster = nextBillingDatePoster;
         this.eventBus = eventBus;
+        this.internalCallContextFactory = internalCallContextFactory;
         this.invoiceDaoHelper = new InvoiceDaoHelper();
         this.cbaDao = new CBADao();
     }
@@ -180,7 +184,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 if (invoice == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_NUMBER_NOT_FOUND, number.longValue());
                 }
-                invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+
+                // The context may not contain the account record id at this point - we couldn't do it in the API above
+                // as we couldn't get access to the invoice object until now.
+                final InternalTenantContext contextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(invoice.getAccountId(), context);
+                invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, contextWithAccountRecordId);
+
                 return invoice;
             }
         });
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
index 54965fd..801eaf7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
@@ -19,6 +19,8 @@ package com.ning.billing.invoice.dao;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -40,11 +42,11 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap.Builder;
 
 public class InvoiceDaoHelper {
 
-
     /**
      * Find amounts to adjust for individual items, if not specified.
      * The user gives us a list of items to adjust associated with a given amount (how much to refund per invoice item).
@@ -57,7 +59,6 @@ public class InvoiceDaoHelper {
      * @param context                       the tenant callcontext
      * @return the final mapping between invoice item ids and amount to refund
      * @throws com.ning.billing.invoice.api.InvoiceApiException
-     *
      */
     public Map<UUID, BigDecimal> computeItemAdjustments(final String invoiceId,
                                                         final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory,
@@ -98,12 +99,10 @@ public class InvoiceDaoHelper {
         return invoiceItemIdsWithAmountsBuilder.build();
     }
 
-
     /**
-     * @param invoiceItem  item we are adjusting
-     * @param requestedPositiveAmountToAdjust
-     *                     amount we are adjusting for that item
-     * @param invoiceItems list of all invoice items on this invoice
+     * @param invoiceItem                     item we are adjusting
+     * @param requestedPositiveAmountToAdjust amount we are adjusting for that item
+     * @param invoiceItems                    list of all invoice items on this invoice
      * @return the amount we should really adjust based on whether or not the item got repaired
      */
     private BigDecimal computeItemAdjustmentAmount(final UUID invoiceItem, final BigDecimal requestedPositiveAmountToAdjust, final List<InvoiceItemModelDao> invoiceItems) {
@@ -155,13 +154,11 @@ public class InvoiceDaoHelper {
         return requestedPositiveAmount;
     }
 
-
     public List<InvoiceModelDao> getUnpaidInvoicesByAccountFromTransaction(final UUID accountId, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final LocalDate upToDate, final InternalTenantContext context) {
         final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
         return getUnpaidInvoicesByAccountFromTransaction(invoices, upToDate);
     }
 
-
     public List<InvoiceModelDao> getUnpaidInvoicesByAccountFromTransaction(final List<InvoiceModelDao> invoices, @Nullable final LocalDate upToDate) {
         final Collection<InvoiceModelDao> unpaidInvoices = Collections2.filter(invoices, new Predicate<InvoiceModelDao>() {
             @Override
@@ -224,11 +221,11 @@ public class InvoiceDaoHelper {
     }
 
     public void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
-        getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
-        getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+        getInvoiceItemsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
+        getInvoicePaymentsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
     }
 
-    public void populateChildren(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+    public void populateChildren(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
         getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
     }
@@ -244,37 +241,49 @@ public class InvoiceDaoHelper {
         return amount == null ? BigDecimal.ZERO : amount;
     }
 
-    private void getInvoiceItemsWithinTransaction(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
-        for (final InvoiceModelDao invoice : invoices) {
-            getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
-        }
-    }
-
-    private void getInvoiceItemsWithinTransaction(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
-        final String invoiceId = invoice.getId().toString();
+    private void getInvoiceItemsWithinTransaction(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+        final InvoiceItemSqlDao invoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+        final List<InvoiceItemModelDao> invoiceItemsForAccount = invoiceItemSqlDao.getByAccountRecordId(context);
 
-        final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
-        final List<InvoiceItemModelDao> items = transInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId, context);
-        invoice.addInvoiceItems(items);
-    }
+        final Map<UUID, List<InvoiceItemModelDao>> invoiceItemsPerInvoiceId = new HashMap<UUID, List<InvoiceItemModelDao>>();
+        for (final InvoiceItemModelDao item : invoiceItemsForAccount) {
+            if (invoiceItemsPerInvoiceId.get(item.getInvoiceId()) == null) {
+                invoiceItemsPerInvoiceId.put(item.getInvoiceId(), new LinkedList<InvoiceItemModelDao>());
+            }
+            invoiceItemsPerInvoiceId.get(item.getInvoiceId()).add(item);
+        }
 
-    private void getInvoicePaymentsWithinTransaction(final List<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         for (final InvoiceModelDao invoice : invoices) {
-            getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
+            // Make sure to set invoice items to a non-null value
+            final List<InvoiceItemModelDao> invoiceItemsForInvoice = Objects.firstNonNull(invoiceItemsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoiceItemModelDao>of());
+            invoice.addInvoiceItems(invoiceItemsForInvoice);
         }
     }
 
-    private void getInvoicePaymentsWithinTransaction(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+    private void getInvoicePaymentsWithinTransaction(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         final InvoicePaymentSqlDao invoicePaymentSqlDao = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
-        final String invoiceId = invoice.getId().toString();
-        final List<InvoicePaymentModelDao> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId, context);
-        for (final InvoicePaymentModelDao cur : invoicePayments) {
-            if (cur.getCurrency() != cur.getProcessedCurrency()) {
-                // If any entry is set with a different processed currency, we use it as a processed currency.
-                invoice.setProcessedCurrency(cur.getProcessedCurrency());
-                break;
+        final List<InvoicePaymentModelDao> invoicePaymentsForAccount = invoicePaymentSqlDao.getByAccountRecordId(context);
+
+        final Map<UUID, List<InvoicePaymentModelDao>> invoicePaymentsPerInvoiceId = new HashMap<UUID, List<InvoicePaymentModelDao>>();
+        for (final InvoicePaymentModelDao invoicePayment : invoicePaymentsForAccount) {
+            if (invoicePaymentsPerInvoiceId.get(invoicePayment.getInvoiceId()) == null) {
+                invoicePaymentsPerInvoiceId.put(invoicePayment.getInvoiceId(), new LinkedList<InvoicePaymentModelDao>());
+            }
+            invoicePaymentsPerInvoiceId.get(invoicePayment.getInvoiceId()).add(invoicePayment);
+        }
+
+        for (final InvoiceModelDao invoice : invoices) {
+            // Make sure to set payments to a non-null value
+            final List<InvoicePaymentModelDao> invoicePaymentsForInvoice = Objects.firstNonNull(invoicePaymentsPerInvoiceId.get(invoice.getId()), ImmutableList.<InvoicePaymentModelDao>of());
+            invoice.addPayments(invoicePaymentsForInvoice);
+
+            for (final InvoicePaymentModelDao invoicePayment : invoicePaymentsForInvoice) {
+                if (invoicePayment.getCurrency() != invoicePayment.getProcessedCurrency()) {
+                    // If any entry is set with a different processed currency, we use it as a processed currency.
+                    invoice.setProcessedCurrency(invoicePayment.getProcessedCurrency());
+                    break;
+                }
             }
         }
-        invoice.addPayments(invoicePayments);
     }
 }
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 00d3c1e..9dc4d55 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
@@ -30,6 +30,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
 import com.ning.billing.invoice.api.Invoice;
@@ -70,7 +71,8 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
         Assert.assertNotNull(migrationInvoiceId);
         //Double check it was created and values are correct
 
-        final InvoiceModelDao invoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
+        final InvoiceModelDao invoice = invoiceDao.getById(migrationInvoiceId, internalTenantContext);
         Assert.assertNotNull(invoice);
 
         Assert.assertEquals(invoice.getAccountId(), accountId);
@@ -111,8 +113,9 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
     // ACCOUNT balance should reflect total of migration and non-migration invoices
     @Test(groups = "slow")
     public void testBalance() throws InvoiceApiException {
-        final InvoiceModelDao migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalCallContext);
-        final InvoiceModelDao regularInvoice = invoiceDao.getById(regularInvoiceId, internalCallContext);
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
+        final InvoiceModelDao migrationInvoice = invoiceDao.getById(migrationInvoiceId, internalTenantContext);
+        final InvoiceModelDao regularInvoice = invoiceDao.getById(regularInvoiceId, internalTenantContext);
         final BigDecimal balanceOfAllInvoices = InvoiceModelDaoHelper.getBalance(migrationInvoice).add(InvoiceModelDaoHelper.getBalance(regularInvoice));
 
         final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
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 683f296..634eb14 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
@@ -346,12 +346,12 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
     public void testAddRemoveWrittenOffTag() throws InvoiceApiException, TagApiException {
         invoiceUserApi.tagInvoiceAsWrittenOff(invoiceId, callContext);
 
-        List<Tag> tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, callContext);
+        List<Tag> tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, false, callContext);
         assertEquals(tags.size(), 1);
         assertEquals(tags.get(0).getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
 
         invoiceUserApi.tagInvoiceAsNotWrittenOff(invoiceId, callContext);
-        tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, callContext);
+        tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, false, callContext);
         assertEquals(tags.size(), 0);
     }
 }
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 2ad6556..0488412 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
@@ -712,7 +712,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
             assertEquals(cbaAfterRefund.compareTo(expectedCba), 0);
         }
 
-
     }
 
     @Test(groups = "slow")
@@ -1412,14 +1411,14 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Verify scenario - no CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 10.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 10.00, 10.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 10.00, 10.00, context);
 
         // Delete the CBA on invoice 1
         invoiceDao.deleteCBA(accountId, invoice1.getId(), creditBalanceAdjInvoiceItem1.getId(), context);
 
         // Verify the result
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 0.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 0.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 0.00, context);
     }
 
     @Test(groups = "slow")
@@ -1470,16 +1469,16 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Verify scenario - half of the CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 5.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00);
-        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
+        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00, context);
 
         // Refund Payment before we can deleted CBA
         invoiceDao.createRefund(paymentId, new BigDecimal("10.0"), false, ImmutableMap.<UUID, BigDecimal>of(), UUID.randomUUID(), context);
 
         // Verify all three invoices were affected
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 0.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 5.00, 5.00);
-        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 5.00, 5.00, context);
+        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00, context);
     }
 
     @Test(groups = "slow")
@@ -1507,7 +1506,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         invoiceUtil.createInvoiceItem(repairAdjInvoiceItem, context);
         invoiceUtil.createInvoiceItem(creditBalanceAdjInvoiceItem1, context);
 
-
         final BigDecimal paymentAmount = new BigDecimal("10.00");
         final UUID paymentId = UUID.randomUUID();
 
@@ -1545,17 +1543,17 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Verify scenario - all CBA should have been used
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 0.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00);
-        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00);
-        invoiceUtil.verifyInvoice(invoice3.getId(), 0.00, -5.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
+        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00, context);
+        invoiceUtil.verifyInvoice(invoice3.getId(), 0.00, -5.00, context);
 
         invoiceDao.createRefund(paymentId, paymentAmount, false, ImmutableMap.<UUID, BigDecimal>of(), UUID.randomUUID(), context);
 
         // Verify all three invoices were affected
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 0.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 10.00, 10.00);
-        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00);
-        invoiceUtil.verifyInvoice(invoice3.getId(), 0.00, -5.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 10.00, 10.00, context);
+        invoiceUtil.verifyInvoice(invoice2.getId(), 0.00, -5.00, context);
+        invoiceUtil.verifyInvoice(invoice3.getId(), 0.00, -5.00, context);
     }
 
     @Test(groups = "slow")
@@ -1580,7 +1578,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Verify scenario
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 10.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
 
         // Delete the CBA on invoice 1
         try {
@@ -1592,6 +1590,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Verify the result
         Assert.assertEquals(invoiceDao.getAccountCBA(accountId, context).doubleValue(), 10.00);
-        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00);
+        invoiceUtil.verifyInvoice(invoice1.getId(), 0.00, 10.00, context);
     }
 }
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
index e64ddeb..17c2f2c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
@@ -82,7 +82,6 @@ import com.google.common.collect.ImmutableMap;
 
 public class TestInvoiceHelper {
 
-
     public static final Currency accountCurrency = Currency.USD;
 
     public static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
@@ -131,7 +130,6 @@ public class TestInvoiceHelper {
     public static final BigDecimal THREE_HUNDRED_AND_SIXTY_FIVE = new BigDecimal("365.0").setScale(NUMBER_OF_DECIMALS);
     public static final BigDecimal THREE_HUNDRED_AND_SIXTY_SIX = new BigDecimal("366.0").setScale(NUMBER_OF_DECIMALS);
 
-
     private final InvoiceGenerator generator;
     private final BillingInternalApi billingApi;
     private final AccountInternalApi accountApi;
@@ -149,7 +147,6 @@ public class TestInvoiceHelper {
     private final InvoicePaymentSqlDao invoicePaymentSqlDao;
     private final InvoiceItemSqlDao invoiceItemSqlDao;
 
-
     @Inject
     public TestInvoiceHelper(final InvoiceGenerator generator, final IDBI dbi,
                              final BillingInternalApi billingApi, final AccountInternalApi accountApi, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
@@ -283,8 +280,8 @@ public class TestInvoiceHelper {
         }
     }
 
-    public void verifyInvoice(final UUID invoiceId, final double balance, final double cbaAmount) throws InvoiceApiException {
-        final InvoiceModelDao invoice = invoiceDao.getById(invoiceId, internalCallContext);
+    public void verifyInvoice(final UUID invoiceId, final double balance, final double cbaAmount, final InternalTenantContext context) throws InvoiceApiException {
+        final InvoiceModelDao invoice = invoiceDao.getById(invoiceId, context);
         Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).doubleValue(), balance);
         Assert.assertEquals(InvoiceModelDaoHelper.getCBAAmount(invoice).doubleValue(), cbaAmount);
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
index e408af4..21507b8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
@@ -20,12 +20,9 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import com.ning.billing.account.api.Account;
 import com.ning.billing.entitlement.api.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
@@ -34,16 +31,11 @@ import com.ning.billing.invoice.api.InvoiceItemType;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimap;
 
 public class AccountTimelineJson {
@@ -95,35 +87,14 @@ public class AccountTimelineJson {
         return tmp.toString();
     }
 
-
     public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments,
-                               final List<SubscriptionBundle> bundlesTimeline, final Multimap<UUID, Refund> refundsByPayment,
-                               final Multimap<UUID, InvoicePayment> chargebacksByPayment, @Nullable final AuditLogsForInvoices invoicesAuditLogs,
-                               @Nullable final AuditLogsForPayments paymentsAuditLogs, @Nullable final AuditLogsForRefunds refundsAuditLogs,
-                               @Nullable final AuditLogsForInvoicePayments chargebacksAuditLogs, @Nullable final AuditLogsForBundles bundlesAuditLogs) {
-        this(account, invoices, payments, bundlesTimeline, refundsByPayment, chargebacksByPayment,
-             invoicesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : invoicesAuditLogs.getInvoiceAuditLogs(),
-             invoicesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : invoicesAuditLogs.getInvoiceItemsAuditLogs(),
-             paymentsAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : paymentsAuditLogs.getPaymentsAuditLogs(),
-             refundsAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : refundsAuditLogs.getRefundsAuditLogs(),
-             chargebacksAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : chargebacksAuditLogs.getInvoicePaymentsAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getBundlesAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getSubscriptionsAuditLogs(),
-             bundlesAuditLogs == null ? ImmutableMap.<UUID, List<AuditLog>>of() : bundlesAuditLogs.getSubscriptionEventsAuditLogs());
-    }
-
-    public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments, final List<SubscriptionBundle> bundles,
-                               final Multimap<UUID, Refund> refundsByPayment, final Multimap<UUID, InvoicePayment> chargebacksByPayment,
-                               final Map<UUID, List<AuditLog>> invoiceAuditLogs, final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs,
-                               final Map<UUID, List<AuditLog>> paymentsAuditLogs, final Map<UUID, List<AuditLog>> refundsAuditLogs,
-                               final Map<UUID, List<AuditLog>> chargebacksAuditLogs, final Map<UUID, List<AuditLog>> bundlesAuditLogs,
-                               final Map<UUID, List<AuditLog>> subscriptionsAuditLogs, final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
+                               final List<SubscriptionBundle> bundles, final Multimap<UUID, Refund> refundsByPayment,
+                               final Multimap<UUID, InvoicePayment> chargebacksByPayment, final AccountAuditLogs accountAuditLogs) {
         this.account = new AccountJson(account, null, null);
         this.bundles = new LinkedList<BundleJson>();
         for (final SubscriptionBundle bundle : bundles) {
-            final List<AuditLog> bundleAuditLogs = bundlesAuditLogs.get(bundle.getId());
-            final BundleJson jsonWithSubscriptions = new BundleJson(bundle, bundleAuditLogs,
-                                                                                                      subscriptionsAuditLogs, subscriptionEventsAuditLogs);
+            final List<AuditLog> bundleAuditLogs = accountAuditLogs.getAuditLogsForBundle(bundle.getId());
+            final BundleJson jsonWithSubscriptions = new BundleJson(bundle, accountAuditLogs);
             this.bundles.add(jsonWithSubscriptions);
         }
 
@@ -133,43 +104,41 @@ public class AccountTimelineJson {
         for (final Invoice invoice : invoices) {
             for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
                 if (InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType())) {
-                    final List<AuditLog> auditLogs = invoiceItemsAuditLogs.get(invoiceItem.getId());
+                    final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForInvoiceItem(invoiceItem.getId());
                     credits.add(new CreditJson(invoice, invoiceItem, auditLogs));
                 }
             }
         }
         // Create now the invoice json objects
         for (final Invoice invoice : invoices) {
-            final List<AuditLog> auditLogs = invoiceAuditLogs.get(invoice.getId());
+            final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForInvoice(invoice.getId());
             this.invoices.add(new InvoiceJson(invoice,
-                                                            getBundleExternalKey(invoice, bundles),
-                                                            credits,
-                                                            auditLogs));
+                                              getBundleExternalKey(invoice, bundles),
+                                              credits,
+                                              auditLogs));
         }
 
         this.payments = new LinkedList<PaymentJson>();
         for (final Payment payment : payments) {
             final List<RefundJson> refunds = new ArrayList<RefundJson>();
             for (final Refund refund : refundsByPayment.get(payment.getId())) {
-                final List<AuditLog> auditLogs = refundsAuditLogs.get(refund.getId());
+                final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForRefund(refund.getId());
                 // TODO add adjusted invoice items?
                 refunds.add(new RefundJson(refund, null, auditLogs));
             }
 
             final List<ChargebackJson> chargebacks = new ArrayList<ChargebackJson>();
             for (final InvoicePayment chargeback : chargebacksByPayment.get(payment.getId())) {
-                final List<AuditLog> auditLogs = chargebacksAuditLogs.get(chargeback.getId());
+                final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForChargeback(chargeback.getId());
                 chargebacks.add(new ChargebackJson(payment.getAccountId(), chargeback, auditLogs));
             }
 
-            final int nbOfPaymentAttempts = payment.getAttempts().size();
-            final String status = payment.getPaymentStatus().toString();
-            final List<AuditLog> auditLogs = paymentsAuditLogs.get(payment.getId());
+            final List<AuditLog> auditLogs = accountAuditLogs.getAuditLogsForPayment(payment.getId());
             this.payments.add(new PaymentJson(payment,
                                               getBundleExternalKey(payment.getInvoiceId(), invoices, bundles),
                                               refunds,
-                                                            chargebacks,
-                                                            auditLogs));
+                                              chargebacks,
+                                              auditLogs));
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
index da1a3e8..c3cae9c 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/BundleJson.java
@@ -18,15 +18,13 @@ package com.ning.billing.jaxrs.json;
 
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
 import com.ning.billing.entitlement.api.Subscription;
 import com.ning.billing.entitlement.api.SubscriptionBundle;
 import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -71,26 +69,21 @@ public class BundleJson extends JsonBase {
         return externalKey;
     }
 
-    public BundleJson(final SubscriptionBundle bundle, final List<AuditLog> auditLogs,
-                      final Map<UUID, List<AuditLog>> subscriptionsAuditLogs, final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
-        super(toAuditLogJson(auditLogs));
+    public BundleJson(final SubscriptionBundle bundle, @Nullable final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForBundle(bundle.getId())));
         this.accountId = bundle.getAccountId().toString();
         this.bundleId = bundle.getId().toString();
         this.externalKey = bundle.getExternalKey();
 
         this.subscriptions = new LinkedList<SubscriptionJson>();
         for (final Subscription cur : bundle.getSubscriptions()) {
-
             final ImmutableList<SubscriptionEvent> events = ImmutableList.<SubscriptionEvent>copyOf(Collections2.filter(bundle.getTimeline().getSubscriptionEvents(), new Predicate<SubscriptionEvent>() {
                 @Override
                 public boolean apply(@Nullable final SubscriptionEvent input) {
                     return input.getEntitlementId().equals(cur.getId());
                 }
             }));
-            this.subscriptions.add(new SubscriptionJson(cur,
-                                                        events,
-                                                        (subscriptionsAuditLogs != null ? subscriptionsAuditLogs.get(cur.getId()) : null),
-                                                        subscriptionEventsAuditLogs));
+            this.subscriptions.add(new SubscriptionJson(cur, events, accountAuditLogs));
         }
     }
 
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
index 3958225..b3ed9d8 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJson.java
@@ -19,8 +19,6 @@ package com.ning.billing.jaxrs.json;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -28,6 +26,7 @@ import org.joda.time.LocalDate;
 
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
@@ -80,9 +79,8 @@ public class InvoiceJson extends JsonBase {
         this.items = items;
     }
 
-
-    public InvoiceJson(final Invoice input, @Nullable final List<AuditLog> auditLogs) {
-        this(input, null, null, auditLogs);
+    public InvoiceJson(final Invoice input) {
+        this(input, false, null);
     }
 
     public InvoiceJson(final Invoice input, final String bundleKeys, final List<CreditJson> credits, final List<AuditLog> auditLogs) {
@@ -91,11 +89,13 @@ public class InvoiceJson extends JsonBase {
              input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, toAuditLogJson(auditLogs));
     }
 
-    public InvoiceJson(final Invoice input, @Nullable final List<AuditLog> invoiceAuditLogs, @Nullable final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs) {
-        super(toAuditLogJson(invoiceAuditLogs));
+    public InvoiceJson(final Invoice input, final boolean withItems, @Nullable final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoice(input.getId())));
         this.items = new ArrayList<InvoiceItemJson>(input.getInvoiceItems().size());
-        for (final InvoiceItem item : input.getInvoiceItems()) {
-            this.items.add(new InvoiceItemJson(item, invoiceItemsAuditLogs == null ? null : invoiceItemsAuditLogs.get(item.getId())));
+        if (withItems) {
+            for (final InvoiceItem item : input.getInvoiceItems()) {
+                this.items.add(new InvoiceItemJson(item, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
+            }
         }
         this.amount = input.getChargedAmount();
         this.currency = input.getCurrency().toString();
@@ -199,7 +199,7 @@ public class InvoiceJson extends JsonBase {
         if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
-        if (balance != null ? balance.compareTo(that.balance) != 0: that.balance != null) {
+        if (balance != null ? balance.compareTo(that.balance) != 0 : that.balance != null) {
             return false;
         }
         if (bundleKeys != null ? !bundleKeys.equals(that.bundleKeys) : that.bundleKeys != null) {
@@ -226,7 +226,7 @@ public class InvoiceJson extends JsonBase {
         if (items != null ? !items.equals(that.items) : that.items != null) {
             return false;
         }
-        if (refundAdj != null ? refundAdj.compareTo(that.refundAdj) != 0: that.refundAdj != null) {
+        if (refundAdj != null ? refundAdj.compareTo(that.refundAdj) != 0 : that.refundAdj != null) {
             return false;
         }
         if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
index ad23683..dfaca55 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/SubscriptionJson.java
@@ -18,8 +18,6 @@ package com.ning.billing.jaxrs.json;
 
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 import javax.annotation.Nullable;
 
@@ -31,7 +29,7 @@ import com.ning.billing.catalog.api.PriceList;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.entitlement.api.Subscription;
 import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -313,12 +311,10 @@ public class SubscriptionJson extends JsonBase {
         this.newEvents = newEvents;
     }
 
-
     public SubscriptionJson(final Subscription subscription,
                             final List<SubscriptionEvent> subscriptionEvents,
-                            final List<AuditLog> subscriptionAuditLogs,
-                            final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs) {
-        super(toAuditLogJson(subscriptionAuditLogs));
+                            @Nullable final AccountAuditLogs accountAuditLogs) {
+        super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscription(subscription.getId())));
         this.startDate = subscription.getEffectiveStartDate();
         this.productName = subscription.getLastActiveProduct().getName();
         this.productCategory = subscription.getLastActiveProductCategory().name();
@@ -334,20 +330,20 @@ public class SubscriptionJson extends JsonBase {
         this.externalKey = subscription.getExternalKey();
         this.events = subscriptionEvents != null ? new LinkedList<EventSubscriptionJson>() : null;
         if (events != null) {
-            for (SubscriptionEvent cur : subscriptionEvents) {
+            for (final SubscriptionEvent cur : subscriptionEvents) {
                 final BillingPeriod billingPeriod = cur.getNextBillingPeriod() != null ? cur.getNextBillingPeriod() : cur.getPrevBillingPeriod();
                 final Product product = cur.getNextProduct() != null ? cur.getNextProduct() : cur.getPrevProduct();
                 final PriceList priceList = cur.getNextPriceList() != null ? cur.getNextPriceList() : cur.getPrevPriceList();
                 final PlanPhase phase = cur.getNextPhase() != null ? cur.getNextPhase() : cur.getPrevPhase();
                 this.events.add(new EventSubscriptionJson(cur.getId().toString(),
-                                                              billingPeriod != null ? billingPeriod.toString() : null,
-                                                              cur.getRequestedDate(),
-                                                              cur.getEffectiveDate(),
-                                                              product != null ? product.getName() : null,
-                                                              priceList != null ? priceList.getName() : null,
-                                                              cur.getSubscriptionEventType().toString(),
-                                                              phase != null ? phase.getName() : null,
-                                                              (subscriptionEventsAuditLogs != null ? toAuditLogJson(subscriptionEventsAuditLogs.get(cur.getId())) : null)));
+                                                          billingPeriod != null ? billingPeriod.toString() : null,
+                                                          cur.getRequestedDate(),
+                                                          cur.getEffectiveDate(),
+                                                          product != null ? product.getName() : null,
+                                                          priceList != null ? priceList.getName() : null,
+                                                          cur.getSubscriptionEventType().toString(),
+                                                          phase != null ? phase.getName() : null,
+                                                          toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscriptionEvent(cur.getId()))));
             }
         }
         this.newEvents = null;
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index a210318..eb44ad3 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -90,11 +90,7 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
+import com.ning.billing.util.audit.AccountAuditLogs;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.entity.Pagination;
@@ -231,7 +227,7 @@ public class AccountResource extends JaxRsResourceBase {
         final Collection<BundleJson> result = Collections2.transform(bundles, new Function<SubscriptionBundle, BundleJson>() {
             @Override
             public BundleJson apply(final SubscriptionBundle input) {
-                return new BundleJson(input, null, null, null);
+                return new BundleJson(input, null);
             }
         });
         return Response.status(Status.OK).entity(result).build();
@@ -322,15 +318,12 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the invoices
         final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices, auditMode.getLevel(), tenantContext);
 
         // Get the payments
         final List<Payment> payments = paymentApi.getAccountPayments(accountId, tenantContext);
-        final AuditLogsForPayments paymentsAuditLogs = auditUserApi.getAuditLogsForPayments(payments, auditMode.getLevel(), tenantContext);
 
         // Get the refunds
         final List<Refund> refunds = paymentApi.getAccountRefunds(account, tenantContext);
-        final AuditLogsForRefunds refundsAuditLogs = auditUserApi.getAuditLogsForRefunds(refunds, auditMode.getLevel(), tenantContext);
         final Multimap<UUID, Refund> refundsByPayment = ArrayListMultimap.<UUID, Refund>create();
         for (final Refund refund : refunds) {
             refundsByPayment.put(refund.getPaymentId(), refund);
@@ -338,7 +331,6 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the chargebacks
         final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(accountId, tenantContext);
-        final AuditLogsForInvoicePayments chargebacksAuditLogs = auditUserApi.getAuditLogsForInvoicePayments(chargebacks, auditMode.getLevel(), tenantContext);
         final Multimap<UUID, InvoicePayment> chargebacksByPayment = ArrayListMultimap.<UUID, InvoicePayment>create();
         for (final InvoicePayment chargeback : chargebacks) {
             chargebacksByPayment.put(chargeback.getPaymentId(), chargeback);
@@ -346,12 +338,13 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the bundles
         final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
-        final AuditLogsForBundles bundlesAuditLogs = auditUserApi.getAuditLogsForBundles(bundles, auditMode.getLevel(), tenantContext);
+
+        // Get all audit logs
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
 
         final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundles,
                                                                  refundsByPayment, chargebacksByPayment,
-                                                                 invoicesAuditLogs, paymentsAuditLogs, refundsAuditLogs,
-                                                                 chargebacksAuditLogs, bundlesAuditLogs);
+                                                                 accountAuditLogs);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -392,7 +385,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-
     /*
      * ************************** INVOICE CBA REBALANCING ********************************
      */
@@ -421,7 +413,7 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICES)
     @Produces(APPLICATION_JSON)
-    public Response getInvoices(@PathParam("accountId") final String accountId,
+    public Response getInvoices(@PathParam("accountId") final String accountIdString,
                                 @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
                                 @QueryParam(QUERY_UNPAID_INVOICES_ONLY) @DefaultValue("false") final boolean unpaidInvoicesOnly,
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@@ -429,35 +421,22 @@ public class AccountResource extends JaxRsResourceBase {
         final TenantContext tenantContext = context.createContext(request);
 
         // Verify the account exists
-        accountUserApi.getAccountById(UUID.fromString(accountId), tenantContext);
-
+        final UUID accountId = UUID.fromString(accountIdString);
+        accountUserApi.getAccountById(accountId, tenantContext);
 
         final List<Invoice> invoices = unpaidInvoicesOnly ?
-                                       new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(UUID.fromString(accountId), null, tenantContext)) :
-                                       invoiceApi.getInvoicesByAccount(UUID.fromString(accountId), tenantContext);
-
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices, auditMode.getLevel(), tenantContext);
+                                       new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, tenantContext)) :
+                                       invoiceApi.getInvoicesByAccount(accountId, tenantContext);
 
-        if (withItems) {
-            final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
-            for (final Invoice invoice : invoices) {
-                result.add(new InvoiceJson(invoice,
-                                           invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                           invoicesAuditLogs.getInvoiceItemsAuditLogs()));
-            }
-
-            return Response.status(Status.OK).entity(result).build();
-        } else {
-            final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
-            for (final Invoice invoice : invoices) {
-                result.add(new InvoiceJson(invoice,
-                                           invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId())));
-            }
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
 
-            return Response.status(Status.OK).entity(result).build();
+        final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
+        for (final Invoice invoice : invoices) {
+            result.add(new InvoiceJson(invoice, withItems, accountAuditLogs));
         }
-    }
 
+        return Response.status(Status.OK).entity(result).build();
+    }
 
     /*
      * ************************** PAYMENTS ********************************
@@ -500,7 +479,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Consumes(APPLICATION_JSON)
@@ -580,7 +558,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-
     /*
      * ************************** CHARGEBACKS ********************************
      */
@@ -598,7 +575,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(chargebacksJson).build();
     }
 
-
     /*
      * ************************** REFUNDS ********************************
      */
@@ -622,7 +598,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-
     /*
      * ************************** OVERDUE ********************************
      */
@@ -687,10 +662,12 @@ public class AccountResource extends JaxRsResourceBase {
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
+                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, context.createContext(request));
+        final UUID accountId = UUID.fromString(accountIdString);
+        return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
     }
 
     @POST
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
index 91dcf0e..a29f632 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/BundleResource.java
@@ -58,6 +58,7 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 
 import com.google.inject.Inject;
 
@@ -91,10 +92,9 @@ public class BundleResource extends JaxRsResourceBase {
     @Produces(APPLICATION_JSON)
     public Response getBundle(@PathParam("bundleId") final String bundleId,
                               @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
-
         final UUID id = UUID.fromString(bundleId);
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(id, context.createContext(request));
-        final BundleJson json = new BundleJson(bundle, null, null, null);
+        final BundleJson json = new BundleJson(bundle, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -103,7 +103,7 @@ public class BundleResource extends JaxRsResourceBase {
     public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
         final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, context.createContext(request));
-        final BundleJson json = new BundleJson(bundle, null, null, null);
+        final BundleJson json = new BundleJson(bundle, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -131,11 +131,11 @@ public class BundleResource extends JaxRsResourceBase {
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response resumeBundle(@PathParam(ID_PARAM_NAME) final String id,
-                                @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
-                                @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                @HeaderParam(HDR_REASON) final String reason,
-                                @HeaderParam(HDR_COMMENT) final String comment,
-                                @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException {
+                                 @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                 @HeaderParam(HDR_REASON) final String reason,
+                                 @HeaderParam(HDR_COMMENT) final String comment,
+                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException {
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID bundleId = UUID.fromString(id);
@@ -185,10 +185,14 @@ public class BundleResource extends JaxRsResourceBase {
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String bundleIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, context.createContext(request));
+                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
+        final UUID bundleId = UUID.fromString(bundleIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, context.createContext(request));
+        return super.getTags(bundle.getAccountId(), bundleId, auditMode, includedDeleted, tenantContext);
     }
 
     @PUT
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 449a023..dbd5d85 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -18,8 +18,6 @@ package com.ning.billing.jaxrs.resources;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
@@ -38,7 +36,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -70,13 +67,12 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
+import com.ning.billing.util.audit.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 
 import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -109,7 +105,6 @@ public class InvoiceResource extends JaxRsResourceBase {
         this.invoiceNotifier = invoiceNotifier;
     }
 
-
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -119,19 +114,12 @@ public class InvoiceResource extends JaxRsResourceBase {
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId), tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(ImmutableList.<Invoice>of(invoice),
-                                                                                            auditMode.getLevel(),
-                                                                                            tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
         } else {
-            final InvoiceJson json = withItems ?
-                                           new InvoiceJson(invoice,
-                                                                    invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                                                    invoicesAuditLogs.getInvoiceItemsAuditLogs()) :
-                                           new InvoiceJson(invoice,
-                                                                 invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()));
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -145,19 +133,12 @@ public class InvoiceResource extends JaxRsResourceBase {
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoiceByNumber(invoiceNumber, tenantContext);
-        final AuditLogsForInvoices invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(ImmutableList.<Invoice>of(invoice),
-                                                                                            auditMode.getLevel(),
-                                                                                            tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceNumber);
         } else {
-            final InvoiceJson json = withItems ?
-                                           new InvoiceJson(invoice,
-                                                                    invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()),
-                                                                    invoicesAuditLogs.getInvoiceItemsAuditLogs()) :
-                                           new InvoiceJson(invoice,
-                                                                 invoicesAuditLogs.getInvoiceAuditLogs().get(invoice.getId()));
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -186,7 +167,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRun,
                                                                              callContext);
         if (dryRun) {
-            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, null)).build();
+            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice)).build();
         } else {
             return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", generatedInvoice.getId());
         }
@@ -250,7 +231,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
-    @Path("/" +CHARGES)
+    @Path("/" + CHARGES)
     public Response createExternalCharge(final InvoiceItemJson externalChargeJson,
                                          @QueryParam(QUERY_REQUESTED_DT) final String requestedDateTimeString,
                                          @QueryParam(QUERY_PAY_INVOICE) @DefaultValue("false") final Boolean payInvoice,
@@ -334,11 +315,18 @@ public class InvoiceResource extends JaxRsResourceBase {
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
         final List<Payment> payments = paymentApi.getInvoicePayments(UUID.fromString(invoiceId), tenantContext);
-        final AuditLogsForPayments auditLogsForPayments = auditUserApi.getAuditLogsForPayments(payments, auditMode.getLevel(), tenantContext);
-
         final List<PaymentJson> result = new ArrayList<PaymentJson>(payments.size());
+        if (payments.size() == 0) {
+            return Response.status(Status.OK).entity(result).build();
+        }
+
+        final AccountAuditLogsForObjectType auditLogsForPayments = auditUserApi.getAccountAuditLogs(payments.get(0).getAccountId(),
+                                                                                                    ObjectType.PAYMENT,
+                                                                                                    auditMode.getLevel(),
+                                                                                                    tenantContext);
+
         for (final Payment cur : payments) {
-            result.add(new PaymentJson(cur, auditLogsForPayments.getPaymentsAuditLogs().get(cur.getId())));
+            result.add(new PaymentJson(cur, auditLogsForPayments.getAuditLogs(cur.getId())));
         }
 
         return Response.status(Status.OK).entity(result).build();
@@ -430,16 +418,20 @@ public class InvoiceResource extends JaxRsResourceBase {
     }
 
     @GET
-    @Path("/{invoiceId:"  + UUID_PATTERN + "}/" + TAGS)
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String invoiceIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, context.createContext(request));
+                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, InvoiceApiException {
+        final UUID invoiceId = UUID.fromString(invoiceIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Invoice invoice = invoiceApi.getInvoice(invoiceId, tenantContext);
+        return super.getTags(invoice.getAccountId(), invoiceId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
-    @Path("/{invoiceId:"  + UUID_PATTERN + "}/" + TAGS)
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response createTags(@PathParam(ID_PARAM_NAME) final String id,
@@ -454,7 +446,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     }
 
     @DELETE
-    @Path("/{invoiceId:"  + UUID_PATTERN + "}/" + TAGS)
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index ca1ee21..f7046e9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -82,6 +82,7 @@ public interface JaxrsResource {
     public static final String QUERY_PAYMENT_WITH_REFUNDS_AND_CHARGEBACKS = "withRefundsAndChargebacks";
 
     public static final String QUERY_TAGS = "tagList";
+    public static final String QUERY_TAGS_INCLUDED_DELETED = "includedDeleted";
     public static final String QUERY_CUSTOM_FIELDS = "customFieldList";
 
     public static final String QUERY_PAYMENT_METHOD_PLUGIN_NAME = "pluginName";
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 150b97e..e583cd4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -17,8 +17,10 @@
 package com.ning.billing.jaxrs.resources;
 
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.ws.rs.core.Response;
@@ -48,6 +50,7 @@ import com.ning.billing.util.api.CustomFieldUserApi;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
@@ -98,14 +101,19 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return null;
     }
 
-    protected Response getTags(final UUID taggedObjectId, final AuditMode auditMode, final TenantContext context) throws TagDefinitionApiException {
-        final List<Tag> tags = tagUserApi.getTagsForObject(taggedObjectId, getObjectType(), context);
+    protected Response getTags(final UUID accountId, final UUID taggedObjectId, final AuditMode auditMode, final boolean includeDeleted, final TenantContext context) throws TagDefinitionApiException {
+        final List<Tag> tags = tagUserApi.getTagsForObject(taggedObjectId, getObjectType(), includeDeleted, context);
+        final AccountAuditLogsForObjectType tagsAuditLogs = auditUserApi.getAccountAuditLogs(accountId, ObjectType.TAG, auditMode.getLevel(), context);
 
+        final Map<UUID, TagDefinition> tagDefinitionsCache = new HashMap<UUID, TagDefinition>();
         final Collection<TagJson> result = new LinkedList<TagJson>();
         for (final Tag tag : tags) {
-            final TagDefinition tagDefinition = tagUserApi.getTagDefinition(tag.getTagDefinitionId(), context);
-            // TODO PIERRE - Bulk API
-            final List<AuditLog> auditLogs = auditUserApi.getAuditLogs(tag.getId(), ObjectType.TAG, auditMode.getLevel(), context);
+            if (tagDefinitionsCache.get(tag.getTagDefinitionId()) == null) {
+                tagDefinitionsCache.put(tag.getTagDefinitionId(), tagUserApi.getTagDefinition(tag.getTagDefinitionId(), context));
+            }
+            final TagDefinition tagDefinition = tagDefinitionsCache.get(tag.getTagDefinitionId());
+
+            final List<AuditLog> auditLogs = tagsAuditLogs.getAuditLogs(tag.getId());
             result.add(new TagJson(tagDefinition, auditLogs));
         }
 
@@ -189,7 +197,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return toLocalDate(account, inputDateTime, context);
     }
 
-
     protected LocalDate toLocalDate(final Account account, final String inputDate, final TenantContext context) {
 
         final LocalDate maybeResult = extractLocalDate(inputDate);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
index 0a65996..8f99a77 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentResource.java
@@ -121,8 +121,8 @@ public class PaymentResource extends JaxRsResourceBase {
 
             paymentJson = new PaymentJson(payment,
                                           null, // TODO - the keys are really only used for the timeline
-                                                              refunds,
-                                                              chargebacks);
+                                          refunds,
+                                          chargebacks);
         } else {
             paymentJson = new PaymentJson(payment, null);
         }
@@ -135,10 +135,10 @@ public class PaymentResource extends JaxRsResourceBase {
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response retryFailedPayment(@PathParam(ID_PARAM_NAME) final String paymentIdString,
-                                   @HeaderParam(HDR_CREATED_BY) final String createdBy,
-                                   @HeaderParam(HDR_REASON) final String reason,
-                                   @HeaderParam(HDR_COMMENT) final String comment,
-                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
+                                       @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                       @HeaderParam(HDR_REASON) final String reason,
+                                       @HeaderParam(HDR_COMMENT) final String comment,
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
@@ -150,13 +150,11 @@ public class PaymentResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(new PaymentJson(newPayment, null)).build();
     }
 
-
-
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
     @Produces(APPLICATION_JSON)
     public Response getChargebacksForPayment(@PathParam("paymentId") final String paymentId,
-                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
+                                             @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
 
         final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByPaymentId(UUID.fromString(paymentId), tenantContext);
@@ -173,7 +171,6 @@ public class PaymentResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(chargebacksJson).build();
     }
 
-
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Produces(APPLICATION_JSON)
@@ -229,7 +226,7 @@ public class PaymentResource extends JaxRsResourceBase {
     }
 
     @GET
-    @Path("/{paymentId:"  + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
     public Response getCustomFields(@PathParam(ID_PARAM_NAME) final String id,
                                     @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@@ -238,7 +235,7 @@ public class PaymentResource extends JaxRsResourceBase {
     }
 
     @POST
-    @Path("/{paymentId:" + UUID_PATTERN  + "}/" + CUSTOM_FIELDS)
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response createCustomFields(@PathParam(ID_PARAM_NAME) final String id,
@@ -252,7 +249,7 @@ public class PaymentResource extends JaxRsResourceBase {
     }
 
     @DELETE
-    @Path("/{paymentId:" + UUID_PATTERN  + "}/" + CUSTOM_FIELDS)
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response deleteCustomFields(@PathParam(ID_PARAM_NAME) final String id,
@@ -268,10 +265,14 @@ public class PaymentResource extends JaxRsResourceBase {
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String paymentIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, context.createContext(request));
+                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
+        final UUID paymentId = UUID.fromString(paymentIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Payment payment = paymentApi.getPayment(paymentId, false, tenantContext);
+        return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
@@ -290,7 +291,7 @@ public class PaymentResource extends JaxRsResourceBase {
     }
 
     @DELETE
-    @Path("/{paymentId:" + UUID_PATTERN + "}/"+ TAGS)
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     public Response deleteTags(@PathParam(ID_PARAM_NAME) final String id,
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
index f464200..eeb33e6 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/SubscriptionResource.java
@@ -74,6 +74,7 @@ import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.userrequest.CompletionUserRequestBase;
 
 import com.google.inject.Inject;
@@ -114,7 +115,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException {
         final UUID uuid = UUID.fromString(subscriptionId);
         final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(uuid, context.createContext(request));
-        final SubscriptionJson json = new SubscriptionJson(subscription, null, null, null);
+        final SubscriptionJson json = new SubscriptionJson(subscription, null, null);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -429,10 +430,14 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
-    public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
+    public Response getTags(@PathParam(ID_PARAM_NAME) final String subscriptionIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
-        return super.getTags(UUID.fromString(id), auditMode, context.createContext(request));
+                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
+        final UUID subscriptionId = UUID.fromString(subscriptionIdString);
+        final TenantContext tenantContext = context.createContext(request);
+        final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(subscriptionId, tenantContext);
+        return super.getTags(subscription.getAccountId(), subscriptionId, auditMode, includedDeleted, tenantContext);
     }
 
     @POST
diff --git a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
index 30a7eea..d123f56 100644
--- a/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
+++ b/jaxrs/src/test/java/com/ning/billing/jaxrs/json/TestAuditLogJson.java
@@ -22,10 +22,12 @@ import org.joda.time.DateTime;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ObjectType;
 import com.ning.billing.jaxrs.JaxrsTestSuiteNoDB;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.audit.DefaultAuditLog;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.dao.EntityAudit;
 import com.ning.billing.util.dao.TableName;
 
@@ -67,7 +69,7 @@ public class TestAuditLogJson extends JaxrsTestSuiteNoDB {
         final ChangeType changeType = ChangeType.DELETE;
         final EntityAudit entityAudit = new EntityAudit(tableName, recordId, changeType, null);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLog auditLog = new DefaultAuditLog(new AuditLogModelDao(entityAudit, callContext), ObjectType.ACCOUNT_EMAIL, UUID.randomUUID());
 
         final AuditLogJson auditLogJson = new AuditLogJson(auditLog);
         Assert.assertEquals(auditLogJson.getChangeType(), changeType.toString());

NEWS 2(+2 -0)

diff --git a/NEWS b/NEWS
index 06a5585..31f07c8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 0.8.7
     DDL: remove unused paid_through_date column
+    Add API to retrieve deleted tags
+    Update killbill-oss-parent to 0.5.9
 
 0.8.6
     Partial fix for https://github.com/killbill/killbill/issues/141
diff --git a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
index 68195ff..6377b2d 100644
--- a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
+++ b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
@@ -40,7 +40,7 @@ import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.entity.Pagination;
 
-public  class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi {
+public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi {
 
     private volatile ServiceRegistration<PaymentPluginApi> paymentInfoPluginRegistration;
 
@@ -90,6 +90,16 @@ public  class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi
     }
 
     @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return callWithRuntimeAndChecking(new PluginCallback(VALIDATION_PLUGIN_TYPE.PAYMENT) {
+            @Override
+            public Pagination<PaymentInfoPlugin> doCall(final Ruby runtime) throws PaymentPluginApiException {
+                return ((PaymentPluginApi) pluginInstance).searchPayments(searchKey, offset, limit, tenantContext);
+            }
+        });
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
 
         return callWithRuntimeAndChecking(new PluginCallback(VALIDATION_PLUGIN_TYPE.PAYMENT) {
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index 718ca6b..3396d2c 100644
--- a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -52,6 +52,11 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
         testDao.insertProcessedPayment(kbPaymentId, kbPaymentMethodId, amount);
         return new PaymentInfoPlugin() {
             @Override
+            public UUID getKbPaymentId() {
+                return kbPaymentId;
+            }
+
+            @Override
             public BigDecimal getAmount() {
                 return amount;
             }
@@ -104,6 +109,36 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
     }
 
     @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return new Pagination<PaymentInfoPlugin>() {
+            @Override
+            public Long getCurrentOffset() {
+                return 0L;
+            }
+
+            @Override
+            public Long getNextOffset() {
+                return null;
+            }
+
+            @Override
+            public Long getMaxNbRecords() {
+                return 0L;
+            }
+
+            @Override
+            public Long getTotalNbRecords() {
+                return 0L;
+            }
+
+            @Override
+            public Iterator<PaymentInfoPlugin> iterator() {
+                return null;
+            }
+        };
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
         return null;
     }
@@ -165,7 +200,6 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
         };
     }
 
-
     @Override
     public void resetPaymentMethods(final UUID kbAccountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
     }
diff --git a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index f5c0871..854aa07 100644
--- a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -53,6 +53,11 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     public PaymentInfoPlugin processPayment(final UUID accountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
         return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
             @Override
+            public UUID getKbPaymentId() {
+                return kbPaymentId;
+            }
+
+            @Override
             public BigDecimal getAmount() {
                 return amount;
             }
@@ -105,6 +110,11 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
         final BigDecimal someAmount = new BigDecimal("12.45");
         return withRuntimeCheckForExceptions(new PaymentInfoPlugin() {
             @Override
+            public UUID getKbPaymentId() {
+                return kbPaymentId;
+            }
+
+            @Override
             public BigDecimal getAmount() {
                 return someAmount;
             }
@@ -152,6 +162,36 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     }
 
     @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return new Pagination<PaymentInfoPlugin>() {
+            @Override
+            public Long getCurrentOffset() {
+                return 0L;
+            }
+
+            @Override
+            public Long getNextOffset() {
+                return null;
+            }
+
+            @Override
+            public Long getMaxNbRecords() {
+                return 0L;
+            }
+
+            @Override
+            public Long getTotalNbRecords() {
+                return 0L;
+            }
+
+            @Override
+            public Iterator<PaymentInfoPlugin> iterator() {
+                return null;
+            }
+        };
+    }
+
+    @Override
     public RefundInfoPlugin processRefund(final UUID accountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
 
         final BigDecimal someAmount = new BigDecimal("12.45");
@@ -259,7 +299,6 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
     public void resetPaymentMethods(final UUID accountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
     }
 
-
     private <T> T withRuntimeCheckForExceptions(final T result) throws PaymentPluginApiException {
         if (paymentPluginApiExceptionOnNextCalls != null) {
             throw paymentPluginApiExceptionOnNextCalls;
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index 8352981..0ee1c3b 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -90,6 +90,16 @@ public class DefaultPaymentApi implements PaymentApi {
     }
 
     @Override
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
+        return paymentProcessor.searchPayments(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final TenantContext context) throws PaymentApiException {
+        return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, internalCallContextFactory.createInternalTenantContext(context));
+    }
+
+    @Override
     public List<Payment> getInvoicePayments(final UUID invoiceId, final TenantContext context) {
         return paymentProcessor.getInvoicePayments(invoiceId, internalCallContextFactory.createInternalTenantContext(context));
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index f6bf2ae..19f8e02 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -86,10 +86,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
         super(pluginRegistry, accountInternalApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
     }
 
-    public Set<String> getAvailablePlugins() {
-        return pluginRegistry.getAllServices();
-    }
-
     public UUID addPaymentMethod(final String paymentPluginServiceName, final Account account,
                                  final boolean setDefault, final PaymentMethodPlugin paymentMethodProps, final InternalCallContext context)
             throws PaymentApiException {
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 6ee14fa..addd642 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
@@ -16,8 +16,12 @@
 
 package com.ning.billing.payment.core;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
 import com.google.inject.name.Named;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
@@ -32,12 +36,16 @@ import com.ning.billing.osgi.api.OSGIServiceRegistration;
 import com.ning.billing.payment.api.DefaultPayment;
 import com.ning.billing.payment.api.DefaultPaymentErrorEvent;
 import com.ning.billing.payment.api.DefaultPaymentInfoEvent;
+import com.ning.billing.payment.api.DefaultPaymentMethod;
 import com.ning.billing.payment.api.DefaultPaymentPluginErrorEvent;
 import com.ning.billing.payment.api.Payment;
 import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.payment.api.PaymentMethod;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
 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.PaymentMethodModelDao;
 import com.ning.billing.payment.dao.PaymentModelDao;
 import com.ning.billing.payment.dao.RefundModelDao;
 import com.ning.billing.payment.dispatcher.PluginDispatcher;
@@ -56,6 +64,9 @@ import com.ning.billing.events.PaymentErrorInternalEvent;
 import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.invoice.api.InvoiceInternalApi;
 import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -138,6 +149,73 @@ public class PaymentProcessor extends ProcessorBase {
         return fromPaymentModelDao(model, pluginInfo, context);
     }
 
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final InternalTenantContext internalTenantContext) {
+        // Note that we cannot easily do streaming here, since we would have to rely on the statistics
+        // returned by the Pagination objects from the plugins and we probably don't want to do that (if
+        // one plugin gets it wrong, it may starve the others).
+        final List<Payment> allResults = new LinkedList<Payment>();
+        Long totalNbRecords = 0L;
+        Long maxNbRecords = 0L;
+
+        // Search in all plugins (we treat the full set of results as a union with respect to offset/limit)
+        boolean firstSearch = true;
+        for (final String pluginName : getAvailablePlugins()) {
+            try {
+                final Pagination<Payment> payments;
+                if (allResults.size() >= limit) {
+                    // We have enough results, we just keep going (limit 1) to get the stats
+                    payments = searchPayments(searchKey, firstSearch ? offset : 0L, 1L, pluginName, internalTenantContext);
+                    // Required to close database connections
+                    ImmutableList.<Payment>copyOf(payments);
+                } else {
+                    payments = searchPayments(searchKey, firstSearch ? offset : 0L, limit - allResults.size(), pluginName, internalTenantContext);
+                    allResults.addAll(ImmutableList.<Payment>copyOf(payments));
+                }
+                firstSearch = false;
+                totalNbRecords += payments.getTotalNbRecords();
+                maxNbRecords += payments.getMaxNbRecords();
+            } catch (final PaymentApiException e) {
+                log.warn("Error while searching plugin " + pluginName, e);
+                // Non-fatal, continue to search other plugins
+            }
+        }
+
+        return new DefaultPagination<Payment>(offset, limit, totalNbRecords, maxNbRecords, allResults.iterator());
+    }
+
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
+        final Pagination<PaymentInfoPlugin> payments;
+        try {
+            payments = pluginApi.searchPayments(searchKey, offset, limit, buildTenantContext(internalTenantContext));
+        } catch (final PaymentPluginApiException e) {
+            throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENTS, pluginName, searchKey);
+        }
+
+        return new DefaultPagination<Payment>(payments,
+                                              limit,
+                                              Iterators.<Payment>filter(Iterators.<PaymentInfoPlugin, Payment>transform(payments.iterator(),
+                                                                                                                        new Function<PaymentInfoPlugin, Payment>() {
+                                                                                                                            @Override
+                                                                                                                            public Payment apply(final PaymentInfoPlugin paymentInfoPlugin) {
+                                                                                                                                if (paymentInfoPlugin.getKbPaymentId() == null) {
+                                                                                                                                    // Garbage from the plugin?
+                                                                                                                                    log.debug("Plugin {} returned a payment without a kbPaymentId for searchKey {}", pluginName, searchKey);
+                                                                                                                                    return null;
+                                                                                                                                }
+
+                                                                                                                                final PaymentModelDao model = paymentDao.getPayment(paymentInfoPlugin.getKbPaymentId(), internalTenantContext);
+                                                                                                                                if (model == null) {
+                                                                                                                                    log.warn("Unable to find payment id " + paymentInfoPlugin.getKbPaymentId() + " present in plugin " + pluginName);
+                                                                                                                                    return null;
+                                                                                                                                }
+
+                                                                                                                                return fromPaymentModelDao(model, paymentInfoPlugin, internalTenantContext);
+                                                                                                                            }
+                                                                                                                        }),
+                                                                        Predicates.<Payment>notNull()));
+    }
+
     public List<Payment> getInvoicePayments(final UUID invoiceId, final InternalTenantContext context) {
         return getPayments(paymentDao.getPaymentsForInvoice(invoiceId, context), context);
     }
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index b582bc8..a68b447 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -17,6 +17,7 @@
 package com.ning.billing.payment.core;
 
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
@@ -113,6 +114,10 @@ public abstract class ProcessorBase {
         }
     }
 
+    public Set<String> getAvailablePlugins() {
+        return pluginRegistry.getAllServices();
+    }
+
     protected PaymentPluginApi getPaymentPluginApi(final String pluginName) throws PaymentApiException {
         final PaymentPluginApi pluginApi = pluginRegistry.getServiceForName(pluginName);
         if (pluginApi == null) {
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
index e7590c8..d23374f 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentInfoPlugin.java
@@ -17,6 +17,7 @@
 package com.ning.billing.payment.provider;
 
 import java.math.BigDecimal;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 
@@ -26,6 +27,7 @@ import com.ning.billing.payment.plugin.api.PaymentPluginStatus;
 
 public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
 
+    private final UUID kbPaymentId;
     private final BigDecimal amount;
     private final DateTime effectiveDate;
     private final DateTime createdDate;
@@ -33,8 +35,9 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     private final String error;
     private final Currency currency;
 
-    public DefaultNoOpPaymentInfoPlugin(final BigDecimal amount, final Currency currency, final DateTime effectiveDate,
+    public DefaultNoOpPaymentInfoPlugin(final UUID kbPaymentId, final BigDecimal amount, final Currency currency, final DateTime effectiveDate,
                                         final DateTime createdDate, final PaymentPluginStatus status, final String error) {
+        this.kbPaymentId = kbPaymentId;
         this.amount = amount;
         this.effectiveDate = effectiveDate;
         this.createdDate = createdDate;
@@ -44,6 +47,11 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
     }
 
     @Override
+    public UUID getKbPaymentId() {
+        return kbPaymentId;
+    }
+
+    @Override
     public BigDecimal getAmount() {
         return amount;
     }
@@ -90,13 +98,14 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultNoOpPaymentInfoPlugin");
-        sb.append("{amount=").append(amount);
+        final StringBuilder sb = new StringBuilder("DefaultNoOpPaymentInfoPlugin{");
+        sb.append("kbPaymentId=").append(kbPaymentId);
+        sb.append(", amount=").append(amount);
         sb.append(", effectiveDate=").append(effectiveDate);
         sb.append(", createdDate=").append(createdDate);
         sb.append(", status=").append(status);
         sb.append(", error='").append(error).append('\'');
+        sb.append(", currency=").append(currency);
         sb.append('}');
         return sb.toString();
     }
@@ -112,18 +121,24 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
 
         final DefaultNoOpPaymentInfoPlugin that = (DefaultNoOpPaymentInfoPlugin) o;
 
-        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
-        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+        if (createdDate != null ? createdDate.compareTo(that.createdDate) != 0 : that.createdDate != null) {
             return false;
         }
-        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+        if (currency != that.currency) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
             return false;
         }
         if (error != null ? !error.equals(that.error) : that.error != null) {
             return false;
         }
+        if (kbPaymentId != null ? !kbPaymentId.equals(that.kbPaymentId) : that.kbPaymentId != null) {
+            return false;
+        }
         if (status != that.status) {
             return false;
         }
@@ -133,11 +148,13 @@ public class DefaultNoOpPaymentInfoPlugin implements PaymentInfoPlugin {
 
     @Override
     public int hashCode() {
-        int result = amount != null ? amount.hashCode() : 0;
+        int result = kbPaymentId != null ? kbPaymentId.hashCode() : 0;
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
         result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (error != null ? error.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
         return result;
     }
 }
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index 9de86b6..066000e 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -96,7 +96,7 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
         payments.put(kbPaymentId.toString(), result);
         return result;
     }
@@ -111,6 +111,29 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
     }
 
     @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        final ImmutableList<PaymentInfoPlugin> allResults = ImmutableList.<PaymentInfoPlugin>copyOf(Iterables.<PaymentInfoPlugin>filter(Iterables.<PaymentInfoPlugin>concat(payments.values()), new Predicate<PaymentInfoPlugin>() {
+            @Override
+            public boolean apply(final PaymentInfoPlugin input) {
+                return (input.getKbPaymentId() != null && input.getKbPaymentId().toString().equals(searchKey)) ||
+                       (input.getFirstPaymentReferenceId() != null && input.getFirstPaymentReferenceId().contains(searchKey)) ||
+                       (input.getSecondPaymentReferenceId() != null && input.getSecondPaymentReferenceId().contains(searchKey));
+            }
+        }));
+
+        final List<PaymentInfoPlugin> results;
+        if (offset >= allResults.size()) {
+            results = ImmutableList.<PaymentInfoPlugin>of();
+        } else if (offset + limit > allResults.size()) {
+            results = allResults.subList(offset.intValue(), allResults.size());
+        } else {
+            results = allResults.subList(offset.intValue(), offset.intValue() + limit.intValue());
+        }
+
+        return new DefaultPagination<PaymentInfoPlugin>(offset, limit, (long) results.size(), (long) payments.values().size(), results.iterator());
+    }
+
+    @Override
     public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
         final PaymentMethodPlugin realWithID = new DefaultNoOpPaymentMethodPlugin(kbPaymentMethodId, paymentMethodProps);
         List<PaymentMethodPlugin> pms = paymentMethods.get(kbPaymentMethodId.toString());
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 8658da9..ba12401 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -60,12 +60,17 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
 
     @Override
     public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
     }
 
     @Override
     public PaymentInfoPlugin getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+    }
+
+    @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        return new DefaultPagination<PaymentInfoPlugin>(offset, limit, 0L, 0L, Iterators.<PaymentInfoPlugin>emptyIterator());
     }
 
     @Override
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index f4e4ec9..39d330b 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -102,7 +102,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         }
 
         final PaymentPluginStatus status = (makeAllInvoicesFailWithError.get() || makeNextInvoiceFailWithError.getAndSet(false)) ? PaymentPluginStatus.ERROR : PaymentPluginStatus.PROCESSED;
-        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
+        final PaymentInfoPlugin result = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, currency, clock.getUTCNow(), clock.getUTCNow(), status, null);
         payments.put(kbPaymentId.toString(), result);
         return result;
     }
@@ -116,6 +116,18 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
         return payment;
     }
 
+    @Override
+    public Pagination<PaymentInfoPlugin> searchPayments(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+        final ImmutableList<PaymentInfoPlugin> results = ImmutableList.<PaymentInfoPlugin>copyOf(Iterables.<PaymentInfoPlugin>filter(payments.values(), new Predicate<PaymentInfoPlugin>() {
+            @Override
+            public boolean apply(final PaymentInfoPlugin input) {
+                return (input.getKbPaymentId() != null && input.getKbPaymentId().equals(searchKey)) ||
+                       (input.getFirstPaymentReferenceId() != null && input.getFirstPaymentReferenceId().contains(searchKey)) ||
+                       (input.getSecondPaymentReferenceId() != null && input.getSecondPaymentReferenceId().contains(searchKey));
+            }
+        }));
+        return DefaultPagination.<PaymentInfoPlugin>build(offset, limit, results);
+    }
 
     @Override
     public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
index f74c0e0..c056958 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/TestDefaultNoOpPaymentInfoPlugin.java
@@ -31,21 +31,22 @@ public class TestDefaultNoOpPaymentInfoPlugin extends PaymentTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testEquals() throws Exception {
+        final UUID kbPaymentId = UUID.randomUUID();
         final BigDecimal amount = new BigDecimal("1.394810E-3");
         final DateTime effectiveDate = clock.getUTCNow().plusDays(1);
         final DateTime createdDate = clock.getUTCNow();
         final PaymentPluginStatus status = PaymentPluginStatus.UNDEFINED;
         final String error = UUID.randomUUID().toString();
 
-        final DefaultNoOpPaymentInfoPlugin info = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin info = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, Currency.USD, effectiveDate, createdDate,
                                                                                    status, error);
         Assert.assertEquals(info, info);
 
-        final DefaultNoOpPaymentInfoPlugin sameInfo = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin sameInfo = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, Currency.USD, effectiveDate, createdDate,
                                                                                        status, error);
         Assert.assertEquals(sameInfo, info);
 
-        final DefaultNoOpPaymentInfoPlugin otherInfo = new DefaultNoOpPaymentInfoPlugin(amount, Currency.USD, effectiveDate, createdDate,
+        final DefaultNoOpPaymentInfoPlugin otherInfo = new DefaultNoOpPaymentInfoPlugin(kbPaymentId, amount, Currency.USD, effectiveDate, createdDate,
                                                                                         status, UUID.randomUUID().toString());
         Assert.assertNotEquals(otherInfo, info);
     }

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index d4cb24f..d61cf7e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.5.8</version>
+        <version>0.5.9</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.8.7-SNAPSHOT</version>
diff --git a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
index 81d747f..cb39d3d 100644
--- a/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
+++ b/server/src/main/java/com/ning/billing/server/modules/DBIProvider.java
@@ -27,10 +27,12 @@ import org.skife.jdbi.v2.tweak.TransactionHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.util.dao.AuditLogModelDaoMapper;
 import com.ning.billing.util.dao.DateTimeArgumentFactory;
 import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
 import com.ning.billing.util.dao.EnumArgumentFactory;
 import com.ning.billing.util.dao.LocalDateArgumentFactory;
+import com.ning.billing.util.dao.RecordIdIdMappingsMapper;
 import com.ning.billing.util.dao.UUIDArgumentFactory;
 import com.ning.billing.util.dao.UuidMapper;
 import com.ning.jetty.jdbi.config.DaoConfig;
@@ -71,6 +73,8 @@ public class DBIProvider implements Provider<DBI> {
         dbi.registerArgumentFactory(new LocalDateArgumentFactory());
         dbi.registerArgumentFactory(new EnumArgumentFactory());
         dbi.registerMapper(new UuidMapper());
+        dbi.registerMapper(new AuditLogModelDaoMapper());
+        dbi.registerMapper(new RecordIdIdMappingsMapper());
 
         if (sqlLog != null) {
             dbi.setSQLLog(sqlLog);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index 5126a79..5782bd8 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -432,14 +432,22 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         return getInvoiceWithAudits(invoiceId, AuditLevel.NONE);
     }
 
+    protected InvoiceJson getInvoice(final String invoiceId, final Boolean withItems) throws IOException {
+        return doGetInvoice(invoiceId, withItems, InvoiceJson.class, AuditLevel.NONE);
+    }
+
     protected InvoiceJson getInvoiceWithAudits(final String invoiceId, final AuditLevel auditLevel) throws IOException {
-        return doGetInvoice(invoiceId, Boolean.FALSE, InvoiceJson.class, auditLevel);
+        return doGetInvoice(invoiceId, Boolean.TRUE, InvoiceJson.class, auditLevel);
     }
 
     protected InvoiceJson getInvoice(final Integer invoiceNumber) throws IOException {
         return getInvoice(invoiceNumber.toString());
     }
 
+    protected InvoiceJson getInvoice(final Integer invoiceNumber, final Boolean withItems) throws IOException {
+        return doGetInvoice(invoiceNumber.toString(), withItems, InvoiceJson.class, AuditLevel.NONE);
+    }
+
     protected InvoiceJson getInvoiceWithItems(final String invoiceId) throws IOException {
         return getInvoiceWithItemsWithAudits(invoiceId, AuditLevel.NONE);
     }
@@ -470,7 +478,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     }
 
     protected List<InvoiceJson> getInvoicesForAccountWithAudits(final String accountId, final AuditLevel auditLevel) throws IOException {
-        return doGetInvoicesForAccount(accountId, Boolean.FALSE, new TypeReference<List<InvoiceJson>>() {}, auditLevel);
+        return doGetInvoicesForAccount(accountId, Boolean.TRUE, new TypeReference<List<InvoiceJson>>() {}, auditLevel);
     }
 
     protected List<InvoiceJson> getInvoicesWithItemsForAccount(final String accountId) throws IOException {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
index 34a661e..c58fc6f 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -37,6 +37,7 @@ import com.ning.billing.util.api.AuditLevel;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
 public class TestInvoice extends TestJaxrsBase {
 
@@ -60,8 +61,15 @@ public class TestInvoice extends TestJaxrsBase {
             Assert.assertNull(auditLogJson.getComments());
         }
 
-        // Check we can retrieve an individual invoice
         final InvoiceJson invoiceJson = invoices.get(0);
+
+        // Check get with & without items
+        assertTrue(getInvoice(invoiceJson.getInvoiceId(), Boolean.FALSE).getItems().isEmpty());
+        assertTrue(getInvoice(invoiceJson.getInvoiceNumber(), Boolean.FALSE).getItems().isEmpty());
+        assertEquals(getInvoice(invoiceJson.getInvoiceId(), Boolean.TRUE).getItems().size(), invoiceJson.getItems().size());
+        assertEquals(getInvoice(invoiceJson.getInvoiceNumber(), Boolean.TRUE).getItems().size(), invoiceJson.getItems().size());
+
+        // Check we can retrieve an individual invoice
         final InvoiceJson firstInvoiceJson = getInvoice(invoiceJson.getInvoiceId());
         assertEquals(firstInvoiceJson, invoiceJson);
 
@@ -173,7 +181,7 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(paymentsFromJson.size(), 2);
         PaymentJson secondPayment = null;
         for (PaymentJson cur : paymentsFromJson) {
-            if (! cur.getPaymentId().equals(initialPaymentId)) {
+            if (!cur.getPaymentId().equals(initialPaymentId)) {
                 secondPayment = cur;
                 break;
             }
@@ -291,7 +299,6 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
     }
 
-
     @Test(groups = "slow")
     public void testExternalChargeOnNewInvoiceWithAutomaticPayment() throws Exception {
         final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
@@ -345,7 +352,7 @@ public class TestInvoice extends TestJaxrsBase {
         // Post an external charge
         final BigDecimal chargeAmount = BigDecimal.TEN;
         final InvoiceJson invoiceWithItems = createExternalChargeForInvoice(accountJson.getAccountId(), invoiceId,
-                                                                                     null, chargeAmount, null, null, false);
+                                                                            null, chargeAmount, null, null, false);
         assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
         assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
 
@@ -379,7 +386,6 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(adjustedInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
     }
 
-
     @Test(groups = "slow")
     public void testExternalChargeForBundleOnExistingInvoice() throws Exception {
         final AccountJson accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
@@ -396,7 +402,7 @@ public class TestInvoice extends TestJaxrsBase {
         final BigDecimal chargeAmount = BigDecimal.TEN;
         final String bundleId = UUID.randomUUID().toString();
         final InvoiceJson invoiceWithItems = createExternalChargeForInvoice(accountJson.getAccountId(), invoiceId,
-                                                                                     bundleId, chargeAmount, null, null, false);
+                                                                            bundleId, chargeAmount, null, null, false);
         assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
         assertEquals(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId(), bundleId);
 
diff --git a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
index 6f629b8..fd57844 100644
--- a/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/audit/api/DefaultAuditUserApi.java
@@ -16,44 +16,24 @@
 
 package com.ning.billing.util.audit.api;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.entitlement.api.Subscription;
-import com.ning.billing.entitlement.api.SubscriptionBundle;
-import com.ning.billing.entitlement.api.SubscriptionEvent;
-import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.AuditUserApi;
+import com.ning.billing.util.audit.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForAccount;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
-import com.ning.billing.util.audit.DefaultAuditLogsForBundles;
-import com.ning.billing.util.audit.DefaultAuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.DefaultAuditLogsForInvoices;
-import com.ning.billing.util.audit.DefaultAuditLogsForPayments;
-import com.ning.billing.util.audit.DefaultAuditLogsForRefunds;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.dao.AuditDao;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.dao.TableName;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 
 public class DefaultAuditUserApi implements AuditUserApi {
@@ -68,77 +48,28 @@ public class DefaultAuditUserApi implements AuditUserApi {
     }
 
     @Override
-    public AuditLogsForAccount getAuditLogsForAccount(final UUID accountId, final AuditLevel auditLevel, final TenantContext context) {
-        return new DefaultAuditLogsForAccount(getAuditLogs(accountId, ObjectType.ACCOUNT, auditLevel, context));
-    }
-
-    @Override
-    public AuditLogsForBundles getAuditLogsForBundles(final List<SubscriptionBundle> bundles, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> bundlesAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        final Map<UUID, List<AuditLog>> subscriptionsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        final Map<UUID, List<AuditLog>> subscriptionEventsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final SubscriptionBundle bundle : bundles) {
-            bundlesAuditLogs.put(bundle.getId(), getAuditLogs(bundle.getId(), ObjectType.BUNDLE, auditLevel, context));
-            for (final Subscription cur : bundle.getSubscriptions()) {
-
-                final ImmutableList<SubscriptionEvent> events = ImmutableList.<SubscriptionEvent>copyOf(Collections2.filter(bundle.getTimeline().getSubscriptionEvents(), new Predicate<SubscriptionEvent>() {
-                    @Override
-                    public boolean apply(@Nullable final SubscriptionEvent input) {
-                        return input.getEntitlementId().equals(cur.getId());
-                    }
-                }));
-                subscriptionsAuditLogs.put(cur.getId(), getAuditLogs(cur.getId(), ObjectType.SUBSCRIPTION, auditLevel, context));
-                for (final SubscriptionEvent event : events) {
-                    subscriptionEventsAuditLogs.put(event.getId(), getAuditLogs(event.getId(), event.getSubscriptionEventType().getObjectType(), auditLevel, context));
-                }
-            }
-        }
-
-        return new DefaultAuditLogsForBundles(bundlesAuditLogs, subscriptionsAuditLogs, subscriptionEventsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForInvoicePayments getAuditLogsForInvoicePayments(final List<InvoicePayment> invoicePayments, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> invoicePaymentsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final InvoicePayment invoicePayment : invoicePayments) {
-            invoicePaymentsAuditLogs.put(invoicePayment.getId(), getAuditLogs(invoicePayment.getId(), ObjectType.INVOICE_PAYMENT, auditLevel, context));
-        }
-
-        return new DefaultAuditLogsForInvoicePayments(invoicePaymentsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForRefunds getAuditLogsForRefunds(final List<Refund> refunds, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> refundsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Refund refund : refunds) {
-            refundsAuditLogs.put(refund.getId(), getAuditLogs(refund.getId(), ObjectType.REFUND, auditLevel, context));
+    public AccountAuditLogs getAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        // Optimization - bail early
+        if (AuditLevel.NONE.equals(auditLevel)) {
+            return new DefaultAccountAuditLogs(accountId);
         }
 
-        return new DefaultAuditLogsForRefunds(refundsAuditLogs);
+        return auditDao.getAuditLogsForAccountRecordId(auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
-    public AuditLogsForPayments getAuditLogsForPayments(final List<Payment> payments, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> paymentsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Payment payment : payments) {
-            paymentsAuditLogs.put(payment.getId(), getAuditLogs(payment.getId(), ObjectType.PAYMENT, auditLevel, context));
+    public AccountAuditLogsForObjectType getAccountAuditLogs(final UUID accountId, final ObjectType objectType, final AuditLevel auditLevel, final TenantContext tenantContext) {
+        // Optimization - bail early
+        if (AuditLevel.NONE.equals(auditLevel)) {
+            return new DefaultAccountAuditLogsForObjectType(auditLevel);
         }
 
-        return new DefaultAuditLogsForPayments(paymentsAuditLogs);
-    }
-
-    @Override
-    public AuditLogsForInvoices getAuditLogsForInvoices(final List<Invoice> invoices, final AuditLevel auditLevel, final TenantContext context) {
-        final Map<UUID, List<AuditLog>> invoiceAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        for (final Invoice invoice : invoices) {
-            invoiceAuditLogs.put(invoice.getId(), getAuditLogs(invoice.getId(), ObjectType.INVOICE, auditLevel, context));
-            for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
-                invoiceItemsAuditLogs.put(invoiceItem.getId(), getAuditLogs(invoiceItem.getId(), ObjectType.INVOICE_ITEM, auditLevel, context));
-            }
+        final TableName tableName = getTableNameFromObjectType(objectType);
+        if (tableName == null) {
+            return new DefaultAccountAuditLogsForObjectType(auditLevel);
         }
 
-        return new DefaultAuditLogsForInvoices(invoiceAuditLogs, invoiceItemsAuditLogs);
+        return auditDao.getAuditLogsForAccountRecordId(tableName, auditLevel, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
index ab88cbd..e11446f 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/AuditDao.java
@@ -19,12 +19,20 @@ package com.ning.billing.util.audit.dao;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.dao.TableName;
 
 public interface AuditDao {
 
+    // Make sure to consume all or call close() when done to release the connection
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(AuditLevel auditLevel, InternalTenantContext context);
+
+    // Make sure to consume all or call close() when done to release the connection
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(TableName tableName, AuditLevel auditLevel, InternalTenantContext context);
+
     public List<AuditLog> getAuditLogsForId(TableName tableName, UUID objectId, AuditLevel auditLevel, InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
index 1aa55e6..f9f032b 100644
--- a/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
+++ b/util/src/main/java/com/ning/billing/util/audit/dao/DefaultAuditDao.java
@@ -16,28 +16,39 @@
 
 package com.ning.billing.util.audit.dao;
 
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
 
 import org.skife.jdbi.v2.IDBI;
 
+import com.ning.billing.ObjectType;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.clock.Clock;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
+import com.ning.billing.util.audit.DefaultAuditLog;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.clock.Clock;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.dao.NonEntitySqlDao;
+import com.ning.billing.util.dao.RecordIdIdMappings;
 import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
+import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
 
 public class DefaultAuditDao implements AuditDao {
 
@@ -51,6 +62,104 @@ public class DefaultAuditDao implements AuditDao {
     }
 
     @Override
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(final AuditLevel auditLevel, final InternalTenantContext context) {
+        final UUID accountId = nonEntitySqlDao.getIdFromObject(context.getAccountRecordId(), TableName.ACCOUNT.getTableName());
+
+        // Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
+        // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+        // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+        final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+        final Iterator<AuditLogModelDao> auditLogsForAccountRecordId = auditSqlDao.getAuditLogsForAccountRecordId(context);
+        final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForAccountRecordId, context);
+
+        return new DefaultAccountAuditLogs(accountId, auditLevel, allAuditLogs);
+    }
+
+    @Override
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(final TableName tableName, final AuditLevel auditLevel, final InternalTenantContext context) {
+        final String actualTableName;
+        if (tableName.hasHistoryTable()) {
+            actualTableName = tableName.getHistoryTableName().name(); // upper cased
+        } else {
+            actualTableName = tableName.getTableName();
+        }
+
+        // Lazy evaluate records to minimize the memory footprint (these can yield a lot of results)
+        // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+        // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+        final EntitySqlDao auditSqlDao = transactionalSqlDao.onDemand(EntitySqlDao.class);
+        final Iterator<AuditLogModelDao> auditLogsForTableNameAndAccountRecordId = auditSqlDao.getAuditLogsForTableNameAndAccountRecordId(actualTableName, context);
+        final Iterator<AuditLog> allAuditLogs = buildAuditLogsFromModelDao(auditLogsForTableNameAndAccountRecordId, context);
+
+        return new DefaultAccountAuditLogsForObjectType(auditLevel, allAuditLogs);
+    }
+
+    private Iterator<AuditLog> buildAuditLogsFromModelDao(final Iterator<AuditLogModelDao> auditLogsForAccountRecordId, final InternalTenantContext tenantContext) {
+        final Map<TableName, Map<Long, UUID>> recordIdIdsCache = new HashMap<TableName, Map<Long, UUID>>();
+        final Map<TableName, Map<Long, UUID>> historyRecordIdIdsCache = new HashMap<TableName, Map<Long, UUID>>();
+        return Iterators.<AuditLogModelDao, AuditLog>transform(auditLogsForAccountRecordId,
+                                                               new Function<AuditLogModelDao, AuditLog>() {
+                                                                   @Override
+                                                                   public AuditLog apply(final AuditLogModelDao input) {
+                                                                       // If input is for e.g. TAG_DEFINITION_HISTORY, retrieve TAG_DEFINITIONS
+                                                                       // For tables without history, e.g. TENANT, originalTableNameForHistoryTableName will be null
+                                                                       final TableName originalTableNameForHistoryTableName = findTableNameForHistoryTableName(input.getTableName());
+
+                                                                       final ObjectType objectType;
+                                                                       final UUID auditedEntityId;
+                                                                       if (originalTableNameForHistoryTableName != null) {
+                                                                           // input point to a history entry
+                                                                           objectType = originalTableNameForHistoryTableName.getObjectType();
+
+                                                                           if (historyRecordIdIdsCache.get(originalTableNameForHistoryTableName) == null) {
+                                                                               if (TableName.ACCOUNT.equals(originalTableNameForHistoryTableName)) {
+                                                                                   final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappingsForAccountsTable(originalTableNameForHistoryTableName.getTableName(),
+                                                                                                                                                                                              input.getTableName().getTableName(),
+                                                                                                                                                                                              tenantContext);
+                                                                                   historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
+                                                                               } else if (TableName.TAG_DEFINITIONS.equals(originalTableNameForHistoryTableName)) {
+                                                                                   final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappingsForTablesWithoutAccountRecordId(originalTableNameForHistoryTableName.getTableName(),
+                                                                                                                                                                                                             input.getTableName().getTableName(),
+                                                                                                                                                                                                             tenantContext);
+                                                                                   historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
+                                                                               } else {
+                                                                                   final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getHistoryRecordIdIdMappings(originalTableNameForHistoryTableName.getTableName(),
+                                                                                                                                                                              input.getTableName().getTableName(),
+                                                                                                                                                                              tenantContext);
+                                                                                   historyRecordIdIdsCache.put(originalTableNameForHistoryTableName, RecordIdIdMappings.toMap(mappings));
+
+                                                                               }
+                                                                           }
+
+                                                                           auditedEntityId = historyRecordIdIdsCache.get(originalTableNameForHistoryTableName).get(input.getTargetRecordId());
+                                                                       } else {
+                                                                           objectType = input.getTableName().getObjectType();
+
+                                                                           if (recordIdIdsCache.get(input.getTableName()) == null) {
+                                                                               final Iterable<RecordIdIdMappings> mappings = nonEntitySqlDao.getRecordIdIdMappings(input.getTableName().getTableName(),
+                                                                                                                                                                   tenantContext);
+                                                                               recordIdIdsCache.put(input.getTableName(), RecordIdIdMappings.toMap(mappings));
+                                                                           }
+
+                                                                           auditedEntityId = recordIdIdsCache.get(input.getTableName()).get(input.getTargetRecordId());
+                                                                       }
+
+                                                                       return new DefaultAuditLog(input, objectType, auditedEntityId);
+                                                                   }
+
+                                                                   private TableName findTableNameForHistoryTableName(final TableName historyTableName) {
+                                                                       for (final TableName tableName : TableName.values()) {
+                                                                           if (historyTableName.equals(tableName.getHistoryTableName())) {
+                                                                               return tableName;
+                                                                           }
+                                                                       }
+
+                                                                       return null;
+                                                                   }
+                                                               });
+    }
+
+    @Override
     public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
         if (tableName.hasHistoryTable()) {
             return doGetAuditLogsViaHistoryForId(tableName, objectId, auditLevel, context);
@@ -64,7 +173,7 @@ public class DefaultAuditDao implements AuditDao {
         if (recordId == null) {
             return ImmutableList.<AuditLog>of();
         } else {
-            return getAuditLogsForRecordId(tableName, recordId, auditLevel, context);
+            return getAuditLogsForRecordId(tableName, objectId, recordId, auditLevel, context);
         }
     }
 
@@ -78,32 +187,44 @@ public class DefaultAuditDao implements AuditDao {
         final List<AuditLog> allAuditLogs = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AuditLog>>() {
             @Override
             public List<AuditLog> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
-                                                                                                                     historyTableName.getTableName().toLowerCase(),
-                                                                                                                     targetRecordId,
-                                                                                                                     context);
+                final List<AuditLogModelDao> auditLogsViaHistoryForTargetRecordId = entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsViaHistoryForTargetRecordId(historyTableName.name(),
+                                                                                                                                                                                  historyTableName.getTableName().toLowerCase(),
+                                                                                                                                                                                  targetRecordId,
+                                                                                                                                                                                  context);
+                return buildAuditLogsFromModelDao(auditLogsViaHistoryForTargetRecordId, tableName.getObjectType(), objectId);
             }
         });
-        return buildAuditLogs(auditLevel, allAuditLogs);
+        return filterAuditLogs(auditLevel, allAuditLogs);
     }
 
-    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final Long targetRecordId, final AuditLevel auditLevel, final InternalTenantContext context) {
+    private List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final UUID auditedEntityId, final Long targetRecordId, final AuditLevel auditLevel, final InternalTenantContext context) {
         final List<AuditLog> allAuditLogs = transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AuditLog>>() {
             @Override
             public List<AuditLog> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsForTargetRecordId(tableName.name(),
-                                                                                                           targetRecordId,
-                                                                                                           context);
+                final List<AuditLogModelDao> auditLogsForTargetRecordId = entitySqlDaoWrapperFactory.become(EntitySqlDao.class).getAuditLogsForTargetRecordId(tableName.name(),
+                                                                                                                                                              targetRecordId,
+                                                                                                                                                              context);
+                return buildAuditLogsFromModelDao(auditLogsForTargetRecordId, tableName.getObjectType(), auditedEntityId);
             }
         });
-        return buildAuditLogs(auditLevel, allAuditLogs);
+        return filterAuditLogs(auditLevel, allAuditLogs);
+    }
+
+    private List<AuditLog> buildAuditLogsFromModelDao(final List<AuditLogModelDao> auditLogsForAccountRecordId, final ObjectType objectType, final UUID auditedEntityId) {
+        return Lists.<AuditLogModelDao, AuditLog>transform(auditLogsForAccountRecordId,
+                                                           new Function<AuditLogModelDao, AuditLog>() {
+                                                               @Override
+                                                               public AuditLog apply(final AuditLogModelDao input) {
+                                                                   return new DefaultAuditLog(input, objectType, auditedEntityId);
+                                                               }
+                                                           });
     }
 
-    private List<AuditLog> buildAuditLogs(final AuditLevel auditLevel, final List<AuditLog> auditLogs) {
+    private List<AuditLog> filterAuditLogs(final AuditLevel auditLevel, final List<AuditLog> auditLogs) {
         // TODO Do the filtering in the query
         if (AuditLevel.FULL.equals(auditLevel)) {
             return auditLogs;
-        } else if (AuditLevel.MINIMAL.equals(auditLevel) && auditLogs.size() > 0) {
+        } else if (AuditLevel.MINIMAL.equals(auditLevel) && !auditLogs.isEmpty()) {
             if (ChangeType.INSERT.equals(auditLogs.get(0).getChangeType())) {
                 return ImmutableList.<AuditLog>of(auditLogs.get(0));
             } else {
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java
new file mode 100644
index 0000000..963eb20
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogs.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.audit;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.util.api.AuditLevel;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+
+public class DefaultAccountAuditLogs implements AccountAuditLogs {
+
+    private final UUID accountId;
+    private final AuditLevel auditLevel;
+    private final Collection<AuditLog> accountAuditLogs;
+
+    private final Map<ObjectType, DefaultAccountAuditLogsForObjectType> auditLogsCache = new HashMap<ObjectType, DefaultAccountAuditLogsForObjectType>();
+
+    public DefaultAccountAuditLogs(final UUID accountId) {
+        this(accountId, AuditLevel.NONE, Iterators.<AuditLog>emptyIterator());
+    }
+
+    public DefaultAccountAuditLogs(final UUID accountId, final AuditLevel auditLevel, final Iterator<AuditLog> accountAuditLogsOrderedByTableName) {
+        this.accountId = accountId;
+        this.auditLevel = auditLevel;
+        // TODO pierre - lame, we should be smarter to avoid loading all entries in memory. It's a bit tricky though...
+        this.accountAuditLogs = ImmutableList.<AuditLog>copyOf(accountAuditLogsOrderedByTableName);
+    }
+
+    public void close() {
+        // Make sure to go through the results to close the connection
+        // no-op for now, see TODO above
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForAccount() {
+        return getAuditLogs(ObjectType.ACCOUNT).getAuditLogs(accountId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForBundle(final UUID bundleId) {
+        return getAuditLogs(ObjectType.BUNDLE).getAuditLogs(bundleId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForSubscription(final UUID subscriptionId) {
+        return getAuditLogs(ObjectType.SUBSCRIPTION).getAuditLogs(subscriptionId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForSubscriptionEvent(final UUID subscriptionEventId) {
+        return getAuditLogs(ObjectType.SUBSCRIPTION_EVENT).getAuditLogs(subscriptionEventId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForInvoice(final UUID invoiceId) {
+        return getAuditLogs(ObjectType.INVOICE).getAuditLogs(invoiceId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForInvoiceItem(final UUID invoiceItemId) {
+        return getAuditLogs(ObjectType.INVOICE_ITEM).getAuditLogs(invoiceItemId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForPayment(final UUID paymentId) {
+        return getAuditLogs(ObjectType.PAYMENT).getAuditLogs(paymentId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForRefund(final UUID refundId) {
+        return getAuditLogs(ObjectType.REFUND).getAuditLogs(refundId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForChargeback(final UUID chargebackId) {
+        return getAuditLogs(ObjectType.INVOICE_PAYMENT).getAuditLogs(chargebackId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForBlockingState(final UUID blockingStateId) {
+        return getAuditLogs(ObjectType.BLOCKING_STATES).getAuditLogs(blockingStateId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForInvoicePayment(final UUID invoicePaymentId) {
+        return getAuditLogs(ObjectType.INVOICE_PAYMENT).getAuditLogs(invoicePaymentId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForTag(final UUID tagId) {
+        return getAuditLogs(ObjectType.TAG).getAuditLogs(tagId);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForCustomField(final UUID customFieldId) {
+        return getAuditLogs(ObjectType.CUSTOM_FIELD).getAuditLogs(customFieldId);
+    }
+
+    @Override
+    public AccountAuditLogsForObjectType getAuditLogs(final ObjectType objectType) {
+        if (auditLogsCache.get(objectType) == null) {
+            auditLogsCache.put(objectType, new DefaultAccountAuditLogsForObjectType(auditLevel, new ObjectTypeFilter(objectType, accountAuditLogs.iterator())));
+        }
+
+        // Should never be null
+        return auditLogsCache.get(objectType);
+    }
+
+    private final class ObjectTypeFilter extends AbstractIterator<AuditLog> {
+
+        private boolean hasSeenObjectType = false;
+
+        private final ObjectType objectType;
+        private final Iterator<AuditLog> accountAuditLogs;
+
+        private ObjectTypeFilter(final ObjectType objectType, final Iterator<AuditLog> accountAuditLogs) {
+            this.objectType = objectType;
+            this.accountAuditLogs = accountAuditLogs;
+        }
+
+        @Override
+        protected AuditLog computeNext() {
+            while (accountAuditLogs.hasNext()) {
+                final AuditLog element = accountAuditLogs.next();
+                if (predicate.apply(element)) {
+                    hasSeenObjectType = true;
+                    return element;
+                } else if (hasSeenObjectType) {
+                    // Optimization trick: audit log records are ordered first by table name
+                    // (hence object type) - when we are done and we switch to another ObjectType,
+                    // we are guaranteed there is nothing left to do
+                    return endOfData();
+                }
+            }
+
+            return endOfData();
+        }
+
+        private final Predicate<AuditLog> predicate = new Predicate<AuditLog>() {
+            @Override
+            public boolean apply(final AuditLog auditLog) {
+                return objectType.equals(auditLog.getAuditedObjectType());
+            }
+        };
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
new file mode 100644
index 0000000..108b170
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAccountAuditLogsForObjectType.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.audit;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.util.api.AuditLevel;
+import com.ning.billing.util.customfield.ShouldntHappenException;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+
+public class DefaultAccountAuditLogsForObjectType implements AccountAuditLogsForObjectType {
+
+    private final Map<UUID, List<AuditLog>> auditLogsCache;
+
+    private final AuditLevel auditLevel;
+    private final Iterator<AuditLog> allAuditLogsForObjectType;
+
+    public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel) {
+        this(auditLevel, Iterators.<AuditLog>emptyIterator());
+    }
+
+    public DefaultAccountAuditLogsForObjectType(final AuditLevel auditLevel, final Iterator<AuditLog> allAuditLogsForObjectType) {
+        this.auditLevel = auditLevel;
+        this.auditLogsCache = new HashMap<UUID, List<AuditLog>>();
+        this.allAuditLogsForObjectType = allAuditLogsForObjectType;
+    }
+
+    // Used by DefaultAccountAuditLogs
+    void initializeIfNeeded(final UUID objectId) {
+        if (auditLogsCache.get(objectId) == null) {
+            auditLogsCache.put(objectId, new LinkedList<AuditLog>());
+        }
+    }
+
+    public void close() {
+        // Make sure to go through the results to close the connection
+        while (allAuditLogsForObjectType.hasNext()) {
+            allAuditLogsForObjectType.next();
+        }
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogs(final UUID objectId) {
+        switch (auditLevel) {
+            case FULL:
+                // We need to go through the whole list
+                cacheAllAuditLogs();
+
+                // We went through the whole list, mark we don't have any entry for it if needed
+                initializeIfNeeded(objectId);
+
+                // Should never be null
+                return auditLogsCache.get(objectId);
+            case MINIMAL:
+                if (auditLogsCache.get(objectId) == null) {
+                    // We just want the first INSERT audit log
+                    final AuditLog candidate = Iterators.<AuditLog>tryFind(allAuditLogsForObjectType,
+                                                                           new Predicate<AuditLog>() {
+                                                                               @Override
+                                                                               public boolean apply(final AuditLog auditLog) {
+                                                                                   // As we consume the data source, cache the entries
+                                                                                   cacheAuditLog(auditLog);
+
+                                                                                   return objectId.equals(auditLog.getAuditedEntityId()) &&
+                                                                                          // Given our ordering, this should always be true for the first entry
+                                                                                          ChangeType.INSERT.equals(auditLog.getChangeType());
+                                                                               }
+                                                                           }).orNull();
+
+                    if (candidate == null) {
+                        // We went through the whole list, mark we don't have any entry for it
+                        initializeIfNeeded(objectId);
+                    }
+                }
+
+                // Should never be null
+                return auditLogsCache.get(objectId);
+            case NONE:
+                // Close the connection ASAP since we won't need it
+                close();
+                return ImmutableList.<AuditLog>of();
+            default:
+                throw new ShouldntHappenException("AuditLevel " + auditLevel + " unsupported");
+        }
+    }
+
+    private void cacheAllAuditLogs() {
+        while (allAuditLogsForObjectType.hasNext()) {
+            final AuditLog auditLog = allAuditLogsForObjectType.next();
+            cacheAuditLog(auditLog);
+        }
+    }
+
+    private void cacheAuditLog(final AuditLog auditLog) {
+        initializeIfNeeded(auditLog.getAuditedEntityId());
+        auditLogsCache.get(auditLog.getAuditedEntityId()).add(auditLog);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountAuditLogsForObjectType{");
+        sb.append("auditLogsCache=").append(auditLogsCache);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountAuditLogsForObjectType that = (DefaultAccountAuditLogsForObjectType) o;
+
+        if (!auditLogsCache.equals(that.auditLogsCache)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return auditLogsCache.hashCode();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
index 0c3e07e..b2deef3 100644
--- a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010-2012 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
  *
  * Ning licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -16,56 +16,76 @@
 
 package com.ning.billing.util.audit;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.dao.EntityAudit;
+import com.ning.billing.ObjectType;
+import com.ning.billing.entity.EntityBase;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
+
+public class DefaultAuditLog extends EntityBase implements AuditLog {
 
-public class DefaultAuditLog extends EntityAudit implements AuditLog {
+    private final AuditLogModelDao auditLogModelDao;
+    private final ObjectType objectType;
+    private final UUID auditedEntityId;
 
-    private final CallContext callContext;
+    public DefaultAuditLog(final AuditLogModelDao auditLogModelDao, final ObjectType objectType, final UUID auditedEntityId) {
+        super(auditLogModelDao);
+        this.auditLogModelDao = auditLogModelDao;
+        this.objectType = objectType;
+        this.auditedEntityId = auditedEntityId;
+    }
 
-    public DefaultAuditLog(final EntityAudit entityAudit, final CallContext callContext) {
-        super(entityAudit.getId(), entityAudit.getTableName(), entityAudit.getTargetRecordId(), entityAudit.getChangeType(), entityAudit.getCreatedDate());
-        this.callContext = callContext;
+    @Override
+    public UUID getAuditedEntityId() {
+        return auditedEntityId;
     }
 
+    @Override
+    public ObjectType getAuditedObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public ChangeType getChangeType() {
+        return auditLogModelDao.getChangeType();
+    }
 
     @Override
     public String getUserName() {
-        return callContext.getUserName();
+        return auditLogModelDao.getCallContext().getUserName();
     }
 
     @Override
     public DateTime getCreatedDate() {
-        return callContext.getCreatedDate();
+        return auditLogModelDao.getCallContext().getCreatedDate();
     }
 
     @Override
     public String getReasonCode() {
-        return callContext.getReasonCode();
+        return auditLogModelDao.getCallContext().getReasonCode();
     }
 
     @Override
     public String getUserToken() {
-        if (callContext.getUserToken() == null) {
+        if (auditLogModelDao.getCallContext().getUserToken() == null) {
             return null;
         } else {
-            return callContext.getUserToken().toString();
+            return auditLogModelDao.getCallContext().getUserToken().toString();
         }
     }
 
     @Override
     public String getComment() {
-        return callContext.getComments();
+        return auditLogModelDao.getCallContext().getComments();
     }
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("DefaultAuditLog {");
-        sb.append(super.toString());
-        sb.append(", callcontext=").append(callContext);
+        final StringBuilder sb = new StringBuilder("DefaultAuditLog{");
+        sb.append("auditLogModelDao=").append(auditLogModelDao);
+        sb.append(", auditedEntityId=").append(auditedEntityId);
         sb.append('}');
         return sb.toString();
     }
@@ -84,7 +104,10 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
 
         final DefaultAuditLog that = (DefaultAuditLog) o;
 
-        if (callContext != null ? !callContext.equals(that.callContext) : that.callContext != null) {
+        if (auditLogModelDao != null ? !auditLogModelDao.equals(that.auditLogModelDao) : that.auditLogModelDao != null) {
+            return false;
+        }
+        if (auditedEntityId != null ? !auditedEntityId.equals(that.auditedEntityId) : that.auditedEntityId != null) {
             return false;
         }
 
@@ -94,7 +117,8 @@ public class DefaultAuditLog extends EntityAudit implements AuditLog {
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        result = 31 * result + (callContext != null ? callContext.hashCode() : 0);
+        result = 31 * result + (auditLogModelDao != null ? auditLogModelDao.hashCode() : 0);
+        result = 31 * result + (auditedEntityId != null ? auditedEntityId.hashCode() : 0);
         return result;
     }
 }
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
index 17c9ed0..c5560b1 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalCallContextFactory.java
@@ -82,6 +82,11 @@ public class InternalCallContextFactory {
         return new InternalTenantContext(tenantRecordId, accountRecordId);
     }
 
+    public InternalTenantContext createInternalTenantContext(final UUID accountId, final InternalTenantContext context) {
+        final Long tenantRecordId = context.getTenantRecordId();
+        final Long accountRecordId = getAccountRecordId(accountId, ObjectType.ACCOUNT);
+        return new InternalTenantContext(tenantRecordId, accountRecordId);
+    }
 
     /**
      * Crate an internal tenant callcontext from a tenant callcontext, and retrieving the account_record_id from another table
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
index d9f9b83..7f91aac 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditSqlDao.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.util.dao;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
@@ -23,11 +24,11 @@ import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.Define;
-import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.customizers.FetchSize;
 
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.cache.Cachable;
 import com.ning.billing.util.cache.Cachable.CacheType;
 import com.ning.billing.util.cache.CachableKey;
@@ -39,11 +40,11 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
  * which is good enough: the cache will always get at least the initial CREATION audit log entry, which is the one
  * we really care about (both for Analytics and for Kaui's endpoints). Besides, we do cache invalidation properly
  * on our own node (see EntitySqlDaoWrapperInvocationHandler).
- *
+ * <p/>
  * Note 2: in the queries below, tableName always refers to the TableName enum, not the actual table name (TableName.getTableName()).
  */
 @EntitySqlDaoStringTemplate("/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg")
-@RegisterMapper(AuditLogMapper.class)
+// Note: @RegisterMapper annotation won't work here as we build the SqlObject via EntitySqlDao (annotations won't be inherited for JDBI)
 public interface AuditSqlDao {
 
     @SqlUpdate
@@ -51,15 +52,28 @@ public interface AuditSqlDao {
                                            @BindBean final InternalCallContext context);
 
     @SqlQuery
+    // Magic value to force MySQL to stream from the database
+    // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+    @FetchSize(Integer.MIN_VALUE)
+    public Iterator<AuditLogModelDao> getAuditLogsForAccountRecordId(@BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    // Magic value to force MySQL to stream from the database
+    // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+    @FetchSize(Integer.MIN_VALUE)
+    public Iterator<AuditLogModelDao> getAuditLogsForTableNameAndAccountRecordId(@Bind("tableName") final String tableName,
+                                                                                 @BindBean final InternalTenantContext context);
+
+    @SqlQuery
     @Cachable(CacheType.AUDIT_LOG)
-    public List<AuditLog> getAuditLogsForTargetRecordId(@CachableKey(1) @Bind("tableName") final String tableName,
-                                                        @CachableKey(2) @Bind("targetRecordId") final long targetRecordId,
-                                                        @BindBean final InternalTenantContext context);
+    public List<AuditLogModelDao> getAuditLogsForTargetRecordId(@CachableKey(1) @Bind("tableName") final String tableName,
+                                                                @CachableKey(2) @Bind("targetRecordId") final long targetRecordId,
+                                                                @BindBean final InternalTenantContext context);
 
     @SqlQuery
     @Cachable(CacheType.AUDIT_LOG_VIA_HISTORY)
-    public List<AuditLog> getAuditLogsViaHistoryForTargetRecordId(@CachableKey(1) @Bind("tableName") final String historyTableName, /* Uppercased - used to find entries in audit_log table */
-                                                                  @CachableKey(2) @Define("historyTableName") final String actualHistoryTableName, /* Actual table name, used in the inner join query */
-                                                                  @CachableKey(3) @Bind("targetRecordId") final long targetRecordId,
-                                                                  @BindBean final InternalTenantContext context);
+    public List<AuditLogModelDao> getAuditLogsViaHistoryForTargetRecordId(@CachableKey(1) @Bind("tableName") final String historyTableName, /* Uppercased - used to find entries in audit_log table */
+                                                                          @CachableKey(2) @Define("historyTableName") final String actualHistoryTableName, /* Actual table name, used in the inner join query */
+                                                                          @CachableKey(3) @Bind("targetRecordId") final long targetRecordId,
+                                                                          @BindBean final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
index 36d7811..82c3a7f 100644
--- a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
@@ -19,12 +19,15 @@ package com.ning.billing.util.dao;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.customizers.Define;
 import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
 import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.stringtemplate.UseStringTemplate3StatementLocator;
 
+import com.ning.billing.callcontext.InternalTenantContext;
+
 @UseStringTemplate3StatementLocator
 public interface NonEntitySqlDao extends Transactional<NonEntitySqlDao>, CloseMe {
 
@@ -54,4 +57,23 @@ public interface NonEntitySqlDao extends Transactional<NonEntitySqlDao>, CloseMe
 
     @SqlQuery
     public Long getHistoryTargetRecordId(@Bind("recordId") Long recordId, @Define("tableName") final String tableName);
+
+    @SqlQuery
+    public Iterable<RecordIdIdMappings> getHistoryRecordIdIdMappings(@Define("tableName") String tableName,
+                                                                     @Define("historyTableName") String historyTableName,
+                                                                     @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Iterable<RecordIdIdMappings> getHistoryRecordIdIdMappingsForAccountsTable(@Define("tableName") String tableName,
+                                                                                     @Define("historyTableName") String historyTableName,
+                                                                                     @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Iterable<RecordIdIdMappings> getHistoryRecordIdIdMappingsForTablesWithoutAccountRecordId(@Define("tableName") String tableName,
+                                                                                                    @Define("historyTableName") String historyTableName,
+                                                                                                    @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public Iterable<RecordIdIdMappings> getRecordIdIdMappings(@Define("tableName") String tableName,
+                                                              @BindBean final InternalTenantContext context);
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/RecordIdIdMappings.java b/util/src/main/java/com/ning/billing/util/dao/RecordIdIdMappings.java
new file mode 100644
index 0000000..5f9fdc5
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/dao/RecordIdIdMappings.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class RecordIdIdMappings {
+
+    private final Long recordId;
+    private final UUID id;
+
+    public RecordIdIdMappings(final long recordId, final UUID id) {
+        this.recordId = recordId;
+        this.id = id;
+    }
+
+    public Long getRecordId() {
+        return recordId;
+    }
+
+    public UUID getId() {
+        return id;
+    }
+
+    public static Map<Long, UUID> toMap(final Iterable<RecordIdIdMappings> mappings) {
+        final Map<Long, UUID> result = new LinkedHashMap<Long, UUID>();
+        for (final RecordIdIdMappings mapping : mappings) {
+            result.put(mapping.getRecordId(), mapping.getId());
+        }
+        return result;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
index f4c9148..44506de 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
@@ -61,6 +61,8 @@ public interface EntitySqlDao<M extends EntityModelDao<E>, E extends Entity> ext
     @SqlQuery
     public List<M> getByAccountRecordId(@BindBean final InternalTenantContext context);
 
+    @SqlQuery
+    public List<M> getByAccountRecordIdIncludedDeleted(@BindBean final InternalTenantContext context);
 
     @SqlQuery
     @Cachable(CacheType.RECORD_ID)
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
index 143f3bc..e70cfcd 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoStringTemplate.java
@@ -77,9 +77,11 @@ public @interface EntitySqlDaoStringTemplate {
                                     final ParameterizedType type = (ParameterizedType) sqlObjectType.getGenericInterfaces()[i];
                                     for (int j = 0; j < type.getActualTypeArguments().length; j++) {
                                         final Type modelType = type.getActualTypeArguments()[j];
-                                        final Class modelClazz = (Class) modelType;
-                                        if (Entity.class.isAssignableFrom(modelClazz)) {
-                                            query.registerMapper(new LowerToCamelBeanMapperFactory(modelClazz));
+                                        if (modelType instanceof Class) {
+                                            final Class modelClazz = (Class) modelType;
+                                            if (Entity.class.isAssignableFrom(modelClazz)) {
+                                                query.registerMapper(new LowerToCamelBeanMapperFactory(modelClazz));
+                                            }
                                         }
                                     }
                                 }
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 02d56bb..4fe070e 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -384,9 +384,17 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         return nonEntityDao.retrieveLastHistoryRecordIdFromTransaction(entityRecordId, entityModelDao.getHistoryTableName(), transactional);
     }
 
-    private void insertAudits(final TableName tableName, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext context) {
+    private void insertAudits(final TableName tableName, final Long entityRecordId, final Long historyRecordId, final ChangeType changeType, final InternalCallContext contextMaybeWithoutAccountRecordId) {
         final TableName destinationTableName = Objects.firstNonNull(tableName.getHistoryTableName(), tableName);
         final EntityAudit audit = new EntityAudit(destinationTableName, historyRecordId, changeType, clock.getUTCNow());
+
+        final InternalCallContext context;
+        // Populate the account record id when creating the account record
+        if (TableName.ACCOUNT.equals(tableName) && ChangeType.INSERT.equals(changeType)) {
+            context = new InternalCallContext(contextMaybeWithoutAccountRecordId, entityRecordId);
+        } else {
+            context = contextMaybeWithoutAccountRecordId;
+        }
         sqlDao.insertAuditFromTransaction(audit, context);
 
         // We need to invalidate the caches. There is a small window of doom here where caches will be stale.
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index b3a93b4..daf484e 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -22,11 +22,11 @@ import java.util.UUID;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
 import com.ning.billing.util.api.TagUserApi;
 import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.billing.util.tag.ControlTagType;
@@ -142,18 +142,18 @@ public class DefaultTagUserApi implements TagUserApi {
     }
 
     @Override
-    public List<Tag> getTagsForObject(final UUID objectId, final ObjectType objectType, final TenantContext context) {
-        return withModelTransform(tagDao.getTagsForObject(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context)));
+    public List<Tag> getTagsForObject(final UUID objectId, final ObjectType objectType, final boolean includedDeleted, final TenantContext context) {
+        return withModelTransform(tagDao.getTagsForObject(objectId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(context)));
     }
 
     @Override
-    public List<Tag> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final TenantContext context) {
-        return withModelTransform(tagDao.getTagsForAccountType(accountId, objectType, internalCallContextFactory.createInternalTenantContext(accountId, context)));
+    public List<Tag> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final TenantContext context) {
+        return withModelTransform(tagDao.getTagsForAccountType(accountId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(accountId, context)));
     }
 
     @Override
-    public List<Tag> getTagsForAccount(final UUID accountId, final TenantContext context) {
-        return withModelTransform(tagDao.getTagsForAccount(internalCallContextFactory.createInternalTenantContext(accountId, context)));
+    public List<Tag> getTagsForAccount(final UUID accountId, final boolean includedDeleted, final TenantContext context) {
+        return withModelTransform(tagDao.getTagsForAccount(includedDeleted, internalCallContextFactory.createInternalTenantContext(accountId, context)));
     }
 
     private List<Tag> withModelTransform(final List<TagModelDao> input) {
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java
index 888638b..d1307ec 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDao.java
@@ -19,8 +19,6 @@ package com.ning.billing.util.tag.dao;
 import java.util.List;
 import java.util.UUID;
 
-import javax.annotation.Nullable;
-
 import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,19 +27,19 @@ import com.ning.billing.BillingExceptionBase;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.ObjectType;
 import com.ning.billing.bus.api.PersistentBus;
-import com.ning.billing.util.api.TagApiException;
-import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.events.TagInternalEvent;
+import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.dao.NonEntityDao;
 import com.ning.billing.util.entity.dao.EntityDaoBase;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import com.ning.billing.events.TagInternalEvent;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.api.user.TagEventBuilder;
@@ -68,33 +66,42 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
     }
 
     @Override
-    public List<TagModelDao> getTagsForObject(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForObject(final UUID objectId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagModelDao>>() {
             @Override
             public List<TagModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(TagSqlDao.class).getTagsForObject(objectId, objectType, internalTenantContext);
+                final TagSqlDao tagSqlDao = entitySqlDaoWrapperFactory.become(TagSqlDao.class);
+                if (includedDeleted) {
+                    return tagSqlDao.getTagsForObjectIncludedDeleted(objectId, objectType, internalTenantContext);
+                } else {
+                    return tagSqlDao.getTagsForObject(objectId, objectType, internalTenantContext);
+                }
             }
         });
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
-
-        final List<TagModelDao> allTags = getTagsForAccount(internalTenantContext);
+    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
+        final List<TagModelDao> allTags = getTagsForAccount(includedDeleted, internalTenantContext);
         return ImmutableList.<TagModelDao>copyOf(Collections2.filter(allTags, new Predicate<TagModelDao>() {
             @Override
-            public boolean apply(@Nullable final TagModelDao input) {
+            public boolean apply(final TagModelDao input) {
                 return input.getObjectType() == objectType;
             }
         }));
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccount(final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForAccount(final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TagModelDao>>() {
             @Override
             public List<TagModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                return entitySqlDaoWrapperFactory.become(TagSqlDao.class).getByAccountRecordId(internalTenantContext);
+                final TagSqlDao tagSqlDao = entitySqlDaoWrapperFactory.become(TagSqlDao.class);
+                if (includedDeleted) {
+                    return tagSqlDao.getByAccountRecordIdIncludedDeleted(internalTenantContext);
+                } else {
+                    return tagSqlDao.getByAccountRecordId(internalTenantContext);
+                }
             }
         });
     }
@@ -110,7 +117,7 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
         switch (changeType) {
             case INSERT:
                 tagEvent = (isControlTag) ?
-                           tagEventBuilder.newControlTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(),tagDefinition,
+                           tagEventBuilder.newControlTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition,
                                                                       context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) :
                            tagEventBuilder.newUserTagCreationEvent(tag.getId(), tag.getObjectId(), tag.getObjectType(), tagDefinition,
                                                                    context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
index 152115a..0077072 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -20,9 +20,9 @@ import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.api.TagApiException;
 
 public interface TagDao {
 
@@ -32,9 +32,9 @@ public interface TagDao {
 
     TagModelDao getById(UUID tagId, InternalTenantContext context);
 
-    List<TagModelDao> getTagsForObject(UUID objectId, ObjectType objectType, InternalTenantContext internalTenantContext);
+    List<TagModelDao> getTagsForObject(UUID objectId, ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
 
-    List<TagModelDao> getTagsForAccountType(UUID accountId, ObjectType objectType, InternalTenantContext internalTenantContext);
+    List<TagModelDao> getTagsForAccountType(UUID accountId, ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
 
-    List<TagModelDao> getTagsForAccount(InternalTenantContext internalTenantContext);
+    List<TagModelDao> getTagsForAccount(boolean includedDeleted, InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
index 720f474..26caab0 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagSqlDao.java
@@ -25,9 +25,9 @@ import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
@@ -45,4 +45,9 @@ public interface TagSqlDao extends EntitySqlDao<TagModelDao, Tag> {
     List<TagModelDao> getTagsForObject(@Bind("objectId") UUID objectId,
                                        @Bind("objectType") ObjectType objectType,
                                        @BindBean InternalTenantContext internalTenantContext);
+
+    @SqlQuery
+    List<TagModelDao> getTagsForObjectIncludedDeleted(@Bind("objectId") UUID objectId,
+                                                      @Bind("objectType") ObjectType objectType,
+                                                      @BindBean InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagInternalApi.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagInternalApi.java
index c766478..880fb82 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagInternalApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagInternalApi.java
@@ -61,7 +61,7 @@ public class DefaultTagInternalApi implements TagInternalApi {
 
     @Override
     public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return ImmutableList.<Tag>copyOf(Collections2.transform(tagDao.getTagsForObject(objectId, objectType, context),
+        return ImmutableList.<Tag>copyOf(Collections2.transform(tagDao.getTagsForObject(objectId, objectType, false, context),
                                                                 new Function<TagModelDao, Tag>() {
                                                                     @Override
                                                                     public Tag apply(final TagModelDao input) {
diff --git a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
index 1969615..b7e4d4b 100644
--- a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
@@ -73,3 +73,45 @@ where record_id = :recordId
 ;
 >>
 
+getHistoryRecordIdIdMappings(tableName, historyTableName) ::= <<
+select
+  ht.record_id
+, t.id
+from <tableName> t
+join <historyTableName> ht on ht.target_record_id = t.record_id
+where t.account_record_id = :accountRecordId
+and t.tenant_record_id = :tenantRecordId
+;
+>>
+
+getHistoryRecordIdIdMappingsForAccountsTable(tableName, historyTableName) ::= <<
+select
+  ht.record_id
+, t.id
+from <tableName> t
+join <historyTableName> ht on ht.target_record_id = t.record_id
+where t.record_id = :accountRecordId
+and t.tenant_record_id = :tenantRecordId
+;
+>>
+
+getHistoryRecordIdIdMappingsForTablesWithoutAccountRecordId(tableName, historyTableName) ::= <<
+select
+  ht.record_id
+, t.id
+from <tableName> t
+join <historyTableName> ht on ht.target_record_id = t.record_id
+where 1 = 1
+and t.tenant_record_id = :tenantRecordId
+;
+>>
+
+getRecordIdIdMappings(tableName) ::= <<
+select
+  t.record_id
+, t.id
+from <tableName> t
+where t.account_record_id = :accountRecordId
+and t.tenant_record_id = :tenantRecordId
+;
+>>
\ No newline at end of file
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
index 64a66fd..6214825 100644
--- a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -203,6 +203,15 @@ where <accountRecordIdField("t.")> \<=\> :accountRecordId
 ;
 >>
 
+getByAccountRecordIdIncludedDeleted(accountRecordId) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <accountRecordIdField("t.")> \<=\> :accountRecordId
+<AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
+;
+>>
 
 getHistoryTargetRecordId(recordId) ::= <<
 select
@@ -332,6 +341,29 @@ values (
 ;
 >>
 
+getAuditLogsForAccountRecordId() ::= <<
+select
+  <auditTableFields("t.")>
+from <auditTableName()> t
+where <accountRecordIdField("t.")> = :accountRecordId
+<andCheckSoftDeletionWithComma("t.")>
+<AND_CHECK_TENANT("t.")>
+order by t.table_name, <recordIdField("t.")> ASC
+;
+>>
+
+getAuditLogsForTableNameAndAccountRecordId() ::= <<
+select
+  <auditTableFields("t.")>
+from <auditTableName()> t
+where <accountRecordIdField("t.")> = :accountRecordId
+and t.table_name = :tableName
+<andCheckSoftDeletionWithComma("t.")>
+<AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
+;
+>>
+
 getAuditLogsForTargetRecordId() ::= <<
 select
   <auditTableFields("t.")>
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
index 4d709a9..ae38127 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagSqlDao.sql.stg
@@ -46,3 +46,14 @@ and t.object_type = :objectType
 <AND_CHECK_TENANT("t.")>
 ;
 >>
+
+getTagsForObjectIncludedDeleted() ::= <<
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where 1 = 1
+and t.object_id = :objectId
+and t.object_type = :objectType
+<AND_CHECK_TENANT("t.")>
+;
+>>
diff --git a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
index 824e50e..46033f4 100644
--- a/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
+++ b/util/src/test/java/com/ning/billing/dbi/DBIProvider.java
@@ -22,18 +22,17 @@ import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.tweak.transactions.SerializableTransactionRunner;
 
+import com.ning.billing.util.dao.AuditLogModelDaoMapper;
 import com.ning.billing.util.dao.DateTimeArgumentFactory;
 import com.ning.billing.util.dao.DateTimeZoneArgumentFactory;
 import com.ning.billing.util.dao.EnumArgumentFactory;
 import com.ning.billing.util.dao.LocalDateArgumentFactory;
+import com.ning.billing.util.dao.RecordIdIdMappingsMapper;
 import com.ning.billing.util.dao.UUIDArgumentFactory;
 import com.ning.billing.util.dao.UuidMapper;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
-import com.jolbox.bonecp.BoneCPConfig;
-import com.jolbox.bonecp.BoneCPDataSource;
-import com.mchange.v2.c3p0.ComboPooledDataSource;
 
 public class DBIProvider implements Provider<IDBI> {
 
@@ -53,6 +52,8 @@ public class DBIProvider implements Provider<IDBI> {
         dbi.registerArgumentFactory(new LocalDateArgumentFactory());
         dbi.registerArgumentFactory(new EnumArgumentFactory());
         dbi.registerMapper(new UuidMapper());
+        dbi.registerMapper(new AuditLogModelDaoMapper());
+        dbi.registerMapper(new RecordIdIdMappingsMapper());
 
         // Restart transactions in case of deadlocks
         dbi.setTransactionHandler(new SerializableTransactionRunner());
diff --git a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
index 0861ac9..a50d378 100644
--- a/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
+++ b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
@@ -16,35 +16,19 @@
 
 package com.ning.billing.util.audit.api;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
-import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.entitlement.api.SubscriptionBundle;
-import com.ning.billing.subscription.api.timeline.BundleBaseTimeline;
-import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.api.InvoicePayment;
-import com.ning.billing.payment.api.Payment;
-import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.util.audit.AuditLogsForBundles;
-import com.ning.billing.util.audit.AuditLogsForInvoicePayments;
-import com.ning.billing.util.audit.AuditLogsForInvoices;
-import com.ning.billing.util.audit.AuditLogsForPayments;
-import com.ning.billing.util.audit.AuditLogsForRefunds;
 import com.ning.billing.util.audit.AuditLogsTestBase;
 import com.ning.billing.util.audit.dao.MockAuditDao;
 import com.ning.billing.util.dao.TableName;
-import com.ning.billing.util.entity.Entity;
 
 import com.google.common.collect.ImmutableList;
 
@@ -53,7 +37,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
     private List<AuditLog> auditLogs;
     private List<UUID> objectIds;
 
-
     @Override
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {
@@ -61,7 +44,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
         auditLogs = ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog(), createAuditLog(), createAuditLog());
         objectIds = ImmutableList.<UUID>of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID());
 
-
         for (final TableName tableName : TableName.values()) {
             for (final UUID objectId : objectIds) {
                 for (final AuditLog auditLog : auditLogs) {
@@ -72,66 +54,6 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
     }
 
     @Test(groups = "fast")
-    public void testForBundles() throws Exception {
-        final List<SubscriptionBundle> bundles = new ArrayList<SubscriptionBundle>();
-        for (final UUID objectId : objectIds) {
-            final SubscriptionBundle entity = Mockito.mock(SubscriptionBundle.class);
-            Mockito.when(entity.getId()).thenReturn(objectId);
-            bundles.add(entity);
-        }
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForBundles auditLogsForBundles = auditUserApi.getAuditLogsForBundles(bundles, level, callContext);
-            verifyAuditLogs(auditLogsForBundles.getBundlesAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForInvoicePayments() throws Exception {
-        final List<InvoicePayment> invoicePayments = createMocks(InvoicePayment.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForInvoicePayments auditLogsForInvoicePayments = auditUserApi.getAuditLogsForInvoicePayments(invoicePayments, level, callContext);
-            verifyAuditLogs(auditLogsForInvoicePayments.getInvoicePaymentsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForRefunds() throws Exception {
-        final List<Refund> refunds = createMocks(Refund.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForRefunds auditLogsForRefunds = auditUserApi.getAuditLogsForRefunds(refunds, level, callContext);
-            verifyAuditLogs(auditLogsForRefunds.getRefundsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForPayments() throws Exception {
-        final List<Payment> payments = createMocks(Payment.class);
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForPayments auditLogsForPayments = auditUserApi.getAuditLogsForPayments(payments, level, callContext);
-            verifyAuditLogs(auditLogsForPayments.getPaymentsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
-    public void testForInvoices() throws Exception {
-        final List<Invoice> invoices = createMocks(Invoice.class);
-        final List<InvoiceItem> invoiceItems = createMocks(InvoiceItem.class);
-        for (final Invoice invoice : invoices) {
-            Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
-        }
-
-        for (final AuditLevel level : AuditLevel.values()) {
-            final AuditLogsForInvoices auditLogsForInvoices = auditUserApi.getAuditLogsForInvoices(invoices, level, callContext);
-            verifyAuditLogs(auditLogsForInvoices.getInvoiceAuditLogs(), level);
-            verifyAuditLogs(auditLogsForInvoices.getInvoiceItemsAuditLogs(), level);
-        }
-    }
-
-    @Test(groups = "fast")
     public void testForObject() throws Exception {
         for (final ObjectType objectType : ObjectType.values()) {
             for (final UUID objectId : objectIds) {
@@ -147,34 +69,4 @@ public class TestDefaultAuditUserApi extends AuditLogsTestBase {
             }
         }
     }
-
-    private void verifyAuditLogs(final Map<UUID, List<AuditLog>> objectsAuditLogs, final AuditLevel level) {
-        Assert.assertEquals(objectsAuditLogs.size(), objectIds.size());
-
-        if (AuditLevel.NONE.equals(level)) {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId).size(), 0);
-            }
-        } else if (AuditLevel.MINIMAL.equals(level)) {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId).size(), 1);
-                Assert.assertEquals(objectsAuditLogs.get(objectId).get(0), auditLogs.get(0));
-            }
-        } else {
-            for (final UUID objectId : objectIds) {
-                Assert.assertEquals(objectsAuditLogs.get(objectId), auditLogs);
-            }
-        }
-    }
-
-    private <T extends Entity> List<T> createMocks(final Class<T> clazz) {
-        final List<T> entities = new ArrayList<T>();
-        for (final UUID objectId : objectIds) {
-            final T entity = Mockito.mock(clazz);
-            Mockito.when(entity.getId()).thenReturn(objectId);
-            entities.add(entity);
-        }
-
-        return entities;
-    }
 }
diff --git a/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java b/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
index 56d2349..b6982ee 100644
--- a/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
@@ -22,9 +22,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.audit.AuditLog;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.DefaultAccountAuditLogs;
+import com.ning.billing.util.audit.DefaultAccountAuditLogsForObjectType;
 import com.ning.billing.util.dao.TableName;
 
 import com.google.common.base.Objects;
@@ -51,6 +53,16 @@ public class MockAuditDao implements AuditDao {
     }
 
     @Override
+    public DefaultAccountAuditLogs getAuditLogsForAccountRecordId(final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DefaultAccountAuditLogsForObjectType getAuditLogsForAccountRecordId(final TableName tableName, final AuditLevel auditLevel, final InternalTenantContext context) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId, final AuditLevel auditLevel, final InternalTenantContext context) {
         final Map<UUID, List<AuditLog>> auditLogsForTableName = auditLogsForTables.get(tableName);
         if (auditLogsForTableName == null) {
diff --git a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
index b249338..d8f837a 100644
--- a/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/TestDefaultAuditDao.java
@@ -29,6 +29,8 @@ import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
 import com.ning.billing.util.api.AuditLevel;
 import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.audit.AccountAuditLogs;
+import com.ning.billing.util.audit.AccountAuditLogsForObjectType;
 import com.ning.billing.util.audit.AuditLog;
 import com.ning.billing.util.audit.ChangeType;
 import com.ning.billing.util.dao.TableName;
@@ -63,6 +65,12 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
         for (final AuditLevel level : AuditLevel.values()) {
             final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG, tag.getId(), level, internalCallContext);
             verifyAuditLogsForTag(auditLogs, level);
+
+            final AccountAuditLogs accountAuditLogs = auditDao.getAuditLogsForAccountRecordId(level, internalCallContext);
+            verifyAuditLogsForTag(accountAuditLogs.getAuditLogs(ObjectType.TAG).getAuditLogs(tag.getId()), level);
+
+            final AccountAuditLogsForObjectType accountAuditLogsForObjectType = auditDao.getAuditLogsForAccountRecordId(TableName.TAG, level, internalCallContext);
+            verifyAuditLogsForTag(accountAuditLogsForObjectType.getAuditLogs(tag.getId()), level);
         }
     }
 
@@ -102,7 +110,7 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
         tagDao.create(new TagModelDao(theTag), internalCallContext);
         assertListenerStatus();
 
-        final List<TagModelDao> tags = tagDao.getTagsForObject(objectId, ObjectType.ACCOUNT, internalCallContext);
+        final List<TagModelDao> tags = tagDao.getTagsForObject(objectId, ObjectType.ACCOUNT, false, internalCallContext);
         Assert.assertEquals(tags.size(), 1);
         tag = tags.get(0);
         Assert.assertEquals(tag.getTagDefinitionId(), tagDefinition.getId());
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
index b519df5..785b062 100644
--- a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLog.java
@@ -21,12 +21,14 @@ import java.util.UUID;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.billing.ObjectType;
+import com.ning.billing.callcontext.DefaultCallContext;
+import com.ning.billing.clock.ClockMock;
 import com.ning.billing.util.UtilTestSuiteNoDB;
+import com.ning.billing.util.audit.dao.AuditLogModelDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.callcontext.DefaultCallContext;
 import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.clock.ClockMock;
 import com.ning.billing.util.dao.EntityAudit;
 import com.ning.billing.util.dao.TableName;
 
@@ -47,7 +49,7 @@ public class TestDefaultAuditLog extends UtilTestSuiteNoDB {
         final ClockMock clock = new ClockMock();
         final CallContext callContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLog auditLog = new DefaultAuditLog(new AuditLogModelDao(entityAudit, callContext), ObjectType.ACCOUNT_EMAIL, UUID.randomUUID());
         Assert.assertEquals(auditLog.getChangeType(), changeType);
         Assert.assertNull(auditLog.getComment());
         Assert.assertNotNull(auditLog.getCreatedDate());
@@ -71,15 +73,15 @@ public class TestDefaultAuditLog extends UtilTestSuiteNoDB {
         final ClockMock clock = new ClockMock();
         final CallContext callContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
 
-        final AuditLog auditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLogModelDao auditLog = new AuditLogModelDao(entityAudit, callContext);
         Assert.assertEquals(auditLog, auditLog);
 
-        final AuditLog sameAuditLog = new DefaultAuditLog(entityAudit, callContext);
+        final AuditLogModelDao sameAuditLog = new AuditLogModelDao(entityAudit, callContext);
         Assert.assertEquals(sameAuditLog, auditLog);
 
         clock.addMonths(1);
         final CallContext otherCallContext = new DefaultCallContext(tenantId, userName, callOrigin, userType, userToken, clock);
-        final AuditLog otherAuditLog = new DefaultAuditLog(entityAudit, otherCallContext);
+        final AuditLogModelDao otherAuditLog = new AuditLogModelDao(entityAudit, otherCallContext);
         Assert.assertNotEquals(otherAuditLog, auditLog);
     }
 }
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index 5653a7f..890febb 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -24,9 +24,9 @@ import java.util.Map;
 import java.util.UUID;
 
 import com.ning.billing.ObjectType;
-import com.ning.billing.util.api.TagApiException;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.api.TagApiException;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -65,7 +65,7 @@ public class MockTagDao implements TagDao {
     }
 
     @Override
-    public List<TagModelDao> getTagsForObject(final UUID objectId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForObject(final UUID objectId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         if (tagStore.get(objectId) == null) {
             return ImmutableList.<TagModelDao>of();
         }
@@ -79,12 +79,12 @@ public class MockTagDao implements TagDao {
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccount(final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForAccount(final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
index ab76d25..34211b4 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/TestDefaultTagDao.java
@@ -135,10 +135,10 @@ public class TestDefaultTagDao extends UtilTestSuiteWithEmbeddedDB {
         assertListenerStatus();
 
         // Make sure we can retrieve it via the DAO
-        final List<TagModelDao> foundTags = tagDao.getTagsForObject(objectId, objectType, internalCallContext);
+        final List<TagModelDao> foundTags = tagDao.getTagsForObject(objectId, objectType, false, internalCallContext);
         Assert.assertEquals(foundTags.size(), 1);
         Assert.assertEquals(foundTags.get(0).getTagDefinitionId(), createdTagDefinition.getId());
-        final List<TagModelDao> foundTagsForAccount = tagDao.getTagsForAccount(internalCallContext);
+        final List<TagModelDao> foundTagsForAccount = tagDao.getTagsForAccount(false, internalCallContext);
         Assert.assertEquals(foundTagsForAccount.size(), 1);
         Assert.assertEquals(foundTagsForAccount.get(0).getTagDefinitionId(), createdTagDefinition.getId());
 
@@ -148,7 +148,9 @@ public class TestDefaultTagDao extends UtilTestSuiteWithEmbeddedDB {
         assertListenerStatus();
 
         // Make sure the tag is deleted
-        Assert.assertEquals(tagDao.getTagsForObject(objectId, objectType, internalCallContext).size(), 0);
-        Assert.assertEquals(tagDao.getTagsForAccount(internalCallContext).size(), 0);
+        Assert.assertEquals(tagDao.getTagsForObject(objectId, objectType, false, internalCallContext).size(), 0);
+        Assert.assertEquals(tagDao.getTagsForAccount(false, internalCallContext).size(), 0);
+        Assert.assertEquals(tagDao.getTagsForObject(objectId, objectType, true, internalCallContext).size(), 1);
+        Assert.assertEquals(tagDao.getTagsForAccount(true, internalCallContext).size(), 1);
     }
 }