killbill-uncached

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/payment/api/Payment.java b/api/src/main/java/com/ning/billing/payment/api/Payment.java
index a7a483b..1d2db56 100644
--- a/api/src/main/java/com/ning/billing/payment/api/Payment.java
+++ b/api/src/main/java/com/ning/billing/payment/api/Payment.java
@@ -22,10 +22,9 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.Entity;
 
-public interface Payment {
-
-    public UUID getId();
+public interface Payment extends Entity {
 
     public UUID getAccountId();
 
diff --git a/api/src/main/java/com/ning/billing/payment/api/Refund.java b/api/src/main/java/com/ning/billing/payment/api/Refund.java
index d6bb3ea..efc7267 100644
--- a/api/src/main/java/com/ning/billing/payment/api/Refund.java
+++ b/api/src/main/java/com/ning/billing/payment/api/Refund.java
@@ -22,8 +22,9 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.entity.Entity;
 
-public interface Refund {
+public interface Refund extends Entity {
 
     public UUID getId();
 
diff --git a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
index 1770335..266eb3b 100644
--- a/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/AuditUserApi.java
@@ -19,13 +19,63 @@ package com.ning.billing.util.api;
 import java.util.List;
 import java.util.UUID;
 
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+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.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.dao.ObjectType;
 
 public interface AuditUserApi {
 
     /**
-     * Get all the audit entries for a given object
+     * Fetch all audit logs for bundles.
+     *
+     * @param bundles the bundles to lookup
+     * @return all audit logs for these refunds
+     */
+    public AuditLogsForBundles getAuditLogsForBundles(final List<SubscriptionBundle> bundles);
+
+    /**
+     * Fetch all audit logs for invoice payments.
+     *
+     * @param invoicePayments the invoice payments to lookup
+     * @return all audit logs for these invoice payments
+     */
+    public AuditLogsForInvoicePayments getAuditLogsForInvoicePayments(final List<InvoicePayment> invoicePayments);
+
+    /**
+     * Fetch all audit logs for refunds.
+     *
+     * @param refunds the refunds to lookup
+     * @return all audit logs for these refunds
+     */
+    public AuditLogsForRefunds getAuditLogsForRefunds(final List<Refund> refunds);
+
+    /**
+     * Fetch all audit logs for payments.
+     *
+     * @param payments the payments to lookup
+     * @return all audit logs for these payments
+     */
+    public AuditLogsForPayments getAuditLogsForPayments(final List<Payment> payments);
+
+    /**
+     * Fetch all audit logs for invoices and associated invoice items.
+     *
+     * @param invoices the invoices to lookup
+     * @return all audit logs for these invoices
+     */
+    public AuditLogsForInvoices getAuditLogsForInvoices(final List<Invoice> invoices);
+
+    /**
+     * Get all the audit entries for a given object.
      *
      * @param objectId   the object id
      * @param objectType the type of object
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLogsForBundles.java b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForBundles.java
new file mode 100644
index 0000000..0eecc75
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForBundles.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public interface AuditLogsForBundles {
+
+    /**
+     * @return mapping between bundle id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getBundlesAuditLogs();
+
+    /**
+     * @return mapping between subscription event id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getSubscriptionEventsAuditLogs();
+}
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoicePayments.java b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoicePayments.java
new file mode 100644
index 0000000..009fbe2
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoicePayments.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public interface AuditLogsForInvoicePayments {
+
+    /**
+     * @return mapping between invoice payment id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getInvoicePaymentsAuditLogs();
+}
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoices.java b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoices.java
new file mode 100644
index 0000000..99e20a4
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForInvoices.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public interface AuditLogsForInvoices {
+
+    /**
+     * @return mapping between invoice id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getInvoiceAuditLogs();
+
+    /**
+     * @return mapping between invoice item id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getInvoiceItemsAuditLogs();
+}
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLogsForPayments.java b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForPayments.java
new file mode 100644
index 0000000..8e1bc19
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForPayments.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public interface AuditLogsForPayments {
+
+    /**
+     * @return mapping between payment id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getPaymentsAuditLogs();
+}
diff --git a/api/src/main/java/com/ning/billing/util/audit/AuditLogsForRefunds.java b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForRefunds.java
new file mode 100644
index 0000000..152a1fc
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/audit/AuditLogsForRefunds.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public interface AuditLogsForRefunds {
+
+    /**
+     * @return mapping between refund id and associated audit logs
+     */
+    public Map<UUID, List<AuditLog>> getRefundsAuditLogs();
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
index f45509c..1148b22 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
@@ -19,7 +19,6 @@ package com.ning.billing.invoice.api.migration;
 import java.math.BigDecimal;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,7 +30,7 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoiceMigrationApi;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.AuditedInvoiceDao;
 import com.ning.billing.invoice.model.MigrationInvoiceItem;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.CallOrigin;
@@ -45,11 +44,11 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
     private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceMigrationApi.class);
 
     private final AccountUserApi accountUserApi;
-    private final DefaultInvoiceDao dao;
+    private final AuditedInvoiceDao dao;
     private final Clock clock;
 
     @Inject
-    public DefaultInvoiceMigrationApi(final AccountUserApi accountUserApi, final DefaultInvoiceDao dao, final Clock clock) {
+    public DefaultInvoiceMigrationApi(final AccountUserApi accountUserApi, final AuditedInvoiceDao dao, final Clock clock) {
         this.accountUserApi = accountUserApi;
         this.dao = dao;
         this.clock = clock;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
index 9a5bdad..a6d35f8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/DefaultInvoiceModule.java
@@ -32,7 +32,7 @@ import com.ning.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import com.ning.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
 import com.ning.billing.invoice.api.user.DefaultInvoiceUserApi;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.AuditedInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.generator.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.generator.InvoiceGenerator;
@@ -51,7 +51,7 @@ public class DefaultInvoiceModule extends AbstractModule implements InvoiceModul
     InvoiceConfig config;
 
     protected void installInvoiceDao() {
-        bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDao.class).to(AuditedInvoiceDao.class).asEagerSingleton();
     }
 
     @Override
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 2e2c6c9..0c14015 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -25,6 +25,18 @@ getById() ::= <<
   WHERE id = :id;
 >>
 
+getByRecordId() ::= <<
+  SELECT <fields()>
+  FROM invoice_items
+  WHERE record_id = :recordId;
+>>
+
+getRecordId() ::= <<
+  SELECT record_id
+  FROM invoice_items
+  WHERE id = :id;
+>>
+
 getInvoiceItemsByInvoice() ::= <<
   SELECT <fields()>
   FROM invoice_items
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
index 9ccbf84..864c4b4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
@@ -37,7 +37,7 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePayment.InvoicePaymentType;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.AuditedInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
 import com.ning.billing.invoice.dao.InvoiceSqlDao;
@@ -87,7 +87,7 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
         final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
         final TagDao tagDao = new MockTagDao();
         final TagUserApi tagUserApi = new DefaultTagUserApi(tagDefinitionDao, tagDao);
-        final InvoiceDao invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
+        final InvoiceDao invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
         invoicePaymentApi = new DefaultInvoicePaymentApi(invoiceDao);
 
         context = new TestCallContext("Invoice payment tests");
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index e9385a2..1b42c4e 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -110,7 +110,7 @@ public class InvoiceDaoTestBase extends InvoicingTestBase {
         final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
         final TagDao tagDao = new AuditedTagDao(dbi, tagEventBuilder, bus);
         final TagUserApi tagUserApi = new DefaultTagUserApi(tagDefinitionDao, tagDao);
-        invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
+        invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
         invoiceDao.test();
 
         invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
index 9c8a92a..3724b9f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/TestDefaultInvoiceDao.java
@@ -50,7 +50,7 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
 
     private InvoiceSqlDao invoiceSqlDao;
     private TagUserApi tagUserApi;
-    private DefaultInvoiceDao dao;
+    private AuditedInvoiceDao dao;
 
     @BeforeMethod(groups = "fast")
     public void setUp() throws Exception {
@@ -62,7 +62,7 @@ public class TestDefaultInvoiceDao extends InvoiceTestSuite {
         final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
         final TagDao tagDao = new MockTagDao();
         tagUserApi = new DefaultTagUserApi(tagDefinitionDao, tagDao);
-        dao = new DefaultInvoiceDao(idbi, poster, tagUserApi, Mockito.mock(Clock.class));
+        dao = new AuditedInvoiceDao(idbi, poster, tagUserApi, Mockito.mock(Clock.class));
     }
 
     @Test(groups = "fast")
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java b/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
index 689b11a..e9842eb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/TestChargeBacks.java
@@ -36,7 +36,7 @@ import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
-import com.ning.billing.invoice.dao.DefaultInvoiceDao;
+import com.ning.billing.invoice.dao.AuditedInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceItemSqlDao;
 import com.ning.billing.invoice.dao.InvoiceSqlDao;
@@ -89,7 +89,7 @@ public class TestChargeBacks extends InvoiceTestSuiteWithEmbeddedDB {
         final TagDefinitionDao tagDefinitionDao = new MockTagDefinitionDao();
         final TagDao tagDao = new MockTagDao();
         final TagUserApi tagUserApi = new DefaultTagUserApi(tagDefinitionDao, tagDao);
-        final InvoiceDao invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
+        final InvoiceDao invoiceDao = new AuditedInvoiceDao(dbi, nextBillingDatePoster, tagUserApi, clock);
         invoicePaymentApi = new DefaultInvoicePaymentApi(invoiceDao);
 
         context = new TestCallContext("Charge back tests");
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 9d889d4..633b6a3 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
@@ -24,6 +24,8 @@ 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.timeline.BundleTimeline;
 import com.ning.billing.invoice.api.Invoice;
@@ -33,9 +35,15 @@ 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.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 {
@@ -87,6 +95,20 @@ public class AccountTimelineJson {
         return tmp.toString();
     }
 
+    public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments,
+                               final List<BundleTimeline> 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());
+    }
+
     public AccountTimelineJson(final Account account, final List<Invoice> invoices, final List<Payment> payments, final List<BundleTimeline> bundles,
                                final Multimap<UUID, Refund> refundsByPayment, final Multimap<UUID, InvoicePayment> chargebacksByPayment,
                                final Map<UUID, List<AuditLog>> invoiceAuditLogs, final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs,
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 a8093d6..d06ec99 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
@@ -18,10 +18,8 @@ package com.ning.billing.jaxrs.resources;
 
 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;
 
 import javax.ws.rs.Consumes;
@@ -53,7 +51,6 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
@@ -78,7 +75,11 @@ 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.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.dao.ObjectType;
 import com.ning.billing.util.tag.ControlTagType;
 
@@ -225,64 +226,55 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Get the invoices
         final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId());
-        final Map<UUID, List<AuditLog>> invoiceAuditLogs = new HashMap<UUID, List<AuditLog>>();
-        final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        AuditLogsForInvoices invoicesAuditLogs = null;
         if (withAudit) {
-            for (final Invoice invoice : invoices) {
-                invoiceAuditLogs.put(invoice.getId(), auditUserApi.getAuditLogs(invoice.getId(), ObjectType.INVOICE));
-                for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
-                    invoiceItemsAuditLogs.put(invoiceItem.getId(), auditUserApi.getAuditLogs(invoiceItem.getId(), ObjectType.INVOICE_ITEM));
-                }
-            }
+            invoicesAuditLogs = auditUserApi.getAuditLogsForInvoices(invoices);
         }
 
         // Get the payments
         final List<Payment> payments = paymentApi.getAccountPayments(accountId);
-        final Map<UUID, List<AuditLog>> paymentsAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        AuditLogsForPayments paymentsAuditLogs = null;
         if (withAudit) {
-            for (final Payment payment : payments) {
-                paymentsAuditLogs.put(payment.getId(), auditUserApi.getAuditLogs(payment.getId(), ObjectType.PAYMENT));
-            }
+            paymentsAuditLogs = auditUserApi.getAuditLogsForPayments(payments);
         }
 
         // Get the refunds
         final List<Refund> refunds = paymentApi.getAccountRefunds(account);
-        final Map<UUID, List<AuditLog>> refundsAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        AuditLogsForRefunds refundsAuditLogs = null;
+        if (withAudit) {
+            refundsAuditLogs = auditUserApi.getAuditLogsForRefunds(refunds);
+        }
         final Multimap<UUID, Refund> refundsByPayment = ArrayListMultimap.<UUID, Refund>create();
         for (final Refund refund : refunds) {
-            if (withAudit) {
-                refundsAuditLogs.put(refund.getId(), auditUserApi.getAuditLogs(refund.getId(), ObjectType.REFUND));
-            }
             refundsByPayment.put(refund.getPaymentId(), refund);
         }
 
         // Get the chargebacks
         final List<InvoicePayment> chargebacks = invoicePaymentApi.getChargebacksByAccountId(accountId);
-        final Map<UUID, List<AuditLog>> chargebacksAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        AuditLogsForInvoicePayments chargebacksAuditLogs = null;
+        if (withAudit) {
+            chargebacksAuditLogs = auditUserApi.getAuditLogsForInvoicePayments(chargebacks);
+        }
         final Multimap<UUID, InvoicePayment> chargebacksByPayment = ArrayListMultimap.<UUID, InvoicePayment>create();
         for (final InvoicePayment chargeback : chargebacks) {
-            if (withAudit) {
-                chargebacksAuditLogs.put(chargeback.getId(), auditUserApi.getAuditLogs(chargeback.getId(), ObjectType.INVOICE_PAYMENT));
-            }
             chargebacksByPayment.put(chargeback.getPaymentId(), chargeback);
         }
 
         // Get the bundles
         final List<SubscriptionBundle> bundles = entitlementApi.getBundlesForAccount(account.getId());
-        final Map<UUID, List<AuditLog>> bundlesAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        AuditLogsForBundles bundlesAuditLogs = null;
+        if (withAudit) {
+            bundlesAuditLogs = auditUserApi.getAuditLogsForBundles(bundles);
+        }
         final List<BundleTimeline> bundlesTimeline = new LinkedList<BundleTimeline>();
         for (final SubscriptionBundle bundle : bundles) {
-            if (withAudit) {
-                bundlesAuditLogs.put(bundle.getId(), auditUserApi.getAuditLogs(bundle.getId(), ObjectType.BUNDLE));
-            }
             bundlesTimeline.add(timelineApi.getBundleTimeline(bundle.getId()));
         }
 
         final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, bundlesTimeline,
-                                                                 refundsByPayment, chargebacksByPayment, invoiceAuditLogs,
-                                                                 invoiceItemsAuditLogs, paymentsAuditLogs, refundsAuditLogs,
+                                                                 refundsByPayment, chargebacksByPayment,
+                                                                 invoicesAuditLogs, paymentsAuditLogs, refundsAuditLogs,
                                                                  chargebacksAuditLogs, bundlesAuditLogs);
-
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -480,7 +472,7 @@ public class AccountResource extends JaxRsResourceBase {
 
         // Look if there is an AUTO_PAY_OFF for that account and check if the account has a default paymentMethod
         // If not we can't remove the AUTO_PAY_OFF tag
-        final Collection<UUID> tagDefinitionUUIDs =  getTagDefinitionUUIDs(tagList);
+        final Collection<UUID> tagDefinitionUUIDs = getTagDefinitionUUIDs(tagList);
         boolean isTagAutoPayOff = false;
         for (UUID cur : tagDefinitionUUIDs) {
             if (cur.equals(ControlTagType.AUTO_PAY_OFF.getId())) {
diff --git a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
index cad796e..80676f3 100644
--- a/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
+++ b/payment/src/main/java/com/ning/billing/payment/dao/AuditedPaymentDao.java
@@ -96,7 +96,7 @@ public class AuditedPaymentDao implements PaymentDao {
         transactional.insertHistoryFromTransaction(history, context);
 
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENTS, historyRecordId, ChangeType.INSERT);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.INSERT);
         transactional.insertAuditFromTransaction(audit, context);
         return savedPayment;
     }
@@ -108,7 +108,7 @@ public class AuditedPaymentDao implements PaymentDao {
         final EntityHistory<PaymentAttemptModelDao> history = new EntityHistory<PaymentAttemptModelDao>(savedAttempt.getId(), recordId, savedAttempt, ChangeType.INSERT);
         transactional.insertHistoryFromTransaction(history, context);
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPTS, historyRecordId, ChangeType.INSERT);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPT_HISTORY, historyRecordId, ChangeType.INSERT);
         transactional.insertAuditFromTransaction(audit, context);
         return savedAttempt;
     }
@@ -153,7 +153,7 @@ public class AuditedPaymentDao implements PaymentDao {
         final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.UPDATE);
         transactional.insertHistoryFromTransaction(history, context);
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENTS, historyRecordId, ChangeType.UPDATE);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.UPDATE);
         transactional.insertAuditFromTransaction(audit, context);
     }
 
@@ -164,7 +164,7 @@ public class AuditedPaymentDao implements PaymentDao {
         final EntityHistory<PaymentModelDao> history = new EntityHistory<PaymentModelDao>(savedPayment.getId(), recordId, savedPayment, ChangeType.UPDATE);
         transactional.insertHistoryFromTransaction(history, context);
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENTS, historyRecordId, ChangeType.UPDATE);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_HISTORY, historyRecordId, ChangeType.UPDATE);
         transactional.insertAuditFromTransaction(audit, context);
     }
 
@@ -175,7 +175,7 @@ public class AuditedPaymentDao implements PaymentDao {
         final EntityHistory<PaymentAttemptModelDao> history = new EntityHistory<PaymentAttemptModelDao>(savedAttempt.getId(), recordId, savedAttempt, ChangeType.UPDATE);
         transactional.insertHistoryFromTransaction(history, context);
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPTS, historyRecordId, ChangeType.UPDATE);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_ATTEMPT_HISTORY, historyRecordId, ChangeType.UPDATE);
         transactional.insertAuditFromTransaction(audit, context);
     }
 
@@ -197,7 +197,7 @@ public class AuditedPaymentDao implements PaymentDao {
         final EntityHistory<PaymentMethodModelDao> history = new EntityHistory<PaymentMethodModelDao>(savedPaymentMethod.getId(), recordId, savedPaymentMethod, ChangeType.INSERT);
         transactional.insertHistoryFromTransaction(history, context);
         final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_METHODS, historyRecordId, ChangeType.INSERT);
+        final EntityAudit audit = new EntityAudit(TableName.PAYMENT_METHOD_HISTORY, historyRecordId, ChangeType.INSERT);
         transactional.insertAuditFromTransaction(audit, context);
         return savedPaymentMethod;
     }
@@ -262,7 +262,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.INSERT);
                 transactional.insertHistoryFromTransaction(history, context);
                 final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                final EntityAudit audit = new EntityAudit(TableName.REFUNDS, historyRecordId, ChangeType.INSERT);
+                final EntityAudit audit = new EntityAudit(TableName.REFUND_HISTORY, historyRecordId, ChangeType.INSERT);
                 transactional.insertAuditFromTransaction(audit, context);
                 return savedRefund;
             }
@@ -285,7 +285,7 @@ public class AuditedPaymentDao implements PaymentDao {
                 final EntityHistory<RefundModelDao> history = new EntityHistory<RefundModelDao>(savedRefund.getId(), recordId, savedRefund, ChangeType.UPDATE);
                 transactional.insertHistoryFromTransaction(history, context);
                 final Long historyRecordId = transactional.getHistoryRecordId(recordId);
-                final EntityAudit audit = new EntityAudit(TableName.REFUNDS, historyRecordId, ChangeType.UPDATE);
+                final EntityAudit audit = new EntityAudit(TableName.REFUND_HISTORY, historyRecordId, ChangeType.UPDATE);
                 transactional.insertAuditFromTransaction(audit, context);
                 return null;
             }
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index fab5627..c234411 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -16,34 +16,36 @@
 
 package com.ning.billing.jaxrs;
 
+import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.ws.rs.core.Response.Status;
 
 import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.jaxrs.json.AccountJson;
 import com.ning.billing.jaxrs.json.AccountTimelineJson;
+import com.ning.billing.jaxrs.json.AuditLogJson;
 import com.ning.billing.jaxrs.json.BillCycleDayJson;
-import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
+import com.ning.billing.jaxrs.json.ChargebackJson;
+import com.ning.billing.jaxrs.json.CreditJson;
 import com.ning.billing.jaxrs.json.CustomFieldJson;
+import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
 import com.ning.billing.jaxrs.json.PaymentJsonSimple;
+import com.ning.billing.jaxrs.json.PaymentJsonWithBundleKeys;
 import com.ning.billing.jaxrs.json.PaymentMethodJson;
 import com.ning.billing.jaxrs.json.RefundJson;
-import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.json.TagDefinitionJson;
 import com.ning.billing.jaxrs.json.TagJson;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
+import com.ning.billing.util.ChangeType;
 import com.ning.http.client.Response;
 
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -55,39 +57,27 @@ import static org.testng.Assert.assertTrue;
 
 public class TestAccount extends TestJaxrsBase {
 
-    private static final Logger log = LoggerFactory.getLogger(TestAccount.class);
-
     @Test(groups = "slow")
     public void testAccountOk() throws Exception {
-
-        final AccountJson input = createAccount("xoxo", "shdgfhwe", "xoxo@yahoo.com");
+        final AccountJson input = createAccount();
 
         // Retrieves by external key
-        final Map<String, String> queryParams = new HashMap<String, String>();
-        queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "shdgfhwe");
-        Response response = doGet(JaxrsResource.ACCOUNTS_PATH, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        String baseJson = response.getResponseBody();
-        AccountJson objFromJson = mapper.readValue(baseJson, AccountJson.class);
-        Assert.assertTrue(objFromJson.equals(input));
+        final AccountJson retrievedAccount = getAccountByExternalKey(input.getExternalKey());
+        Assert.assertTrue(retrievedAccount.equals(input));
 
         // Update Account
-        final AccountJson newInput = new AccountJson(objFromJson.getAccountId(),
-                                                     "zozo", 4, objFromJson.getExternalKey(), "rr@google.com", new BillCycleDayJson(18, 18),
+        final AccountJson newInput = new AccountJson(input.getAccountId(),
+                                                     "zozo", 4, input.getExternalKey(), "rr@google.com", new BillCycleDayJson(18, 18),
                                                      "USD", null, "UTC", "bl1", "bh2", "", "", "ca", "San Francisco", "usa", "en", "415-255-2991",
                                                      false, false);
-        baseJson = mapper.writeValueAsString(newInput);
-        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + objFromJson.getAccountId();
-        response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        baseJson = response.getResponseBody();
-        objFromJson = mapper.readValue(baseJson, AccountJson.class);
-        Assert.assertTrue(objFromJson.equals(newInput));
+        final AccountJson updatedAccount = updateAccount(input.getAccountId(), newInput);
+        Assert.assertTrue(updatedAccount.equals(newInput));
     }
 
     @Test(groups = "slow")
     public void testUpdateNonExistentAccount() throws Exception {
-        final AccountJson input = getAccountJson("xoxo", "shghaahwe", "xoxo@yahoo.com");
+        final AccountJson input = getAccountJson();
+
         final String baseJson = mapper.writeValueAsString(input);
         final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + input.getAccountId();
         final Response response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
@@ -110,37 +100,110 @@ public class TestAccount extends TestJaxrsBase {
 
     @Test(groups = "slow")
     public void testAccountTimeline() throws Exception {
-
         clock.setTime(new DateTime(2012, 4, 25, 0, 3, 42, 0));
 
-        final AccountJson accountJson = createAccountWithDefaultPaymentMethod("poney", "shdddqgfhwe", "poney@yahoo.com");
-        assertNotNull(accountJson);
-
-        final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), "996599");
-        assertNotNull(bundleJson);
-
-        final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
-        assertNotNull(subscriptionJson);
-
-        // MOVE AFTER TRIAL
-        clock.addDays(31);
+        final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
-        crappyWaitForLackOfProperSynchonization();
+        final AccountTimelineJson timeline = getAccountTimeline(accountJson.getAccountId());
+        Assert.assertEquals(timeline.getPayments().size(), 1);
+        Assert.assertEquals(timeline.getInvoices().size(), 2);
+        Assert.assertEquals(timeline.getBundles().size(), 1);
+        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().size(), 1);
+        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
+    }
 
-        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.TIMELINE;
+    @Test
+    public void testAccountTimelineWithAudits() throws Exception {
+        final DateTime startTime = clock.getUTCNow();
+        final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final DateTime endTime = clock.getUTCNow();
+
+        // Add credit
+        final InvoiceJsonSimple invoice = getInvoicesForAccount(accountJson.getAccountId()).get(1);
+        final DateTime creditEffectiveDate = clock.getUTCNow();
+        final BigDecimal creditAmount = BigDecimal.ONE;
+        createCreditForInvoice(accountJson.getAccountId(), invoice.getInvoiceId(),
+                               creditAmount, clock.getUTCNow(), creditEffectiveDate);
+
+        // Add refund
+        final PaymentJsonSimple postedPayment = getPaymentsForAccount(accountJson.getAccountId()).get(0);
+        final BigDecimal refundAmount = BigDecimal.ONE;
+        createRefund(postedPayment.getPaymentId(), refundAmount);
+
+        // Add chargeback
+        final BigDecimal chargebackAmount = BigDecimal.ONE;
+        createChargeBack(postedPayment.getPaymentId(), chargebackAmount);
+
+        final AccountTimelineJson timeline = getAccountTimelineWithAudits(accountJson.getAccountId());
+
+        // Verify payments
+        Assert.assertEquals(timeline.getPayments().size(), 1);
+        final PaymentJsonWithBundleKeys paymentJson = timeline.getPayments().get(0);
+        final List<AuditLogJson> paymentAuditLogs = paymentJson.getAuditLogs();
+        Assert.assertEquals(paymentAuditLogs.size(), 2);
+        verifyAuditLog(paymentAuditLogs.get(0), ChangeType.INSERT, null, null, "PaymentRequestProcessor", startTime, endTime);
+        verifyAuditLog(paymentAuditLogs.get(1), ChangeType.UPDATE, null, null, "PaymentRequestProcessor", startTime, endTime);
+
+        // Verify refunds
+        Assert.assertEquals(paymentJson.getRefunds().size(), 1);
+        final RefundJson refundJson = paymentJson.getRefunds().get(0);
+        Assert.assertEquals(refundJson.getPaymentId(), paymentJson.getPaymentId());
+        Assert.assertEquals(refundJson.getRefundAmount().compareTo(refundAmount), 0);
+        final List<AuditLogJson> refundAuditLogs = refundJson.getAuditLogs();
+        Assert.assertEquals(refundAuditLogs.size(), 3);
+        verifyAuditLog(refundAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+        verifyAuditLog(refundAuditLogs.get(1), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
+        verifyAuditLog(refundAuditLogs.get(2), ChangeType.UPDATE, reason, comment, createdBy, startTime, endTime);
+
+        // Verify chargebacks
+        Assert.assertEquals(paymentJson.getChargebacks().size(), 1);
+        final ChargebackJson chargebackJson = paymentJson.getChargebacks().get(0);
+        Assert.assertEquals(chargebackJson.getPaymentId(), paymentJson.getPaymentId());
+        Assert.assertEquals(chargebackJson.getChargebackAmount().compareTo(chargebackAmount), 0);
+        final List<AuditLogJson> chargebackAuditLogs = chargebackJson.getAuditLogs();
+        Assert.assertEquals(chargebackAuditLogs.size(), 1);
+        verifyAuditLog(chargebackAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+
+        // Verify invoices
+        Assert.assertEquals(timeline.getInvoices().size(), 2);
+        final List<AuditLogJson> firstInvoiceAuditLogs = timeline.getInvoices().get(0).getAuditLogs();
+        Assert.assertEquals(firstInvoiceAuditLogs.size(), 1);
+        verifyAuditLog(firstInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, "Transition", startTime, endTime);
+        final List<AuditLogJson> secondInvoiceAuditLogs = timeline.getInvoices().get(1).getAuditLogs();
+        Assert.assertEquals(secondInvoiceAuditLogs.size(), 1);
+        verifyAuditLog(secondInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, "Transition", startTime, endTime);
+
+        // Verify credits
+        final List<CreditJson> credits = timeline.getInvoices().get(1).getCredits();
+        Assert.assertEquals(credits.size(), 1);
+        Assert.assertEquals(credits.get(0).getCreditAmount().compareTo(creditAmount.negate()), 0);
+        final List<AuditLogJson> creditAuditLogs = credits.get(0).getAuditLogs();
+        Assert.assertEquals(creditAuditLogs.size(), 1);
+        verifyAuditLog(creditAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+
+        // Verify bundles
+        Assert.assertEquals(timeline.getBundles().size(), 1);
+        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().size(), 1);
+        Assert.assertEquals(timeline.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
+        final List<AuditLogJson> bundleAuditLogs = timeline.getBundles().get(0).getAuditLogs();
+        Assert.assertEquals(bundleAuditLogs.size(), 3);
+        verifyAuditLog(bundleAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
+        verifyAuditLog(bundleAuditLogs.get(1), ChangeType.UPDATE, null, null, "Transition", startTime, endTime);
+        verifyAuditLog(bundleAuditLogs.get(2), ChangeType.UPDATE, null, null, "Transition", startTime, endTime);
+
+        // TODO subscription events audit logs
+    }
 
-        final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        final String baseJson = response.getResponseBody();
-        final AccountTimelineJson objFromJson = mapper.readValue(baseJson, AccountTimelineJson.class);
-        assertNotNull(objFromJson);
-        log.info(baseJson);
-
-        Assert.assertEquals(objFromJson.getPayments().size(), 1);
-        Assert.assertEquals(objFromJson.getInvoices().size(), 2);
-        Assert.assertEquals(objFromJson.getBundles().size(), 1);
-        Assert.assertEquals(objFromJson.getBundles().get(0).getSubscriptions().size(), 1);
-        Assert.assertEquals(objFromJson.getBundles().get(0).getSubscriptions().get(0).getEvents().size(), 2);
+    private void verifyAuditLog(final AuditLogJson auditLogJson, final ChangeType changeType, @Nullable final String reasonCode,
+                                @Nullable final String comments, @Nullable final String changedBy,
+                                final DateTime startTime, final DateTime endTime) {
+        Assert.assertEquals(auditLogJson.getChangeType(), changeType.toString());
+        Assert.assertFalse(auditLogJson.getChangeDate().isBefore(startTime));
+        // Flaky
+        //Assert.assertFalse(auditLogJson.getChangeDate().isAfter(endTime));
+        Assert.assertEquals(auditLogJson.getReasonCode(), reasonCode);
+        Assert.assertEquals(auditLogJson.getComments(), comments);
+        Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
     }
 
     @Test(groups = "slow")
@@ -228,18 +291,17 @@ public class TestAccount extends TestJaxrsBase {
         paymentMethods = mapper.readValue(baseJson, new TypeReference<List<PaymentMethodJson>>() {});
         assertEquals(paymentMethods.size(), 1);
 
-
         //
         // DELETE DEFAULT PAYMENT METHOD (without special flag first)
         //
-        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId() ;
+        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId();
         response = doDelete(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.BAD_REQUEST.getStatusCode());
 
         //
         // RETRY TO DELETE DEFAULT PAYMENT METHOD (with special flag this time)
         //
-        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId() ;
+        uri = JaxrsResource.PAYMENT_METHODS_PATH + "/" + paymentMethodPP.getPaymentMethodId();
         queryParams = new HashMap<String, String>();
         queryParams.put(JaxrsResource.QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF, "true");
 
@@ -280,36 +342,15 @@ public class TestAccount extends TestJaxrsBase {
 
     @Test(groups = "slow")
     public void testAccountPaymentsWithRefund() throws Exception {
+        final AccountJson accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
-        //clock.setTime(new DateTime(2012, 4, 25, 0, 3, 42, 0));
-
-        final AccountJson accountJson = createAccountWithDefaultPaymentMethod("ermenehildo", "shtyrgfhwe", "ermenehildo@yahoo.com");
-        assertNotNull(accountJson);
-
-        final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), "396199");
-        assertNotNull(bundleJson);
-
-        final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
-        assertNotNull(subscriptionJson);
-
-        clock.addMonths(1);
-        crappyWaitForLackOfProperSynchonization();
-
-        String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.PAYMENTS;
-
-        Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        String baseJson = response.getResponseBody();
-        List<PaymentJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<PaymentJsonSimple>>() {});
+        // Verify payments
+        final List<PaymentJsonSimple> objFromJson = getPaymentsForAccount(accountJson.getAccountId());
         Assert.assertEquals(objFromJson.size(), 1);
 
-        uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.REFUNDS;
-        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        baseJson = response.getResponseBody();
-        List<RefundJson> objRefundFromJson = mapper.readValue(baseJson, new TypeReference<List<RefundJson>>() {});
+        // Verify refunds
+        final List<RefundJson> objRefundFromJson = getRefundsForAccount(accountJson.getAccountId());
         Assert.assertEquals(objRefundFromJson.size(), 0);
-
     }
 
     @Test(groups = "slow")
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestCredit.java b/server/src/test/java/com/ning/billing/jaxrs/TestCredit.java
index b459ce3..3668f5b 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestCredit.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestCredit.java
@@ -17,33 +17,21 @@
 package com.ning.billing.jaxrs;
 
 import java.math.BigDecimal;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.ws.rs.core.Response.Status;
 
 import org.joda.time.DateTime;
-import org.joda.time.Interval;
-import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.jaxrs.json.AccountJson;
-import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
 import com.ning.billing.jaxrs.json.CreditJson;
 import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
-import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.http.client.Response;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
 
 public class TestCredit extends TestJaxrsBase {
 
@@ -51,69 +39,22 @@ public class TestCredit extends TestJaxrsBase {
 
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
-        accountJson = createAccount(UUID.randomUUID().toString(), UUID.randomUUID().toString(), "foo@bar.com");
-        assertNotNull(accountJson);
+        accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
     }
 
     @Test(groups = "slow")
     public void testAddCreditToInvoice() throws Exception {
-        final DateTime requestedDate = clock.getUTCNow();
-        final DateTime effectiveDate = clock.getUTCNow();
-        final InvoiceJsonSimple invoice = createInvoice();
-        final CreditJson input = new CreditJson(BigDecimal.TEN, invoice.getInvoiceId(), UUID.randomUUID().toString(),
-                                                requestedDate, effectiveDate,
-                                                UUID.randomUUID().toString(), accountJson.getAccountId(),
-                                                null);
-        final String jsonInput = mapper.writeValueAsString(input);
+        final InvoiceJsonSimple invoice = getInvoicesForAccount(accountJson.getAccountId()).get(1);
 
-        // Create the credit
-        Response response = doPost(JaxrsResource.CREDITS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode(), response.getResponseBody());
-
-        final String location = response.getHeader("Location");
-        assertNotNull(location);
-
-        // Retrieves by Id based on Location returned
-        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
-        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        final DateTime effectiveDate = clock.getUTCNow();
+        final BigDecimal creditAmount = BigDecimal.ONE;
+        final CreditJson objFromJson = createCreditForInvoice(accountJson.getAccountId(), invoice.getInvoiceId(),
+                                                              creditAmount, clock.getUTCNow(), effectiveDate);
 
         // We can't just compare the object via .equals() due e.g. to the invoice id
-        final CreditJson objFromJson = mapper.readValue(response.getResponseBody(), CreditJson.class);
-        assertEquals(objFromJson.getAccountId(), input.getAccountId());
-        assertEquals(objFromJson.getCreditAmount().compareTo(input.getCreditAmount()), 0);
-        assertEquals(objFromJson.getEffectiveDate().toLocalDate().compareTo(input.getEffectiveDate().toLocalDate()), 0);
-    }
-
-    private InvoiceJsonSimple createInvoice() throws Exception {
-        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
-        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
-
-        final AccountJson accountJson = createAccountWithDefaultPaymentMethod("poupou", "qhddffrwe", "poupou@yahoo.com");
-        assertNotNull(accountJson);
-
-        final BundleJsonNoSubscriptions bundleJson = createBundle(accountJson.getAccountId(), "9967599");
-        assertNotNull(bundleJson);
-
-        final SubscriptionJsonNoEvents subscriptionJson = createSubscription(bundleJson.getBundleId(), "Shotgun", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(), true);
-        assertNotNull(subscriptionJson);
-
-        // MOVE AFTER TRIAL
-        final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(30));
-        clock.addDeltaFromReality(it.toDurationMillis());
-        crappyWaitForLackOfProperSynchonization();
-
-        final String uri = JaxrsResource.INVOICES_PATH;
-        final Map<String, String> queryParams = new HashMap<String, String>();
-        queryParams.put(JaxrsResource.QUERY_ACCOUNT_ID, accountJson.getAccountId());
-
-        final Response response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        final String baseJson = response.getResponseBody();
-        final List<InvoiceJsonSimple> objFromJson = mapper.readValue(baseJson, new TypeReference<List<InvoiceJsonSimple>>() {});
-        assertNotNull(objFromJson);
-        assertEquals(objFromJson.size(), 2);
-
-        return objFromJson.get(1);
+        assertEquals(objFromJson.getAccountId(), accountJson.getAccountId());
+        assertEquals(objFromJson.getCreditAmount().compareTo(creditAmount), 0);
+        assertEquals(objFromJson.getEffectiveDate().toLocalDate().compareTo(effectiveDate.toLocalDate()), 0);
     }
 
     @Test(groups = "slow")
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 1ab8f18..c8941c2 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -64,8 +64,11 @@ import com.ning.billing.invoice.api.InvoiceNotifier;
 import com.ning.billing.invoice.glue.DefaultInvoiceModule;
 import com.ning.billing.invoice.notification.NullInvoiceNotifier;
 import com.ning.billing.jaxrs.json.AccountJson;
+import com.ning.billing.jaxrs.json.AccountTimelineJson;
 import com.ning.billing.jaxrs.json.BillCycleDayJson;
 import com.ning.billing.jaxrs.json.BundleJsonNoSubscriptions;
+import com.ning.billing.jaxrs.json.ChargebackJson;
+import com.ning.billing.jaxrs.json.CreditJson;
 import com.ning.billing.jaxrs.json.InvoiceItemJsonSimple;
 import com.ning.billing.jaxrs.json.InvoiceJsonSimple;
 import com.ning.billing.jaxrs.json.InvoiceJsonWithItems;
@@ -75,6 +78,7 @@ import com.ning.billing.jaxrs.json.PaymentMethodJson.PaymentMethodPluginDetailJs
 import com.ning.billing.jaxrs.json.PaymentMethodJson.PaymentMethodProperties;
 import com.ning.billing.jaxrs.json.RefundJson;
 import com.ning.billing.jaxrs.json.SubscriptionJsonNoEvents;
+import com.ning.billing.jaxrs.resources.AccountResource;
 import com.ning.billing.jaxrs.resources.JaxrsResource;
 import com.ning.billing.junction.glue.DefaultJunctionModule;
 import com.ning.billing.payment.glue.PaymentModule;
@@ -139,9 +143,9 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
     protected TestApiListener busHandler;
 
     // Context information to be passed around
-    private static final String createdBy = "Toto";
-    private static final String reason = "i am god";
-    private static final String comment = "no comment";
+    protected static final String createdBy = "Toto";
+    protected static final String reason = "i am god";
+    protected static final String comment = "no comment";
 
     public static void loadSystemPropertiesFromClasspath(final String resource) {
         final URL url = TestJaxrsBase.class.getResource(resource);
@@ -359,6 +363,27 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
         return objFromJson;
     }
 
+    protected AccountTimelineJson getAccountTimeline(final String accountId) throws Exception {
+        return doGetAccountTimeline(accountId, false);
+    }
+
+    protected AccountTimelineJson getAccountTimelineWithAudits(final String accountId) throws Exception {
+        return doGetAccountTimeline(accountId, true);
+    }
+
+    private AccountTimelineJson doGetAccountTimeline(final String accountId, final Boolean withAudits) throws Exception {
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + JaxrsResource.TIMELINE;
+
+        final Response response = doGet(uri, ImmutableMap.<String, String>of(JaxrsResource.QUERY_AUDIT, withAudits.toString()), DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        final String baseJson = response.getResponseBody();
+        final AccountTimelineJson objFromJson = mapper.readValue(baseJson, AccountTimelineJson.class);
+        assertNotNull(objFromJson);
+
+        return objFromJson;
+    }
+
     protected AccountJson createAccountWithDefaultPaymentMethod(final String name, final String key, final String email) throws Exception {
 
         final AccountJson input = createAccount(name, key, email);
@@ -383,6 +408,10 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
         return objFromJson;
     }
 
+    protected AccountJson createAccount() throws Exception {
+        return createAccount(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString().substring(0, 5) + '@' + UUID.randomUUID().toString().substring(0, 5));
+    }
+
     protected AccountJson createAccount(final String name, final String key, final String email) throws Exception {
         final AccountJson input = getAccountJson(name, key, email);
         String baseJson = mapper.writeValueAsString(input);
@@ -402,6 +431,20 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
         return objFromJson;
     }
 
+    protected AccountJson updateAccount(final String accountId, final AccountJson newInput) throws Exception {
+        final String baseJson = mapper.writeValueAsString(newInput);
+
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountId;
+        final Response response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        final String retrievedJson = response.getResponseBody();
+        final AccountJson objFromJson = mapper.readValue(retrievedJson, AccountJson.class);
+        assertNotNull(objFromJson);
+
+        return objFromJson;
+    }
+
     protected BundleJsonNoSubscriptions createBundle(final String accountId, final String key) throws Exception {
         final BundleJsonNoSubscriptions input = new BundleJsonNoSubscriptions(null, accountId, key, null, null);
         String baseJson = mapper.writeValueAsString(input);
@@ -696,9 +739,42 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
     }
 
     //
+    // CHARGEBACKS
+    //
+
+    protected ChargebackJson createChargeBack(final String paymentId, final BigDecimal chargebackAmount) throws IOException {
+        final ChargebackJson input = new ChargebackJson(null, null, chargebackAmount, paymentId, null, null);
+        final String jsonInput = mapper.writeValueAsString(input);
+
+        // Create the chargeback
+        final Response response = doPost(JaxrsResource.CHARGEBACKS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode(), response.getResponseBody());
+
+        // Find the chargeback by location
+        final String location = response.getHeader("Location");
+        assertNotNull(location);
+        final Response responseByLocation = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(responseByLocation.getStatusCode(), Status.OK.getStatusCode());
+
+        return mapper.readValue(responseByLocation.getResponseBody(), ChargebackJson.class);
+    }
+
+    //
     // REFUNDS
     //
 
+    protected List<RefundJson> getRefundsForAccount(final String accountId) throws IOException {
+        final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + JaxrsResource.REFUNDS;
+        final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        final String baseJson = response.getResponseBody();
+        final List<RefundJson> refunds = mapper.readValue(baseJson, new TypeReference<List<RefundJson>>() {});
+        assertNotNull(refunds);
+
+        return refunds;
+    }
+
     protected List<RefundJson> getRefundsForPayment(final String paymentId) throws IOException {
         final String uri = JaxrsResource.PAYMENTS_PATH + "/" + paymentId + "/" + JaxrsResource.REFUNDS;
         final Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
@@ -759,6 +835,37 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
     }
 
     //
+    // CREDITS
+    //
+
+    protected CreditJson createCreditForAccount(final String accountId, final BigDecimal creditAmount,
+                                                final DateTime requestedDate, final DateTime effectiveDate) throws IOException {
+        return createCreditForInvoice(accountId, null, creditAmount, requestedDate, effectiveDate);
+    }
+
+    protected CreditJson createCreditForInvoice(final String accountId, final String invoiceId, final BigDecimal creditAmount,
+                                                final DateTime requestedDate, final DateTime effectiveDate) throws IOException {
+        final CreditJson input = new CreditJson(creditAmount, invoiceId, UUID.randomUUID().toString(),
+                                                requestedDate, effectiveDate,
+                                                UUID.randomUUID().toString(), accountId,
+                                                null);
+        final String jsonInput = mapper.writeValueAsString(input);
+
+        // Create the credit
+        Response response = doPost(JaxrsResource.CREDITS_PATH, jsonInput, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode(), response.getResponseBody());
+
+        final String location = response.getHeader("Location");
+        assertNotNull(location);
+
+        // Retrieves by Id based on Location returned
+        response = doGetWithUrl(location, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        return mapper.readValue(response.getResponseBody(), CreditJson.class);
+    }
+
+    //
     // HTTP CLIENT HELPERS
     //
     protected Response doPost(final String uri, @Nullable final String body, final Map<String, String> queryParams, final int timeoutSec) {
@@ -849,6 +956,10 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
         return builder;
     }
 
+    protected AccountJson getAccountJson() {
+        return getAccountJson(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString().substring(0, 5) + '@' + UUID.randomUUID().toString().substring(0, 5));
+    }
+
     public AccountJson getAccountJson(final String name, final String externalKey, final String email) {
         final String accountId = UUID.randomUUID().toString();
         final int length = 4;
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 6af61ea..14ca318 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,13 +16,31 @@
 
 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.inject.Inject;
 
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.Refund;
 import com.ning.billing.util.api.AuditUserApi;
 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.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.dao.AuditDao;
 import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.dao.TableName;
@@ -39,13 +57,67 @@ public class DefaultAuditUserApi implements AuditUserApi {
     }
 
     @Override
+    public AuditLogsForBundles getAuditLogsForBundles(final List<SubscriptionBundle> bundles) {
+        final Map<UUID, List<AuditLog>> bundlesAuditLogs = new HashMap<UUID, List<AuditLog>>();
+        for (final SubscriptionBundle bundle : bundles) {
+            bundlesAuditLogs.put(bundle.getId(), getAuditLogs(bundle.getId(), ObjectType.BUNDLE));
+        }
+
+        return new DefaultAuditLogsForBundles(bundlesAuditLogs);
+    }
+
+    @Override
+    public AuditLogsForInvoicePayments getAuditLogsForInvoicePayments(final List<InvoicePayment> invoicePayments) {
+        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));
+        }
+
+        return new DefaultAuditLogsForInvoicePayments(invoicePaymentsAuditLogs);
+    }
+
+    @Override
+    public AuditLogsForRefunds getAuditLogsForRefunds(final List<Refund> refunds) {
+        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));
+        }
+
+        return new DefaultAuditLogsForRefunds(refundsAuditLogs);
+    }
+
+    @Override
+    public AuditLogsForPayments getAuditLogsForPayments(final List<Payment> payments) {
+        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));
+        }
+
+        return new DefaultAuditLogsForPayments(paymentsAuditLogs);
+    }
+
+    @Override
+    public AuditLogsForInvoices getAuditLogsForInvoices(final List<Invoice> invoices) {
+        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));
+            for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
+                invoiceItemsAuditLogs.put(invoiceItem.getId(), getAuditLogs(invoiceItem.getId(), ObjectType.INVOICE_ITEM));
+            }
+        }
+
+        return new DefaultAuditLogsForInvoices(invoiceAuditLogs, invoiceItemsAuditLogs);
+    }
+
+    @Override
     public List<AuditLog> getAuditLogs(final UUID objectId, final ObjectType objectType) {
         final TableName tableName = getTableNameFromObjectType(objectType);
         if (tableName == null) {
             return ImmutableList.<AuditLog>of();
         }
 
-        return auditDao.getAuditLogsForRecordId(tableName, objectId);
+        return auditDao.getAuditLogsForId(tableName, objectId);
     }
 
     private TableName getTableNameFromObjectType(final ObjectType objectType) {
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 224b78b..4244ddc 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
@@ -24,5 +24,5 @@ import com.ning.billing.util.dao.TableName;
 
 public interface AuditDao {
 
-    public List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final UUID objectId);
+    public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId);
 }
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 6d4a827..73f82ec 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,6 +16,7 @@
 
 package com.ning.billing.util.audit.dao;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
@@ -39,7 +40,16 @@ public class DefaultAuditDao implements AuditDao {
     }
 
     @Override
-    public List<AuditLog> getAuditLogsForRecordId(final TableName tableName, final UUID objectId) {
+    public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId) {
+        if (tableName.hasHistoryTable()) {
+            return doGetAuditLogsViaHistoryForId(tableName, objectId);
+        } else {
+            return doGetAuditLogsForId(tableName, objectId);
+        }
+    }
+
+    private List<AuditLog> doGetAuditLogsForId(final TableName tableName, final UUID objectId) {
+        // Look at the table and gather all record_id for that objectId
         final Long recordId = auditSqlDao.getRecordIdForTable(tableName.getTableName().toLowerCase(), objectId.toString());
         if (recordId == null) {
             return ImmutableList.<AuditLog>of();
@@ -47,4 +57,21 @@ public class DefaultAuditDao implements AuditDao {
             return auditSqlDao.getAuditLogsForRecordId(tableName, recordId);
         }
     }
+
+    private List<AuditLog> doGetAuditLogsViaHistoryForId(final TableName tableName, final UUID objectId) {
+        final List<AuditLog> auditLogs = new ArrayList<AuditLog>();
+
+        // Look at the history table and gather all the history_record_id for that objectId
+        final List<Long> recordIds = auditSqlDao.getHistoryRecordIdsForTable(tableName.getHistoryTableName().getTableName().toLowerCase(),
+                                                                             objectId.toString());
+        if (recordIds == null) {
+            return auditLogs;
+        } else {
+            for (final Long recordId : recordIds) {
+                auditLogs.addAll(auditSqlDao.getAuditLogsForRecordId(tableName.getHistoryTableName(), recordId));
+            }
+
+            return auditLogs;
+        }
+    }
 }
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForBundles.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForBundles.java
new file mode 100644
index 0000000..4e35a2d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForBundles.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class DefaultAuditLogsForBundles implements AuditLogsForBundles {
+
+    private final Map<UUID, List<AuditLog>> bundlesAuditLogs;
+
+    public DefaultAuditLogsForBundles(final Map<UUID, List<AuditLog>> bundlesAuditLogs) {
+        this.bundlesAuditLogs = bundlesAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getBundlesAuditLogs() {
+        return bundlesAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getSubscriptionEventsAuditLogs() {
+        // TODO
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultAuditLogsForBundles");
+        sb.append("{bundlesAuditLogs=").append(bundlesAuditLogs);
+        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 DefaultAuditLogsForBundles that = (DefaultAuditLogsForBundles) o;
+
+        if (bundlesAuditLogs != null ? !bundlesAuditLogs.equals(that.bundlesAuditLogs) : that.bundlesAuditLogs != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return bundlesAuditLogs != null ? bundlesAuditLogs.hashCode() : 0;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoicePayments.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoicePayments.java
new file mode 100644
index 0000000..06c3935
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoicePayments.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class DefaultAuditLogsForInvoicePayments implements AuditLogsForInvoicePayments {
+
+    private final Map<UUID, List<AuditLog>> invoicePaymentsAuditLogs;
+
+    public DefaultAuditLogsForInvoicePayments(final Map<UUID, List<AuditLog>> invoicePaymentsAuditLogs) {
+        this.invoicePaymentsAuditLogs = invoicePaymentsAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getInvoicePaymentsAuditLogs() {
+        return invoicePaymentsAuditLogs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultAuditLogsForInvoicePayments");
+        sb.append("{invoicePaymentsAuditLogs=").append(invoicePaymentsAuditLogs);
+        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 DefaultAuditLogsForInvoicePayments that = (DefaultAuditLogsForInvoicePayments) o;
+
+        if (invoicePaymentsAuditLogs != null ? !invoicePaymentsAuditLogs.equals(that.invoicePaymentsAuditLogs) : that.invoicePaymentsAuditLogs != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return invoicePaymentsAuditLogs != null ? invoicePaymentsAuditLogs.hashCode() : 0;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoices.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoices.java
new file mode 100644
index 0000000..b094dcf
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForInvoices.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class DefaultAuditLogsForInvoices implements AuditLogsForInvoices {
+
+    private final Map<UUID, List<AuditLog>> invoiceAuditLogs;
+    private final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs;
+
+    public DefaultAuditLogsForInvoices(final Map<UUID, List<AuditLog>> invoiceAuditLogs, final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs) {
+        this.invoiceAuditLogs = invoiceAuditLogs;
+        this.invoiceItemsAuditLogs = invoiceItemsAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getInvoiceAuditLogs() {
+        return invoiceAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getInvoiceItemsAuditLogs() {
+        return invoiceItemsAuditLogs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultAuditLogsForInvoices");
+        sb.append("{invoiceAuditLogs=").append(invoiceAuditLogs);
+        sb.append(", invoiceItemsAuditLogs=").append(invoiceItemsAuditLogs);
+        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 DefaultAuditLogsForInvoices that = (DefaultAuditLogsForInvoices) o;
+
+        if (invoiceAuditLogs != null ? !invoiceAuditLogs.equals(that.invoiceAuditLogs) : that.invoiceAuditLogs != null) {
+            return false;
+        }
+        if (invoiceItemsAuditLogs != null ? !invoiceItemsAuditLogs.equals(that.invoiceItemsAuditLogs) : that.invoiceItemsAuditLogs != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = invoiceAuditLogs != null ? invoiceAuditLogs.hashCode() : 0;
+        result = 31 * result + (invoiceItemsAuditLogs != null ? invoiceItemsAuditLogs.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForPayments.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForPayments.java
new file mode 100644
index 0000000..062813b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForPayments.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class DefaultAuditLogsForPayments implements AuditLogsForPayments {
+
+    private final Map<UUID, List<AuditLog>> paymentsAuditLogs;
+
+    public DefaultAuditLogsForPayments(final Map<UUID, List<AuditLog>> paymentsAuditLogs) {
+        this.paymentsAuditLogs = paymentsAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getPaymentsAuditLogs() {
+        return paymentsAuditLogs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultAuditLogsForPayments");
+        sb.append("{paymentsAuditLogs=").append(paymentsAuditLogs);
+        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 DefaultAuditLogsForPayments that = (DefaultAuditLogsForPayments) o;
+
+        if (paymentsAuditLogs != null ? !paymentsAuditLogs.equals(that.paymentsAuditLogs) : that.paymentsAuditLogs != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return paymentsAuditLogs != null ? paymentsAuditLogs.hashCode() : 0;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForRefunds.java b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForRefunds.java
new file mode 100644
index 0000000..1cfd520
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/audit/DefaultAuditLogsForRefunds.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class DefaultAuditLogsForRefunds implements AuditLogsForRefunds {
+
+    private final Map<UUID, List<AuditLog>> refundsAuditLogs;
+
+    public DefaultAuditLogsForRefunds(final Map<UUID, List<AuditLog>> refundsAuditLogs) {
+        this.refundsAuditLogs = refundsAuditLogs;
+    }
+
+    @Override
+    public Map<UUID, List<AuditLog>> getRefundsAuditLogs() {
+        return refundsAuditLogs;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultAuditLogsForRefunds");
+        sb.append("{refundsAuditLogs=").append(refundsAuditLogs);
+        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 DefaultAuditLogsForRefunds that = (DefaultAuditLogsForRefunds) o;
+
+        if (refundsAuditLogs != null ? !refundsAuditLogs.equals(that.refundsAuditLogs) : that.refundsAuditLogs != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return refundsAuditLogs != null ? refundsAuditLogs.hashCode() : 0;
+    }
+}
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 58a08c6..341b448 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
@@ -54,6 +54,10 @@ public interface AuditSqlDao {
                                     @Bind("id") final String id);
 
     @SqlQuery
+    public List<Long> getHistoryRecordIdsForTable(@Define("tableName") final String tableName,
+                                                  @Bind("id") final String id);
+
+    @SqlQuery
     public Long getHistoryRecordId(@Bind("recordId") final Long recordId);
 
 }
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 4dedcd0..7aec216 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -19,30 +19,50 @@ package com.ning.billing.util.dao;
 import javax.annotation.Nullable;
 
 public enum TableName {
-    ACCOUNT("accounts", ObjectType.ACCOUNT),
-    ACCOUNT_HISTORY("account_history", null),
-    ACCOUNT_EMAIL_HISTORY("account_email_history", ObjectType.ACCOUNT_EMAIL),
+    ACCOUNT_HISTORY("account_history"),
+    ACCOUNT("accounts", ObjectType.ACCOUNT, ACCOUNT_HISTORY),
+    ACCOUNT_EMAIL_HISTORY("account_email_history"),
+    ACCOUNT_EMAIL("account_emails", ObjectType.ACCOUNT_EMAIL, ACCOUNT_EMAIL_HISTORY),
     BUNDLES("bundles", ObjectType.BUNDLE),
-    CUSTOM_FIELD_HISTORY("custom_field_history", null),
+    CUSTOM_FIELD_HISTORY("custom_field_history"),
+    CUSTOM_FIELD("custom_fields", CUSTOM_FIELD_HISTORY),
     INVOICE_ITEMS("invoice_items", ObjectType.INVOICE_ITEM),
     INVOICE_PAYMENTS("invoice_payments", ObjectType.INVOICE_PAYMENT),
     INVOICES("invoices", ObjectType.INVOICE),
-    PAYMENT_ATTEMPTS("payment_attempts", null),
-    PAYMENT_HISTORY("payment_history", null),
-    PAYMENTS("payments", ObjectType.PAYMENT),
-    PAYMENT_METHODS("payment_methods", ObjectType.PAYMENT_METHOD),
+    PAYMENT_ATTEMPT_HISTORY("payment_attempt_history"),
+    PAYMENT_ATTEMPTS("payment_attempts", PAYMENT_ATTEMPT_HISTORY),
+    PAYMENT_HISTORY("payment_history"),
+    PAYMENTS("payments", ObjectType.PAYMENT, PAYMENT_HISTORY),
+    PAYMENT_METHOD_HISTORY("payment_method_history"),
+    PAYMENT_METHODS("payment_methods", ObjectType.PAYMENT_METHOD, PAYMENT_METHOD_HISTORY),
     SUBSCRIPTIONS("subscriptions", ObjectType.SUBSCRIPTION),
-    SUBSCRIPTION_EVENTS("subscription_events", null),
-    REFUNDS("refunds", ObjectType.REFUND),
+    SUBSCRIPTION_EVENTS("subscription_events"),
+    REFUND_HISTORY("refund_history"),
+    REFUNDS("refunds", ObjectType.REFUND, REFUND_HISTORY),
     TAG_DEFINITIONS("tag_definitions", ObjectType.TAG_DEFINITION),
-    TAG_HISTORY("tag_history", null);
+    TAG_HISTORY("tag_history"),
+    TAG("tags", TAG_HISTORY);
 
     private final String tableName;
     private final ObjectType objectType;
+    private final TableName historyTableName;
 
-    TableName(final String tableName, @Nullable final ObjectType objectType) {
+    TableName(final String tableName, @Nullable final ObjectType objectType, @Nullable final TableName historyTableName) {
         this.tableName = tableName;
         this.objectType = objectType;
+        this.historyTableName = historyTableName;
+    }
+
+    TableName(final String tableName, @Nullable final ObjectType objectType) {
+        this(tableName, objectType, null);
+    }
+
+    TableName(final String tableName, @Nullable final TableName historyTableName) {
+        this(tableName, null, historyTableName);
+    }
+
+    TableName(final String tableName) {
+        this(tableName, null, null);
     }
 
     public String getTableName() {
@@ -52,4 +72,12 @@ public enum TableName {
     public ObjectType getObjectType() {
         return objectType;
     }
+
+    public TableName getHistoryTableName() {
+        return historyTableName;
+    }
+
+    public boolean hasHistoryTable() {
+        return historyTableName != null;
+    }
 }
diff --git a/util/src/main/resources/com/ning/billing/util/dao/AuditSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/dao/AuditSqlDao.sql.stg
index b83b952..c8d64bb 100644
--- a/util/src/main/resources/com/ning/billing/util/dao/AuditSqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/dao/AuditSqlDao.sql.stg
@@ -29,3 +29,9 @@ getRecordIdForTable(tableName) ::= <<
   WHERE id = :id
   LIMIT 1
 >>
+
+getHistoryRecordIdsForTable(tableName) ::= <<
+  SELECT history_record_id record_id
+  FROM <tableName>
+  WHERE id = :id
+>>
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
new file mode 100644
index 0000000..9c58442
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/api/TestDefaultAuditUserApi.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2010-2012 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.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.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
+import com.ning.billing.payment.api.Payment;
+import com.ning.billing.payment.api.Refund;
+import com.ning.billing.util.api.AuditUserApi;
+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.ObjectType;
+import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.Entity;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultAuditUserApi extends AuditLogsTestBase {
+
+    private final List<AuditLog> auditLogs = ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog(), createAuditLog(), createAuditLog());
+    private final List<UUID> objectIds = ImmutableList.<UUID>of(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID());
+
+    private AuditUserApi auditUserApi;
+
+    @BeforeTest(groups = "fast")
+    public void setupAuditLogs() throws Exception {
+        final MockAuditDao auditDao = new MockAuditDao();
+        for (final TableName tableName : TableName.values()) {
+            for (final UUID objectId : objectIds) {
+                for (final AuditLog auditLog : auditLogs) {
+                    auditDao.addAuditLogForId(tableName, objectId, auditLog);
+                }
+            }
+        }
+
+        auditUserApi = new DefaultAuditUserApi(auditDao);
+    }
+
+    @Test(groups = "fast")
+    public void testForBundles() throws Exception {
+        final List<SubscriptionBundle> bundles = createMocks(SubscriptionBundle.class);
+
+        // TODO check SubscriptionEventsAuditLogs
+
+        final AuditLogsForBundles auditLogsForBundles = auditUserApi.getAuditLogsForBundles(bundles);
+        verifyAuditLogs(auditLogsForBundles.getBundlesAuditLogs());
+    }
+
+    @Test(groups = "fast")
+    public void testForInvoicePayments() throws Exception {
+        final List<InvoicePayment> invoicePayments = createMocks(InvoicePayment.class);
+
+        final AuditLogsForInvoicePayments auditLogsForInvoicePayments = auditUserApi.getAuditLogsForInvoicePayments(invoicePayments);
+        verifyAuditLogs(auditLogsForInvoicePayments.getInvoicePaymentsAuditLogs());
+    }
+
+    @Test(groups = "fast")
+    public void testForRefunds() throws Exception {
+        final List<Refund> refunds = createMocks(Refund.class);
+
+        final AuditLogsForRefunds auditLogsForRefunds = auditUserApi.getAuditLogsForRefunds(refunds);
+        verifyAuditLogs(auditLogsForRefunds.getRefundsAuditLogs());
+    }
+
+    @Test(groups = "fast")
+    public void testForPayments() throws Exception {
+        final List<Payment> payments = createMocks(Payment.class);
+
+        final AuditLogsForPayments auditLogsForPayments = auditUserApi.getAuditLogsForPayments(payments);
+        verifyAuditLogs(auditLogsForPayments.getPaymentsAuditLogs());
+    }
+
+    @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);
+        }
+
+        final AuditLogsForInvoices auditLogsForInvoices = auditUserApi.getAuditLogsForInvoices(invoices);
+        verifyAuditLogs(auditLogsForInvoices.getInvoiceAuditLogs());
+        verifyAuditLogs(auditLogsForInvoices.getInvoiceItemsAuditLogs());
+    }
+
+    @Test(groups = "fast")
+    public void testForObject() throws Exception {
+        for (final ObjectType objectType : ObjectType.values()) {
+            for (final UUID objectId : objectIds) {
+                Assert.assertEquals(auditUserApi.getAuditLogs(objectId, objectType), auditLogs);
+            }
+        }
+    }
+
+    private void verifyAuditLogs(final Map<UUID, List<AuditLog>> objectsAuditLogs) {
+        Assert.assertEquals(objectsAuditLogs.size(), objectIds.size());
+        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/AuditLogsTestBase.java b/util/src/test/java/com/ning/billing/util/audit/AuditLogsTestBase.java
new file mode 100644
index 0000000..6a54943
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/AuditLogsTestBase.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.UUID;
+
+import org.mockito.Mockito;
+
+import com.ning.billing.util.ChangeType;
+import com.ning.billing.util.UtilTestSuite;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public abstract class AuditLogsTestBase extends UtilTestSuite {
+
+    private final Clock clock = new ClockMock();
+
+    protected ImmutableMap<UUID, List<AuditLog>> createAuditLogsAssociation() {
+        final UUID id1 = UUID.randomUUID();
+        final UUID id2 = UUID.randomUUID();
+        final UUID id3 = UUID.randomUUID();
+        return ImmutableMap.<UUID, List<AuditLog>>of(id1, ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog()),
+                                                     id2, ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog()),
+                                                     id3, ImmutableList.<AuditLog>of(createAuditLog(), createAuditLog()));
+    }
+
+    protected AuditLog createAuditLog() {
+        final AuditLog auditLog = Mockito.mock(AuditLog.class);
+        Mockito.when(auditLog.getCreatedDate()).thenReturn(clock.getUTCNow());
+        Mockito.when(auditLog.getReasonCode()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getUserName()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getUserToken()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getComment()).thenReturn(UUID.randomUUID().toString());
+        Mockito.when(auditLog.getChangeType()).thenReturn(ChangeType.DELETE);
+
+        return auditLog;
+    }
+}
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
new file mode 100644
index 0000000..7af195c
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/dao/MockAuditDao.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2012 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.dao;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.util.audit.AuditLog;
+import com.ning.billing.util.dao.TableName;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+public class MockAuditDao implements AuditDao {
+
+    private final Map<TableName, Map<UUID, List<AuditLog>>> auditLogsForTables = new HashMap<TableName, Map<UUID, List<AuditLog>>>();
+
+    public synchronized void addAuditLogForId(final TableName tableName, final UUID objectId, final AuditLog auditLog) {
+        addAuditLogsForId(tableName, objectId, ImmutableList.<AuditLog>of(auditLog));
+    }
+
+    public synchronized void addAuditLogsForId(final TableName tableName, final UUID objectId, final List<AuditLog> auditLogs) {
+        if (auditLogsForTables.get(tableName) == null) {
+            auditLogsForTables.put(tableName, new HashMap<UUID, List<AuditLog>>());
+        }
+
+        if (auditLogsForTables.get(tableName).get(objectId) == null) {
+            auditLogsForTables.get(tableName).put(objectId, new ArrayList<AuditLog>());
+        }
+
+        auditLogsForTables.get(tableName).get(objectId).addAll(auditLogs);
+    }
+
+    @Override
+    public List<AuditLog> getAuditLogsForId(final TableName tableName, final UUID objectId) {
+        final Map<UUID, List<AuditLog>> auditLogsForTableName = auditLogsForTables.get(tableName);
+        if (auditLogsForTableName == null) {
+            return ImmutableList.<AuditLog>of();
+        }
+
+        final List<AuditLog> auditLogsForObjectId = auditLogsForTableName.get(objectId);
+        return Objects.firstNonNull(auditLogsForObjectId, ImmutableList.<AuditLog>of());
+    }
+}
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 8ce34d0..e4abf3c 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
@@ -18,6 +18,7 @@ package com.ning.billing.util.audit.dao;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.skife.jdbi.v2.Handle;
@@ -30,6 +31,8 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.util.ChangeType;
 import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
+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.bus.Bus;
 import com.ning.billing.util.callcontext.CallContext;
@@ -41,6 +44,7 @@ import com.ning.billing.util.dao.ObjectType;
 import com.ning.billing.util.dao.TableName;
 import com.ning.billing.util.glue.AuditModule;
 import com.ning.billing.util.tag.MockTagStoreModuleSql;
+import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.TagDefinition;
 import com.ning.billing.util.tag.dao.AuditedTagDao;
 import com.ning.billing.util.tag.dao.TagDefinitionDao;
@@ -69,6 +73,7 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
     private IDBI dbi;
 
     private CallContext context;
+    private UUID tagId;
 
     @BeforeClass(groups = "slow")
     public void setup() throws IOException {
@@ -82,18 +87,44 @@ public class TestDefaultAuditDao extends UtilTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testRetrieveAudits() throws Exception {
-        final TagDefinition defYo = tagDefinitionDao.create("yo", "defintion yo", context);
-
-        // Create a tag
-        tagDao.insertTag(UUID.randomUUID(), ObjectType.ACCOUNT, defYo.getId(), context);
+    public void testRetrieveAuditsDirectly() throws Exception {
+        addTag();
 
         // Verify we get an audit entry for the tag_history table
         final Handle handle = dbi.open();
         final String tagHistoryString = (String) handle.select("select id from tag_history limit 1").get(0).get("id");
         handle.close();
 
-        final List<AuditLog> auditLogs = auditDao.getAuditLogsForRecordId(TableName.TAG_HISTORY, UUID.fromString(tagHistoryString));
+        final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG_HISTORY, UUID.fromString(tagHistoryString));
+        verifyAuditLogsForTag(auditLogs);
+    }
+
+    @Test(groups = "slow")
+    public void testRetrieveAuditsViaHistory() throws Exception {
+        addTag();
+
+        final List<AuditLog> auditLogs = auditDao.getAuditLogsForId(TableName.TAG, tagId);
+        verifyAuditLogsForTag(auditLogs);
+    }
+
+    private void addTag() throws TagDefinitionApiException, TagApiException {
+        // Create a tag definition
+        final TagDefinition tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 5),
+                                                                    UUID.randomUUID().toString().substring(0, 5),
+                                                                    context);
+        Assert.assertEquals(tagDefinitionDao.getById(tagDefinition.getId()), tagDefinition);
+
+        // Create a tag
+        final UUID objectId = UUID.randomUUID();
+        tagDao.insertTag(objectId, ObjectType.ACCOUNT, tagDefinition.getId(), context);
+        final Map<String, Tag> tags = tagDao.loadEntities(objectId, ObjectType.ACCOUNT);
+        Assert.assertEquals(tags.size(), 1);
+        final Tag tag = tags.values().iterator().next();
+        Assert.assertEquals(tag.getTagDefinitionId(), tagDefinition.getId());
+        tagId = tag.getId();
+    }
+
+    private void verifyAuditLogsForTag(final List<AuditLog> auditLogs) {
         Assert.assertEquals(auditLogs.size(), 1);
         Assert.assertEquals(auditLogs.get(0).getUserToken(), context.getUserToken().toString());
         Assert.assertEquals(auditLogs.get(0).getChangeType(), ChangeType.INSERT);
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForBundles.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForBundles.java
new file mode 100644
index 0000000..5ba4a40
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForBundles.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestDefaultAuditLogsForBundles extends AuditLogsTestBase {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final Map<UUID, List<AuditLog>> bundlesAuditLogs = createAuditLogsAssociation();
+        Assert.assertEquals(new DefaultAuditLogsForBundles(bundlesAuditLogs).getBundlesAuditLogs(), bundlesAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForBundles(createAuditLogsAssociation()).getBundlesAuditLogs(), bundlesAuditLogs);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoicePayments.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoicePayments.java
new file mode 100644
index 0000000..2f0c6e4
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoicePayments.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestDefaultAuditLogsForInvoicePayments extends AuditLogsTestBase {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final Map<UUID, List<AuditLog>> invoicePaymentsAuditLogs = createAuditLogsAssociation();
+        Assert.assertEquals(new DefaultAuditLogsForInvoicePayments(invoicePaymentsAuditLogs).getInvoicePaymentsAuditLogs(), invoicePaymentsAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForInvoicePayments(createAuditLogsAssociation()).getInvoicePaymentsAuditLogs(), invoicePaymentsAuditLogs);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoices.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoices.java
new file mode 100644
index 0000000..799c716
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForInvoices.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestDefaultAuditLogsForInvoices extends AuditLogsTestBase {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final Map<UUID, List<AuditLog>> invoicesAuditLogs = createAuditLogsAssociation();
+        final Map<UUID, List<AuditLog>> invoiceItemsAuditLogs = createAuditLogsAssociation();
+        Assert.assertEquals(new DefaultAuditLogsForInvoices(invoicesAuditLogs, invoiceItemsAuditLogs).getInvoiceAuditLogs(), invoicesAuditLogs);
+        Assert.assertEquals(new DefaultAuditLogsForInvoices(invoicesAuditLogs, invoiceItemsAuditLogs).getInvoiceItemsAuditLogs(), invoiceItemsAuditLogs);
+
+        Assert.assertNotEquals(new DefaultAuditLogsForInvoices(createAuditLogsAssociation(), invoiceItemsAuditLogs).getInvoiceAuditLogs(), invoicesAuditLogs);
+        Assert.assertEquals(new DefaultAuditLogsForInvoices(createAuditLogsAssociation(), invoiceItemsAuditLogs).getInvoiceItemsAuditLogs(), invoiceItemsAuditLogs);
+        Assert.assertEquals(new DefaultAuditLogsForInvoices(invoicesAuditLogs, createAuditLogsAssociation()).getInvoiceAuditLogs(), invoicesAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForInvoices(invoicesAuditLogs, createAuditLogsAssociation()).getInvoiceItemsAuditLogs(), invoiceItemsAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForInvoices(createAuditLogsAssociation(), createAuditLogsAssociation()).getInvoiceAuditLogs(), invoicesAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForInvoices(createAuditLogsAssociation(), createAuditLogsAssociation()).getInvoiceItemsAuditLogs(), invoiceItemsAuditLogs);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForPayments.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForPayments.java
new file mode 100644
index 0000000..d6074f1
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForPayments.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestDefaultAuditLogsForPayments extends AuditLogsTestBase {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final Map<UUID, List<AuditLog>> paymentsAuditLogs = createAuditLogsAssociation();
+        Assert.assertEquals(new DefaultAuditLogsForPayments(paymentsAuditLogs).getPaymentsAuditLogs(), paymentsAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForPayments(createAuditLogsAssociation()).getPaymentsAuditLogs(), paymentsAuditLogs);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForRefunds.java b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForRefunds.java
new file mode 100644
index 0000000..802ae4c
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/audit/TestDefaultAuditLogsForRefunds.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 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.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestDefaultAuditLogsForRefunds extends AuditLogsTestBase {
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final Map<UUID, List<AuditLog>> refundsAuditLogs = createAuditLogsAssociation();
+        Assert.assertEquals(new DefaultAuditLogsForRefunds(refundsAuditLogs).getRefundsAuditLogs(), refundsAuditLogs);
+        Assert.assertNotEquals(new DefaultAuditLogsForRefunds(createAuditLogsAssociation()).getRefundsAuditLogs(), refundsAuditLogs);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleSql.java b/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleSql.java
index 56bb2f7..aafb006 100644
--- a/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleSql.java
+++ b/util/src/test/java/com/ning/billing/util/tag/MockTagStoreModuleSql.java
@@ -16,10 +16,7 @@
 
 package com.ning.billing.util.tag;
 
-
-import org.skife.jdbi.v2.Handle;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.tweak.HandleCallback;
 
 import com.ning.billing.KillbillTestSuiteWithEmbeddedDB;
 import com.ning.billing.dbi.MysqlTestingHelper;
@@ -29,25 +26,14 @@ import com.ning.billing.util.bus.InMemoryBus;
 import com.ning.billing.util.glue.TagStoreModule;
 
 public class MockTagStoreModuleSql extends TagStoreModule {
-    private MysqlTestingHelper helper;
 
     @Override
     protected void configure() {
-        helper = KillbillTestSuiteWithEmbeddedDB.getMysqlTestingHelper();
+        final MysqlTestingHelper helper = KillbillTestSuiteWithEmbeddedDB.getMysqlTestingHelper();
         bind(IDBI.class).toInstance(helper.getDBI());
         bind(MysqlTestingHelper.class).toInstance(helper);
         install(new MockClockModule());
         bind(Bus.class).toInstance(new InMemoryBus());
         super.configure();
     }
-
-    public void execute(final String ddl) {
-        helper.getDBI().withHandle(new HandleCallback<Void>() {
-            @Override
-            public Void withHandle(final Handle handle) throws Exception {
-                handle.execute(ddl);
-                return null;
-            }
-        });
-    }
 }