killbill-memoizeit

Changes

Details

diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
index 91919ac..5cee274 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/InvoiceApiHelper.java
@@ -74,7 +74,7 @@ public class InvoiceApiHelper {
     public List<InvoiceItem> dispatchToInvoicePluginsAndInsertItems(final UUID accountId, final WithAccountLock withAccountLock, final CallContext context) throws InvoiceApiException {
         GlobalLock lock = null;
         try {
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountId.toString(), NB_LOCK_TRY);
+            lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), accountId.toString(), NB_LOCK_TRY);
 
             final Iterable<Invoice> invoicesForPlugins = withAccountLock.prepareInvoices();
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
index 57ef684..78696a0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
@@ -21,7 +21,11 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,8 +68,9 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
 
     @Override
     public UUID createMigrationInvoice(final UUID accountId, final LocalDate targetDate, final BigDecimal balance, final Currency currency, final CallContext context) {
+        Account account;
         try {
-            accountUserApi.getAccountById(accountId, internalCallContextFactory.createInternalTenantContext(accountId, context));
+            account = accountUserApi.getAccountById(accountId, internalCallContextFactory.createInternalTenantContext(accountId, context));
         } catch (AccountApiException e) {
             log.warn("Unable to find account for id {}", accountId);
             return null;
@@ -75,8 +80,11 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
         final InvoiceItemModelDao migrationInvoiceItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.FIXED, migrationInvoice.getId(), accountId, null, null,
                                                                                  null, MigrationPlan.MIGRATION_PLAN_NAME, MigrationPlan.MIGRATION_PLAN_PHASE_NAME, null,
                                                                                  targetDate, null, balance, null, currency, null);
+
+        final DateTime wrongEffectiveDateButDoesNotMatter = null;
+        final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(wrongEffectiveDateButDoesNotMatter, account.getTimeZone(), clock);
         dao.createInvoice(migrationInvoice, ImmutableList.<InvoiceItemModelDao>of(migrationInvoiceItem),
-                          true, ImmutableMap.<UUID, List<DateTime>>of(), internalCallContextFactory.createInternalCallContext(accountId, context));
+                          true, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<DateTime>>of()), internalCallContextFactory.createInternalCallContext(accountId, context));
 
         return migrationInvoice.getId();
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 762f586..022da79 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -35,6 +35,7 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -217,7 +218,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
     @Override
     public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
-                              final boolean isRealInvoice, final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions,
+                              final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions,
                               final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
@@ -775,19 +776,19 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     private void notifyOfFutureBillingEvents(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                             final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions, final InternalCallContext internalCallContext) {
+                                             final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext internalCallContext) {
 
         final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule().getMillis();
         final boolean isInvoiceNotificationEnabled = dryRunNotificationTime > 0;
-        for (final UUID subscriptionId : callbackDateTimePerSubscriptions.keySet()) {
-            final List<DateTime> callbackDateTimeUTC = callbackDateTimePerSubscriptions.get(subscriptionId);
+        for (final UUID subscriptionId : callbackDateTimePerSubscriptions.getNotifications().keySet()) {
+            final List<DateTime> callbackDateTimeUTC = callbackDateTimePerSubscriptions.getNotifications().get(subscriptionId);
             for (final DateTime cur : callbackDateTimeUTC) {
                 if (isInvoiceNotificationEnabled) {
                     final DateTime curDryRunNotificationTime = cur.minus(dryRunNotificationTime);
                     final DateTime effectiveCurDryRunNotificationTime = (curDryRunNotificationTime.isAfter(clock.getUTCNow())) ? curDryRunNotificationTime : clock.getUTCNow();
-                    nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur, internalCallContext);
+                    nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur, callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
                 }
-                nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur, internalCallContext);
+                nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur, callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
             }
         }
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index 7f2ed99..f8612fa 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -30,6 +30,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.util.entity.Pagination;
@@ -38,7 +39,7 @@ import org.killbill.billing.util.entity.dao.EntityDao;
 public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceApiException> {
 
     void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
-                       final boolean isRealInvoice, final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions,
+                       final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions,
                        final InternalCallContext context);
 
     List<InvoiceItemModelDao> createInvoices(final List<InvoiceModelDao> invoices, final InternalCallContext context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index 9116ba0..70a28e7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -226,7 +226,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         }
 
         // Pretty-print the generated invoice items from the junction events
-        final StringBuilder logStringBuilder = new StringBuilder("Invoice items generated for invoiceId ")
+        final StringBuilder logStringBuilder = new StringBuilder("Proposed Invoice items for invoiceId ")
                 .append(invoiceId)
                 .append(" and accountId ")
                 .append(accountId);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index 11a0813..c5cba2e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -185,7 +185,7 @@ public class InvoiceDispatcher {
                                   @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
         GlobalLock lock = null;
         try {
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountId.toString(), NB_LOCK_TRY);
+            lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), accountId.toString(), NB_LOCK_TRY);
 
             return processAccountWithLock(accountId, targetDate, dryRunArguments, context);
         } catch (final LockFailedException e) {
@@ -287,8 +287,8 @@ public class InvoiceDispatcher {
                                                                                                                        }
                                                                                                                    }));
 
-                final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions = createNextFutureNotificationDate(invoiceItemModelDaos, billingEvents.getUsages(), dateAndTimeZoneContext);
-                invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, callbackDateTimePerSubscriptions, context);
+                final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceItemModelDaos, billingEvents.getUsages(), dateAndTimeZoneContext);
+                invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, futureAccountNotifications, context);
 
                 final List<InvoiceItem> fixedPriceInvoiceItems = invoice.getInvoiceItems(FixedPriceInvoiceItem.class);
                 final List<InvoiceItem> recurringInvoiceItems = invoice.getInvoiceItems(RecurringInvoiceItem.class);
@@ -353,8 +353,9 @@ public class InvoiceDispatcher {
         return internalCallContextFactory.createCallContext(context);
     }
 
+
     @VisibleForTesting
-    Map<UUID, List<DateTime>> createNextFutureNotificationDate(final List<InvoiceItemModelDao> invoiceItems, final Map<String, Usage> knownUsages, final DateAndTimeZoneContext dateAndTimeZoneContext) {
+    FutureAccountNotifications createNextFutureNotificationDate(final List<InvoiceItemModelDao> invoiceItems, final Map<String, Usage> knownUsages, final DateAndTimeZoneContext dateAndTimeZoneContext) {
 
         final Map<UUID, List<DateTime>> result = new HashMap<UUID, List<DateTime>>();
 
@@ -405,7 +406,7 @@ public class InvoiceDispatcher {
             perSubscriptionCallback.add(subscriptionUsageCallbackDate);
         }
 
-        return result;
+        return new FutureAccountNotifications(dateAndTimeZoneContext, result);
     }
 
     private DateTime getNextUsageBillingDate(final String usageName, final LocalDate chargedThroughDate, final DateAndTimeZoneContext dateAndTimeZoneContext, final Map<String, Usage> knownUsages) {
@@ -457,6 +458,25 @@ public class InvoiceDispatcher {
         }
     }
 
+    public static class FutureAccountNotifications {
+        private final DateAndTimeZoneContext accountDateAndTimeZoneContext;
+        private final Map<UUID, List<DateTime>> notifications;
+
+        public FutureAccountNotifications(final DateAndTimeZoneContext accountDateAndTimeZoneContext, final Map<UUID, List<DateTime>> notifications) {
+            this.accountDateAndTimeZoneContext = accountDateAndTimeZoneContext;
+            this.notifications = notifications;
+        }
+
+        public DateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
+            return accountDateAndTimeZoneContext;
+        }
+
+        public Map<UUID, List<DateTime>> getNotifications() {
+            return notifications;
+        }
+    }
+
+
     private final static class NullDryRunArguments implements DryRunArguments {
         @Override
         public PlanPhaseSpecifier getPlanPhaseSpecifier() {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
index e76a1f5..4a04504 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -19,18 +19,24 @@
 package org.killbill.billing.invoice.notification;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.invoice.api.DefaultInvoiceService;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
+import org.killbill.notificationq.api.NotificationEventWithMetadata;
 import org.killbill.notificationq.api.NotificationQueue;
 import org.killbill.notificationq.api.NotificationQueueService;
 import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
@@ -46,29 +52,52 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
 
     @Override
     public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                             final UUID subscriptionId, final DateTime futureNotificationTime, final InternalCallContext internalCallContext) {
-        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, internalCallContext);
+                                                             final UUID subscriptionId, final DateTime futureNotificationTime, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, accountDateAndTimeZoneContext, internalCallContext);
     }
 
 
     @Override
     public void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                                   final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext) {
-        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, internalCallContext);
+                                                                   final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, accountDateAndTimeZoneContext, internalCallContext);
     }
 
 
     private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                                   final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification,  final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext) {
+                                                                   final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification,  final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
         final NotificationQueue nextBillingQueue;
         try {
             nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
                                                                              DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
-            log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString());
 
-            nextBillingQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getHandle().getConnection(), futureNotificationTime,
-                                                                     new NextBillingDateNotificationKey(subscriptionId, targetDate, isDryRunForInvoiceNotification), internalCallContext.getUserToken(),
-                                                                     internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
+            // If we see existing notification for the same date (and isDryRunForInvoiceNotification mode), we don't insert a new notification
+            final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications =  nextBillingQueue.getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection());
+            final NotificationEventWithMetadata<NextBillingDateNotificationKey> existingFutureNotificationWithSameDate = Iterables.tryFind(futureNotifications, new Predicate<NotificationEventWithMetadata<NextBillingDateNotificationKey>>() {
+                @Override
+                public boolean apply(final NotificationEventWithMetadata<NextBillingDateNotificationKey> input) {
+                    final boolean isEventDryRunForNotifications = input.getEvent().isDryRunForInvoiceNotification() != null ?
+                                                                  input.getEvent().isDryRunForInvoiceNotification() : false;
+
+                    final LocalDate notificationEffectiveLocaleDate = new LocalDate(futureNotificationTime, accountDateAndTimeZoneContext.getAccountTimeZone());
+                    final LocalDate eventEffectiveLocaleDate = new LocalDate(input.getEffectiveDate(), accountDateAndTimeZoneContext.getAccountTimeZone());
+
+                    return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0 &&
+                           ((isDryRunForInvoiceNotification && isEventDryRunForNotifications) ||
+                            (!isDryRunForInvoiceNotification && !isEventDryRunForNotifications));
+                }
+            }).orNull();
+
+            if (existingFutureNotificationWithSameDate == null) {
+                log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString());
+
+                nextBillingQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getHandle().getConnection(), futureNotificationTime,
+                                                                         new NextBillingDateNotificationKey(subscriptionId, targetDate, isDryRunForInvoiceNotification), internalCallContext.getUserToken(),
+                                                                         internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
+            } else if (log.isDebugEnabled()) {
+                    log.debug("*********************   SKIPPING Queuing next billing date notification at {} for subscriptionId {} *******************", futureNotificationTime.toString(), subscriptionId.toString());
+            }
+
         } catch (final NoSuchNotificationQueue e) {
             log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e);
         } catch (final IOException e) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
index b702434..233191e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
@@ -23,13 +23,14 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
 
 public interface NextBillingDatePoster {
 
     void insertNextBillingNotificationFromTransaction(EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, UUID accountId,
-                                                      UUID subscriptionId, DateTime futureNotificationTime, InternalCallContext internalCallContext);
+                                                      UUID subscriptionId, DateTime futureNotificationTime, final DateAndTimeZoneContext accountDateAndTimeZoneContext, InternalCallContext internalCallContext);
 
     void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                                   final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext);
+                                                            final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext);
 
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index cf40438..21034f5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -32,6 +32,7 @@ import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
@@ -60,7 +61,7 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
 
     @Override
     public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems,
-                              final boolean isRealInvoice, final Map<UUID, List<DateTime>> callbackDateTimePerSubscriptions, final InternalCallContext context) {
+                              final boolean isRealInvoice, final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext context) {
         synchronized (monitor) {
             storeInvoice(invoice, context);
         }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
index edb4c64..521ea31 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -39,6 +39,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.TestInvoiceHelper.DryRunFutureDateArguments;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
@@ -208,11 +209,11 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
                                                                    internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
                                                                    clock);
 
-        final Map<UUID, List<DateTime>> result = dispatcher.createNextFutureNotificationDate(Collections.singletonList(item), null, dateAndTimeZoneContext);
+        final FutureAccountNotifications futureAccountNotifications = dispatcher.createNextFutureNotificationDate(Collections.singletonList(item), null, dateAndTimeZoneContext);
 
-        Assert.assertEquals(result.size(), 1);
+        Assert.assertEquals(futureAccountNotifications.getNotifications().size(), 1);
 
-        final List<DateTime> receivedDates = result.get(item.getSubscriptionId());
+        final List<DateTime> receivedDates = futureAccountNotifications.getNotifications().get(item.getSubscriptionId());
         Assert.assertEquals(receivedDates.size(), 1);
 
         final LocalDate receivedTargetDate = new LocalDate(receivedDates.get(0), DateTimeZone.forID("Pacific/Pitcairn"));
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 5086d46..2cd3410 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -49,6 +49,7 @@ import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -76,6 +77,7 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.currency.KillBillMoney;
+import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.mockito.Mockito;
@@ -278,7 +280,8 @@ public class TestInvoiceHelper {
                                                                                                                                          }));
 
         // The test does not use the invoice callback notifier hence the empty map
-        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, ImmutableMap.<UUID, List<DateTime>>of(), internalCallContext);
+        final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC, clock);
+        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<DateTime>>of()), internalCallContext);
     }
 
     public void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java
index c66af70..871915a 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/tests/InvoiceTestUtils.java
@@ -24,11 +24,13 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
 import org.killbill.billing.invoice.TestInvoiceHelper;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -40,6 +42,7 @@ import org.killbill.billing.invoice.dao.InvoiceDao;
 import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
+import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
 import org.killbill.clock.Clock;
 import org.mockito.Mockito;
 import org.testng.Assert;
@@ -103,7 +106,9 @@ public class InvoiceTestUtils {
         }
         Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
 
-        invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, ImmutableMap.<UUID, List<DateTime>>of(), internalCallContext);
+        final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC, clock);
+
+        invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<DateTime>>of()), internalCallContext);
 
         return invoice;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index d920800..872296c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -29,6 +29,7 @@ public interface JaxrsResource {
     public static final String TIMELINE = "timeline";
     public static final String REGISTER_NOTIFICATION_CALLBACK = "registerNotificationCallback";
     public static final String UPLOAD_PLUGIN_CONFIG = "uploadPluginConfig";
+    public static final String USER_KEY_VALUE = "userKeyValue";
     public static final String SEARCH = "search";
 
     /*
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
index 977537d..3fb174e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
@@ -16,7 +16,6 @@
 
 package org.killbill.billing.jaxrs.resources;
 
-import java.net.URI;
 import java.util.List;
 import java.util.UUID;
 
@@ -33,7 +32,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ObjectType;
@@ -209,6 +207,58 @@ public class TenantResource extends JaxRsResourceBase {
         return deleteTenantKey(TenantKey.PLUGIN_CONFIG_, pluginName, createdBy, reason, comment, request);
     }
 
+
+    @Timed
+    @POST
+    @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
+    @Consumes(TEXT_PLAIN)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Add a per tenant user key/value")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response insertUserKeyValue(@PathParam("keyName") final String key,
+                               final String value,
+                               @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                               @HeaderParam(HDR_REASON) final String reason,
+                               @HeaderParam(HDR_COMMENT) final String comment,
+                               @javax.ws.rs.core.Context final HttpServletRequest request,
+                               @javax.ws.rs.core.Context  final UriInfo uriInfo) throws TenantApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        tenantApi.addTenantKeyValue(key, value, callContext);
+        return uriBuilder.buildResponse(uriInfo, TenantResource.class, "getUserKeyValue", key);
+    }
+
+    @Timed
+    @GET
+    @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve a per tenant user key/value", response = TenantKeyJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response getUserKeyValue(@PathParam("keyName") final String key,
+                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+        final TenantContext tenantContext = context.createContext(request);
+        final List<String> values = tenantApi.getTenantValuesForKey(key, tenantContext);
+        final TenantKeyJson result = new TenantKeyJson(key, values);
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+
+    @Timed
+    @DELETE
+    @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
+    @ApiOperation(value = "Delete  a per tenant user key/value")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response deleteUserKeyValue(@PathParam("keyName") final String key,
+                                              @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                              @HeaderParam(HDR_REASON) final String reason,
+                                              @HeaderParam(HDR_COMMENT) final String comment,
+                                              @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        tenantApi.deleteTenantKey(key, callContext);
+        return Response.status(Status.OK).build();
+    }
+
+
+
     private Response insertTenantKey(final TenantKey key,
                                      @Nullable final String keyPostfix,
                                      final String value,
@@ -223,10 +273,10 @@ public class TenantResource extends JaxRsResourceBase {
         tenantApi.addTenantKeyValue(tenantKey, value, callContext);
 
         return uriBuilder.buildResponse(uriInfo, TenantResource.class, getMethodStr, keyPostfix);
-//        final URI uri = UriBuilder.fromResource(TenantResource.class).path(TenantResource.class, getMethodStr).build();
-//        return Response.created(uri).build();
     }
 
+
+
     private Response getTenantKey(final TenantKey key,
                                   @Nullable final String keyPostfix,
                                   final HttpServletRequest request) throws TenantApiException {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index 9d70fe7..a411e2c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -215,7 +215,7 @@ public abstract class ProcessorBase {
                 throws ExceptionType, LockFailedException {
             GlobalLock lock = null;
             try {
-                lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), accountExternalKey, NB_LOCK_TRY);
+                lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), accountExternalKey, NB_LOCK_TRY);
                 return callback.doOperation();
             } finally {
                 if (lock != null) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
index bc178c9..fffb976 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestRetryablePayment.java
@@ -700,7 +700,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         GlobalLock lock = null;
         try {
             // Grab lock so that operation later will fail...
-            lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), account.getExternalKey(), 1);
+            lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), account.getExternalKey(), 1);
 
             mockRetryProviderPlugin
                     .setAborted(false)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
index b06bb90..8394193 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultRequestedSubscriptionEvent.java
@@ -58,10 +58,11 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
 
     public DefaultRequestedSubscriptionEvent(final DefaultSubscriptionBase subscription,
                                              final SubscriptionBaseEvent nextEvent,
+                                             final SubscriptionBaseTransitionType transitionType,
                                              final Long searchKey1,
                                              final Long searchKey2,
                                              final UUID userToken) {
         this(nextEvent.getId(), nextEvent.getSubscriptionId(), subscription.getBundleId(), nextEvent.getRequestedDate(), nextEvent.getEffectiveDate(),
-             null, null, null, null, null, null, null, null, nextEvent.getTotalOrdering(), null, 0, null, searchKey1, searchKey2, userToken);
+             null, null, null, null, null, null, null, null, nextEvent.getTotalOrdering(), transitionType, 0, null, searchKey1, searchKey2, userToken);
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 6d770f8..21b377d 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -48,6 +48,7 @@ import org.killbill.billing.entity.EntityPersistenceException;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.RepairSubscriptionInternalEvent;
 import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.migration.AccountMigrationData;
 import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
 import org.killbill.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -416,7 +417,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                                                         new SubscriptionNotificationKey(nextPhase.getId()), context);
 
                 // Notify the Bus of the requested change
-                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, nextPhase, context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, nextPhase, SubscriptionBaseTransitionType.PHASE, context);
 
                 return null;
             }
@@ -511,7 +512,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 }
                 // Notify the Bus of the latest requested change, if needed
                 if (initialEvents.size() > 0) {
-                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, initialEvents.get(initialEvents.size() - 1), context);
+                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, initialEvents.get(initialEvents.size() - 1), SubscriptionBaseTransitionType.CREATE, context);
                 }
                 return null;
             }
@@ -533,7 +534,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 }
 
                 // Notify the Bus of the latest requested change
-                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, recreateEvents.get(recreateEvents.size() - 1), context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, recreateEvents.get(recreateEvents.size() - 1), SubscriptionBaseTransitionType.RE_CREATE, context);
 
                 return null;
             }
@@ -600,7 +601,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                     }
 
                     // Notify the Bus of the latest requested change
-                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, uncancelEvents.get(uncancelEvents.size() - 1), context);
+                    notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, uncancelEvents.get(uncancelEvents.size() - 1), SubscriptionBaseTransitionType.UNCANCEL, context);
                 }
 
                 return null;
@@ -634,7 +635,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
                 // Notify the Bus of the latest requested change
                 final SubscriptionBaseEvent finalEvent = changeEventsTweakedWithMigrateBilling.get(changeEvents.size() - 1);
-                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, context);
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, SubscriptionBaseTransitionType.CHANGE, context);
 
                 return null;
             }
@@ -765,7 +766,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         recordBusOrFutureNotificationFromTransaction(subscription, cancelEvent, entitySqlDaoWrapperFactory, isBusEvent, seqId, context);
 
         // Notify the Bus of the requested change
-        notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, cancelEvent, context);
+        notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, cancelEvent, SubscriptionBaseTransitionType.CANCEL, context);
     }
 
     private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) {
@@ -1103,9 +1104,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     private void notifyBusOfRequestedChange(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final DefaultSubscriptionBase subscription,
-                                            final SubscriptionBaseEvent nextEvent, final InternalCallContext context) {
+                                            final SubscriptionBaseEvent nextEvent, final SubscriptionBaseTransitionType transitionType, final InternalCallContext context) {
         try {
-            eventBus.postFromTransaction(new DefaultRequestedSubscriptionEvent(subscription, nextEvent, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()), entitySqlDaoWrapperFactory.getHandle().getConnection());
+            eventBus.postFromTransaction(new DefaultRequestedSubscriptionEvent(subscription, nextEvent, transitionType, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()), entitySqlDaoWrapperFactory.getHandle().getConnection());
         } catch (EventBusException e) {
             log.warn("Failed to post requested change event for subscription " + subscription.getId(), e);
         }
@@ -1151,7 +1152,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
             // Notify the Bus of the latest requested change
             final SubscriptionBaseEvent finalEvent = curSubscription.getInitialEvents().get(curSubscription.getInitialEvents().size() - 1);
-            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, context);
+            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, SubscriptionBaseTransitionType.MIGRATE_BILLING, context);
         }
 
         transBundleDao.create(new SubscriptionBundleModelDao(bundleData), context);
diff --git a/util/src/main/java/org/killbill/billing/util/globallocker/LockerType.java b/util/src/main/java/org/killbill/billing/util/globallocker/LockerType.java
index de8a0fe..a6fc82a 100644
--- a/util/src/main/java/org/killbill/billing/util/globallocker/LockerType.java
+++ b/util/src/main/java/org/killbill/billing/util/globallocker/LockerType.java
@@ -17,5 +17,5 @@
 package org.killbill.billing.util.globallocker;
 
 public enum LockerType {
-    ACCOUNT_FOR_INVOICE_PAYMENTS
+    ACCNT_INV_PAY
 }
diff --git a/util/src/main/java/org/killbill/billing/util/timezone/DateAndTimeZoneContext.java b/util/src/main/java/org/killbill/billing/util/timezone/DateAndTimeZoneContext.java
index 54fec5d..5781c36 100644
--- a/util/src/main/java/org/killbill/billing/util/timezone/DateAndTimeZoneContext.java
+++ b/util/src/main/java/org/killbill/billing/util/timezone/DateAndTimeZoneContext.java
@@ -77,4 +77,7 @@ public final class DateAndTimeZoneContext {
         return computeUTCDateTimeFromLocalDate(now);
     }
 
+    public DateTimeZone getAccountTimeZone() {
+        return accountTimeZone;
+    }
 }
diff --git a/util/src/test/java/org/killbill/billing/util/globallocker/TestMysqlGlobalLocker.java b/util/src/test/java/org/killbill/billing/util/globallocker/TestMysqlGlobalLocker.java
index 0efbec4..365a23d 100644
--- a/util/src/test/java/org/killbill/billing/util/globallocker/TestMysqlGlobalLocker.java
+++ b/util/src/test/java/org/killbill/billing/util/globallocker/TestMysqlGlobalLocker.java
@@ -26,9 +26,7 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import org.killbill.commons.locker.GlobalLock;
-import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
-import org.killbill.commons.locker.mysql.MySqlGlobalLocker;
 import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
 
 public class TestMysqlGlobalLocker extends UtilTestSuiteWithEmbeddedDB {
@@ -38,7 +36,7 @@ public class TestMysqlGlobalLocker extends UtilTestSuiteWithEmbeddedDB {
     public void testSimpleLocking() throws IOException, LockFailedException {
         final String lockName = UUID.randomUUID().toString();
 
-        final GlobalLock lock = locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), lockName, 3);
+        final GlobalLock lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 3);
 
         dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
@@ -48,11 +46,11 @@ public class TestMysqlGlobalLocker extends UtilTestSuiteWithEmbeddedDB {
                 return null;
             }
         });
-        Assert.assertEquals(locker.isFree(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), lockName), false);
+        Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), false);
 
         boolean gotException = false;
         try {
-            locker.lockWithNumberOfTries(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), lockName, 1);
+            locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 1);
         } catch (LockFailedException e) {
             gotException = true;
         }
@@ -60,6 +58,6 @@ public class TestMysqlGlobalLocker extends UtilTestSuiteWithEmbeddedDB {
 
         lock.release();
 
-        Assert.assertEquals(locker.isFree(LockerType.ACCOUNT_FOR_INVOICE_PAYMENTS.toString(), lockName), true);
+        Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), true);
     }
 }