killbill-aplcache

Merge remote-tracking branch 'upstream/work-for-release-0.17.x'

11/2/2016 12:53:30 PM

Changes

.idea/compiler.xml 68(+34 -34)

account/pom.xml 29(+28 -1)

api/pom.xml 6(+1 -5)

api/src/main/java/org/killbill/billing/account/api/MigrationAccountData.java 37(+0 -37)

api/src/main/java/org/killbill/billing/invoice/api/InvoiceMigrationApi.java 42(+0 -42)

api/src/main/java/org/killbill/billing/subscription/api/migration/SubscriptionBaseMigrationApi.java 133(+0 -133)

beatrix/pom.xml 42(+41 -1)

bin/import-account 96(+79 -17)

catalog/pom.xml 41(+40 -1)

currency/pom.xml 2(+1 -1)

entitlement/pom.xml 37(+32 -5)

entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEffectiveEntitlementEvent.java 166(+0 -166)

invoice/pom.xml 35(+33 -2)

invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java 81(+0 -81)

invoice/src/main/java/org/killbill/billing/invoice/model/MigrationInvoiceItem.java 34(+0 -34)

jaxrs/pom.xml 16(+10 -6)

junction/pom.xml 37(+28 -9)

junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java 152(+0 -152)

NEWS 29(+26 -3)

overdue/pom.xml 33(+32 -1)

overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueInternalApi.java 151(+0 -151)

payment/pom.xml 61(+56 -5)

pom.xml 4(+2 -2)

profiles/killbill/src/main/webapp/lib/handlebars-1.0.0.js 2278(+0 -2278)

profiles/killbill/src/main/webapp/lib/highlight.7.3.pack.js 1(+0 -1)

profiles/killbill/src/main/webapp/lib/swagger.js 1665(+0 -1665)

profiles/killbill/src/main/webapp/lib/swagger-client.js 1382(+0 -1382)

profiles/killbill/src/main/webapp/lib/underscore-min.js 32(+0 -32)

profiles/killpay/src/main/webapp/lib/handlebars-1.0.0.js 2278(+0 -2278)

profiles/killpay/src/main/webapp/lib/highlight.7.3.pack.js 1(+0 -1)

profiles/killpay/src/main/webapp/lib/swagger.js 1665(+0 -1665)

profiles/killpay/src/main/webapp/lib/swagger-client.js 1382(+0 -1382)

profiles/killpay/src/main/webapp/lib/underscore-min.js 32(+0 -32)

profiles/pom.xml 2(+1 -1)

subscription/src/main/java/org/killbill/billing/subscription/alignment/MigrationPlanAligner.java 211(+0 -211)

subscription/src/main/java/org/killbill/billing/subscription/alignment/TimedMigration.java 126(+0 -126)

subscription/src/main/java/org/killbill/billing/subscription/api/migration/DefaultSubscriptionBaseMigrationApi.java 287(+0 -287)

subscription/src/test/java/org/killbill/billing/subscription/alignment/TestTimedMigration.java 54(+0 -54)

subscription/src/test/java/org/killbill/billing/subscription/api/migration/TestMigration.java 304(+0 -304)

subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiRecreate.java 114(+0 -114)

tenant/pom.xml 53(+52 -1)

usage/pom.xml 38(+37 -1)

util/pom.xml 57(+44 -13)

util/src/main/java/org/killbill/billing/util/timezone/DefaultAccountDateAndTimeZoneContext.java 51(+0 -51)

Details

.idea/compiler.xml 68(+34 -34)

diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 0fc88df..9b1b3d8 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -24,124 +24,124 @@
         <processorPath useClasspath="true" />
         <module name="currency" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-beatrix" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-account" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-beatrix" />
+        <module name="killbill-account" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-payment" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-internal-api" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-payment" />
+        <module name="killbill-internal-api" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-invoice" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-beatrix" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-invoice" />
+        <module name="killbill-beatrix" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-util" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-catalog" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-util" />
+        <module name="killbill-catalog" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-internal-api" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-currency" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-internal-api" />
+        <module name="killbill-currency" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-account" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-entitlement" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-account" />
+        <module name="killbill-entitlement" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-currency" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-invoice" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-currency" />
+        <module name="killbill-invoice" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-subscription" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-jaxrs" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-subscription" />
+        <module name="killbill-jaxrs" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-overdue" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-junction" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-overdue" />
+        <module name="killbill-junction" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-junction" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-overdue" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-junction" />
+        <module name="killbill-overdue" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-tenant" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-payment" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-tenant" />
+        <module name="killbill-payment" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-jaxrs" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-subscription" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-jaxrs" />
+        <module name="killbill-subscription" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-profiles-killpay" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-tenant" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-profiles-killpay" />
+        <module name="killbill-tenant" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-profiles-killbill" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-usage" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-profiles-killbill" />
+        <module name="killbill-usage" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-usage" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-util" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-usage" />
+        <module name="killbill-util" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-catalog" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-profiles-killbill" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-catalog" />
+        <module name="killbill-profiles-killbill" />
       </profile>
-      <profile default="false" name="Annotation profile for killbill-entitlement" enabled="true">
+      <profile default="false" name="Annotation profile for killbill-profiles-killpay" enabled="true">
         <sourceOutputDir name="target/generated-sources/annotations" />
         <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
         <outputRelativeToContentRoot value="true" />
         <processorPath useClasspath="true" />
-        <module name="killbill-entitlement" />
+        <module name="killbill-profiles-killpay" />
       </profile>
     </annotationProcessing>
     <bytecodeTargetLevel>
diff --git a/.idea/modules.xml b/.idea/modules.xml
index c733a45..560d78f 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -23,5 +23,4 @@
       <module fileurl="file://$PROJECT_DIR$/util/killbill-util.iml" filepath="$PROJECT_DIR$/util/killbill-util.iml" />
     </modules>
   </component>
-</project>
-
+</project>
\ No newline at end of file

account/pom.xml 29(+28 -1)

diff --git a/account/pom.xml b/account/pom.xml
index bdc1130..ba0b8a9 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
@@ -54,6 +54,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -63,6 +78,11 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
@@ -126,6 +146,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
index 957b2e2..0fcf194 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultAccount.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -27,6 +27,7 @@ import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.dao.AccountModelDao;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
 
 import static org.killbill.billing.account.api.DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL;
 
@@ -37,6 +38,8 @@ public class DefaultAccount extends EntityBase implements Account {
     private final String name;
     private final Integer firstNameLength;
     private final Currency currency;
+    private final UUID parentAccountId;
+    private final Boolean isPaymentDelegatedToParent;
     private final Integer billCycleDayLocal;
     private final UUID paymentMethodId;
     private final DateTimeZone timeZone;
@@ -49,6 +52,7 @@ public class DefaultAccount extends EntityBase implements Account {
     private final String country;
     private final String postalCode;
     private final String phone;
+    private final String notes;
     private final Boolean isMigrated;
     private final Boolean isNotifiedForInvoices;
 
@@ -65,6 +69,8 @@ public class DefaultAccount extends EntityBase implements Account {
              data.getName(),
              data.getFirstNameLength(),
              data.getCurrency(),
+             data.getParentAccountId(),
+             data.isPaymentDelegatedToParent(),
              data.getBillCycleDayLocal(),
              data.getPaymentMethodId(),
              data.getTimeZone(),
@@ -77,18 +83,20 @@ public class DefaultAccount extends EntityBase implements Account {
              data.getCountry(),
              data.getPostalCode(),
              data.getPhone(),
+             data.getNotes(),
              data.isMigrated(),
              data.isNotifiedForInvoices());
     }
 
     // This call is used for testing and update from an existing account
     public DefaultAccount(final UUID id, final String externalKey, final String email,
-                          final String name, final Integer firstNameLength,
-                          final Currency currency, final Integer billCycleDayLocal, final UUID paymentMethodId,
+                          final String name, final Integer firstNameLength, final Currency currency,
+                          final UUID parentAccountId, final Boolean isPaymentDelegatedToParent,
+                          final Integer billCycleDayLocal, final UUID paymentMethodId,
                           final DateTimeZone timeZone, final String locale,
                           final String address1, final String address2, final String companyName,
                           final String city, final String stateOrProvince, final String country,
-                          final String postalCode, final String phone,
+                          final String postalCode, final String phone, final String notes,
                           final Boolean isMigrated, final Boolean isNotifiedForInvoices) {
         this(id,
              null,
@@ -98,6 +106,8 @@ public class DefaultAccount extends EntityBase implements Account {
              name,
              firstNameLength,
              currency,
+             parentAccountId,
+             isPaymentDelegatedToParent,
              billCycleDayLocal,
              paymentMethodId,
              timeZone,
@@ -110,18 +120,20 @@ public class DefaultAccount extends EntityBase implements Account {
              country,
              postalCode,
              phone,
+             notes,
              isMigrated,
              isNotifiedForInvoices);
     }
 
     public DefaultAccount(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate,
                           final String externalKey, final String email,
-                          final String name, final Integer firstNameLength,
-                          final Currency currency, final Integer billCycleDayLocal, final UUID paymentMethodId,
+                          final String name, final Integer firstNameLength, final Currency currency,
+                          final UUID parentAccountId, final Boolean isPaymentDelegatedToParent,
+                          final Integer billCycleDayLocal, final UUID paymentMethodId,
                           final DateTimeZone timeZone, final String locale,
                           final String address1, final String address2, final String companyName,
                           final String city, final String stateOrProvince, final String country,
-                          final String postalCode, final String phone,
+                          final String postalCode, final String phone, final String notes,
                           final Boolean isMigrated, final Boolean isNotifiedForInvoices) {
         super(id, createdDate, updatedDate);
         this.externalKey = externalKey;
@@ -129,6 +141,8 @@ public class DefaultAccount extends EntityBase implements Account {
         this.name = name;
         this.firstNameLength = firstNameLength;
         this.currency = currency;
+        this.parentAccountId = parentAccountId;
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent : false;
         this.billCycleDayLocal = billCycleDayLocal == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : billCycleDayLocal;
         this.paymentMethodId = paymentMethodId;
         this.timeZone = timeZone;
@@ -141,6 +155,7 @@ public class DefaultAccount extends EntityBase implements Account {
         this.postalCode = postalCode;
         this.country = country;
         this.phone = phone;
+        this.notes = notes;
         this.isMigrated = isMigrated;
         this.isNotifiedForInvoices = isNotifiedForInvoices;
     }
@@ -154,6 +169,8 @@ public class DefaultAccount extends EntityBase implements Account {
              accountModelDao.getName(),
              accountModelDao.getFirstNameLength(),
              accountModelDao.getCurrency(),
+             accountModelDao.getParentAccountId(),
+             accountModelDao.getIsPaymentDelegatedToParent(),
              accountModelDao.getBillingCycleDayLocal(),
              accountModelDao.getPaymentMethodId(),
              accountModelDao.getTimeZone(),
@@ -166,6 +183,7 @@ public class DefaultAccount extends EntityBase implements Account {
              accountModelDao.getCountry(),
              accountModelDao.getPostalCode(),
              accountModelDao.getPhone(),
+             accountModelDao.getNotes(),
              accountModelDao.getMigrated(),
              accountModelDao.getIsNotifiedForInvoices());
     }
@@ -196,6 +214,16 @@ public class DefaultAccount extends EntityBase implements Account {
     }
 
     @Override
+    public UUID getParentAccountId() {
+        return parentAccountId;
+    }
+
+    @Override
+    public Boolean isPaymentDelegatedToParent() {
+        return isPaymentDelegatedToParent;
+    }
+
+    @Override
     public Integer getBillCycleDayLocal() {
         return billCycleDayLocal;
     }
@@ -267,6 +295,11 @@ public class DefaultAccount extends EntityBase implements Account {
     }
 
     @Override
+    public String getNotes() {
+        return notes;
+    }
+
+    @Override
     public MutableAccountData toMutableAccountData() {
         return new DefaultMutableAccountData(this);
     }
@@ -327,6 +360,9 @@ public class DefaultAccount extends EntityBase implements Account {
         accountData.setCountry(country != null ? country : currentAccount.getCountry());
         accountData.setPostalCode(postalCode != null ? postalCode : currentAccount.getPostalCode());
         accountData.setPhone(phone != null ? phone : currentAccount.getPhone());
+        accountData.setNotes(notes != null ? notes : currentAccount.getNotes());
+        accountData.setParentAccountId(parentAccountId != null ? parentAccountId : currentAccount.getParentAccountId());
+        accountData.setIsPaymentDelegatedToParent(isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent : currentAccount.isPaymentDelegatedToParent());
         final Boolean isMigrated = this.isMigrated != null ? this.isMigrated : currentAccount.isMigrated();
         if (isMigrated != null) {
             accountData.setIsMigrated(isMigrated);
@@ -344,6 +380,16 @@ public class DefaultAccount extends EntityBase implements Account {
     }
 
     @Override
+    public DateTimeZone getFixedOffsetTimeZone() {
+        return AccountDateTimeUtils.getFixedOffsetTimeZone(this);
+    }
+
+    @Override
+    public DateTime getReferenceTime() {
+        return AccountDateTimeUtils.getReferenceDateTime(this);
+    }
+
+    @Override
     public String toString() {
         return "DefaultAccount [externalKey=" + externalKey +
                ", email=" + email +
@@ -351,6 +397,8 @@ public class DefaultAccount extends EntityBase implements Account {
                ", firstNameLength=" + firstNameLength +
                ", phone=" + phone +
                ", currency=" + currency +
+               ", parentAccountId=" + parentAccountId +
+               ", isPaymentDelegatedToParent=" + isPaymentDelegatedToParent +
                ", billCycleDayLocal=" + billCycleDayLocal +
                ", paymentMethodId=" + paymentMethodId +
                ", timezone=" + timeZone +
@@ -362,6 +410,7 @@ public class DefaultAccount extends EntityBase implements Account {
                ", stateOrProvince=" + stateOrProvince +
                ", postalCode=" + postalCode +
                ", country=" + country +
+               ", notes=" + notes +
                "]";
     }
 
@@ -400,6 +449,12 @@ public class DefaultAccount extends EntityBase implements Account {
         if (currency != that.currency) {
             return false;
         }
+        if (parentAccountId != null ? !parentAccountId.equals(that.parentAccountId) : that.parentAccountId != null) {
+            return false;
+        }
+        if (isPaymentDelegatedToParent != null ? !isPaymentDelegatedToParent.equals(that.isPaymentDelegatedToParent) : that.isPaymentDelegatedToParent != null) {
+            return false;
+        }
         if (email != null ? !email.equals(that.email) : that.email != null) {
             return false;
         }
@@ -436,6 +491,9 @@ public class DefaultAccount extends EntityBase implements Account {
         if (timeZone != null ? !timeZone.equals(that.timeZone) : that.timeZone != null) {
             return false;
         }
+        if (notes != null ? !notes.equals(that.notes) : that.notes != null) {
+            return false;
+        }
 
         return true;
     }
@@ -448,6 +506,8 @@ public class DefaultAccount extends EntityBase implements Account {
         result = 31 * result + (name != null ? name.hashCode() : 0);
         result = 31 * result + (firstNameLength != null ? firstNameLength.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (parentAccountId != null ? parentAccountId.hashCode() : 0);
+        result = 31 * result + (isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent.hashCode() : 0);
         result = 31 * result + billCycleDayLocal;
         result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
         result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
@@ -460,6 +520,7 @@ public class DefaultAccount extends EntityBase implements Account {
         result = 31 * result + (country != null ? country.hashCode() : 0);
         result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
         result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (notes != null ? notes.hashCode() : 0);
         result = 31 * result + (isMigrated != null ? isMigrated.hashCode() : 0);
         result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
         return result;
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java b/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
index e1df215..4090248 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultImmutableAccountData.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -19,8 +19,10 @@ package org.killbill.billing.account.api;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
 
 public class DefaultImmutableAccountData implements ImmutableAccountData {
 
@@ -28,16 +30,26 @@ public class DefaultImmutableAccountData implements ImmutableAccountData {
     private final String externalKey;
     private final Currency currency;
     private final DateTimeZone dateTimeZone;
+    private final UUID parentAccountId;
+    private final boolean isPaymentDelegatedToParent;
+    private final DateTimeZone fixedOffsetDateTimeZone;
+    private final DateTime referenceTime;
 
-    public DefaultImmutableAccountData(final UUID id, final String externalKey, final Currency currency, final DateTimeZone dateTimeZone) {
+    public DefaultImmutableAccountData(final UUID id, final String externalKey, final Currency currency, final DateTimeZone dateTimeZone, final DateTimeZone fixedOffsetDateTimeZone, final DateTime referenceTime, final UUID parentAccountId, final boolean isPaymentDelegatedToParent) {
         this.id = id;
         this.externalKey = externalKey;
         this.currency = currency;
         this.dateTimeZone = dateTimeZone;
+        this.fixedOffsetDateTimeZone = fixedOffsetDateTimeZone;
+        this.referenceTime = referenceTime;
+        this.parentAccountId = parentAccountId;
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
     }
 
     public DefaultImmutableAccountData(final Account account) {
-        this(account.getId(), account.getExternalKey(), account.getCurrency(), account.getTimeZone());
+        this(account.getId(), account.getExternalKey(), account.getCurrency(), account.getTimeZone(),
+             AccountDateTimeUtils.getFixedOffsetTimeZone(account), AccountDateTimeUtils.getReferenceDateTime(account),
+             account.getParentAccountId(), account.isPaymentDelegatedToParent());
     }
 
     @Override
@@ -59,4 +71,76 @@ public class DefaultImmutableAccountData implements ImmutableAccountData {
     public DateTimeZone getTimeZone() {
         return dateTimeZone;
     }
+
+    @Override
+    public UUID getParentAccountId() {
+        return parentAccountId;
+    }
+
+    @Override
+    public Boolean isPaymentDelegatedToParent() {
+        return isPaymentDelegatedToParent;
+    }
+
+    public DateTimeZone getFixedOffsetTimeZone() {
+        return fixedOffsetDateTimeZone;
+    }
+
+    @Override
+    public DateTime getReferenceTime() {
+        return referenceTime;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultImmutableAccountData{");
+        sb.append("id=").append(id);
+        sb.append(", externalKey='").append(externalKey).append('\'');
+        sb.append(", currency=").append(currency);
+        sb.append(", dateTimeZone=").append(dateTimeZone);
+        sb.append(", fixedOffsetDateTimeZone=").append(fixedOffsetDateTimeZone);
+        sb.append(", referenceTime=").append(referenceTime);
+        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 DefaultImmutableAccountData that = (DefaultImmutableAccountData) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) {
+            return false;
+        }
+        if (externalKey != null ? !externalKey.equals(that.externalKey) : that.externalKey != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (dateTimeZone != null ? !dateTimeZone.equals(that.dateTimeZone) : that.dateTimeZone != null) {
+            return false;
+        }
+        if (fixedOffsetDateTimeZone != null ? !fixedOffsetDateTimeZone.equals(that.fixedOffsetDateTimeZone) : that.fixedOffsetDateTimeZone != null) {
+            return false;
+        }
+        return referenceTime != null ? referenceTime.compareTo(that.referenceTime) == 0 : that.referenceTime == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = id != null ? id.hashCode() : 0;
+        result = 31 * result + (externalKey != null ? externalKey.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (dateTimeZone != null ? dateTimeZone.hashCode() : 0);
+        result = 31 * result + (fixedOffsetDateTimeZone != null ? fixedOffsetDateTimeZone.hashCode() : 0);
+        result = 31 * result + (referenceTime != null ? referenceTime.hashCode() : 0);
+        return result;
+    }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java b/account/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
index 010a76d..1f1c6fc 100644
--- a/account/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
+++ b/account/src/main/java/org/killbill/billing/account/api/DefaultMutableAccountData.java
@@ -33,6 +33,8 @@ public class DefaultMutableAccountData implements MutableAccountData {
     private String name;
     private Integer firstNameLength;
     private Currency currency;
+    private UUID parentAccountId;
+    private Boolean isPaymentDelegatedToParent;
     private int billCycleDayLocal;
     private UUID paymentMethodId;
     private DateTimeZone timeZone;
@@ -45,21 +47,25 @@ public class DefaultMutableAccountData implements MutableAccountData {
     private String country;
     private String postalCode;
     private String phone;
+    private String notes;
     private Boolean isMigrated;
     private Boolean isNotifiedForInvoices;
 
     public DefaultMutableAccountData(final String externalKey, final String email, final String name,
-                                     final int firstNameLength, final Currency currency, final int billCycleDayLocal,
-                                     final UUID paymentMethodId, final DateTimeZone timeZone,
+                                     final int firstNameLength, final Currency currency,
+                                     final UUID parentAccountId, final Boolean isPaymentDelegatedToParent,
+                                     final int billCycleDayLocal, final UUID paymentMethodId, final DateTimeZone timeZone,
                                      final String locale, final String address1, final String address2,
                                      final String companyName, final String city, final String stateOrProvince,
                                      final String country, final String postalCode, final String phone,
-                                     final boolean isMigrated, final boolean isNotifiedForInvoices) {
+                                     final String notes, final boolean isMigrated, final boolean isNotifiedForInvoices) {
         this.externalKey = externalKey;
         this.email = email;
         this.name = name;
         this.firstNameLength = firstNameLength;
         this.currency = currency;
+        this.parentAccountId = parentAccountId;
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
         this.billCycleDayLocal = billCycleDayLocal;
         this.paymentMethodId = paymentMethodId;
         this.timeZone = timeZone;
@@ -72,6 +78,7 @@ public class DefaultMutableAccountData implements MutableAccountData {
         this.country = country;
         this.postalCode = postalCode;
         this.phone = phone;
+        this.notes = notes;
         this.isMigrated = isMigrated;
         this.isNotifiedForInvoices = isNotifiedForInvoices;
     }
@@ -82,6 +89,8 @@ public class DefaultMutableAccountData implements MutableAccountData {
         this.name = accountData.getName();
         this.firstNameLength = accountData.getFirstNameLength();
         this.currency = accountData.getCurrency();
+        this.parentAccountId = accountData.getParentAccountId();
+        this.isPaymentDelegatedToParent = accountData.isPaymentDelegatedToParent();
         this.billCycleDayLocal = accountData.getBillCycleDayLocal() == null ? DEFAULT_BILLING_CYCLE_DAY_LOCAL : accountData.getBillCycleDayLocal();
         this.paymentMethodId = accountData.getPaymentMethodId();
         this.timeZone = accountData.getTimeZone();
@@ -94,6 +103,7 @@ public class DefaultMutableAccountData implements MutableAccountData {
         this.country = accountData.getCountry();
         this.postalCode = accountData.getPostalCode();
         this.phone = accountData.getPhone();
+        this.notes = accountData.getNotes();
         this.isMigrated = accountData.isMigrated();
         this.isNotifiedForInvoices = accountData.isNotifiedForInvoices();
     }
@@ -269,6 +279,16 @@ public class DefaultMutableAccountData implements MutableAccountData {
     }
 
     @Override
+    public String getNotes() {
+        return notes;
+    }
+
+    @Override
+    public void setNotes(final String notes) {
+        this.notes = notes;
+    }
+
+    @Override
     public Boolean isMigrated() {
         return isMigrated;
     }
@@ -287,4 +307,24 @@ public class DefaultMutableAccountData implements MutableAccountData {
     public void setIsNotifiedForInvoices(final boolean isNotifiedForInvoices) {
         this.isNotifiedForInvoices = isNotifiedForInvoices;
     }
+
+    @Override
+    public UUID getParentAccountId() {
+        return parentAccountId;
+    }
+
+    @Override
+    public void setParentAccountId(final UUID parentAccountId) {
+        this.parentAccountId = parentAccountId;
+    }
+
+    @Override
+    public void setIsPaymentDelegatedToParent(final boolean isPaymentDelegatedToParent) {
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
+    }
+
+    @Override
+    public Boolean isPaymentDelegatedToParent() {
+        return isPaymentDelegatedToParent;
+    }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
index a082328..c9cc74a 100644
--- a/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/svcs/DefaultAccountInternalApi.java
@@ -29,6 +29,7 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountEmail;
 import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.DefaultAccount;
 import org.killbill.billing.account.api.DefaultAccountEmail;
 import org.killbill.billing.account.api.DefaultMutableAccountData;
 import org.killbill.billing.account.api.ImmutableAccountData;
@@ -173,4 +174,15 @@ public class DefaultAccountInternalApi extends DefaultAccountApiBase implements 
         final ObjectType irrelevant = null;
         return new CacheLoaderArgument(irrelevant, args, context);
     }
+
+    @Override
+    public List<Account> getChildrenAccounts(final UUID parentAccountId, final InternalCallContext context) throws AccountApiException {
+        return ImmutableList.<Account>copyOf(Collections2.transform(accountDao.getAccountsByParentId(parentAccountId, context),
+                                                                    new Function<AccountModelDao, Account>() {
+                                                                        @Override
+                                                                        public Account apply(final AccountModelDao input) {
+                                                                            return new DefaultAccount(input);
+                                                                        }
+                                                                    }));
+    }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
index afd5664..1ddfd15 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountCreationEvent.java
@@ -110,6 +110,8 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
         private final String email;
         private final Integer billCycleDayLocal;
         private final String currency;
+        private final UUID parentAccountId;
+        private final Boolean isPaymentDelegatedToParent;
         private final UUID paymentMethodId;
         private final String timeZone;
         private final String locale;
@@ -121,6 +123,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
         private final String postalCode;
         private final String country;
         private final String phone;
+        private final String notes;
         private final Boolean isMigrated;
         private final Boolean isNotifiedForInvoices;
 
@@ -131,6 +134,8 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
                  d.getEmail(),
                  d.getBillingCycleDayLocal(),
                  d.getCurrency() != null ? d.getCurrency().name() : null,
+                 d.getParentAccountId(),
+                 d.getIsPaymentDelegatedToParent(),
                  d.getPaymentMethodId(),
                  d.getTimeZone() != null ? d.getTimeZone().getID() : null,
                  d.getLocale(),
@@ -142,6 +147,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
                  d.getPostalCode(),
                  d.getCountry(),
                  d.getPhone(),
+                 d.getNotes(),
                  d.getMigrated(),
                  d.getIsNotifiedForInvoices());
         }
@@ -153,6 +159,8 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
                                   @JsonProperty("email") final String email,
                                   @JsonProperty("billCycleDayLocal") final Integer billCycleDayLocal,
                                   @JsonProperty("currency") final String currency,
+                                  @JsonProperty("parentAccountId") final UUID parentAccountId,
+                                  @JsonProperty("isPaymentDelegatedToParent") final Boolean isPaymentDelegatedToParent,
                                   @JsonProperty("paymentMethodId") final UUID paymentMethodId,
                                   @JsonProperty("timeZone") final String timeZone,
                                   @JsonProperty("locale") final String locale,
@@ -164,6 +172,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
                                   @JsonProperty("postalCode") final String postalCode,
                                   @JsonProperty("country") final String country,
                                   @JsonProperty("phone") final String phone,
+                                  @JsonProperty("notes") final String notes,
                                   @JsonProperty("isMigrated") final Boolean isMigrated,
                                   @JsonProperty("isNotifiedForInvoices") final Boolean isNotifiedForInvoices) {
             this.externalKey = externalKey;
@@ -172,6 +181,8 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             this.email = email;
             this.billCycleDayLocal = billCycleDayLocal;
             this.currency = currency;
+            this.parentAccountId = parentAccountId;
+            this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
             this.paymentMethodId = paymentMethodId;
             this.timeZone = timeZone;
             this.locale = locale;
@@ -183,6 +194,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             this.postalCode = postalCode;
             this.country = country;
             this.phone = phone;
+            this.notes = notes;
             this.isMigrated = isMigrated;
             this.isNotifiedForInvoices = isNotifiedForInvoices;
         }
@@ -221,6 +233,17 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             }
         }
 
+        @Override
+        public UUID getParentAccountId() {
+            return parentAccountId;
+        }
+
+        @Override
+        @JsonIgnore
+        public Boolean isPaymentDelegatedToParent() {
+            return isPaymentDelegatedToParent;
+        }
+
         @JsonIgnore
         @Override
         public DateTimeZone getTimeZone() {
@@ -282,6 +305,11 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
         }
 
         @Override
+        public String getNotes() {
+            return notes;
+        }
+
+        @Override
         public UUID getPaymentMethodId() {
             return paymentMethodId;
         }
@@ -298,7 +326,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             return isNotifiedForInvoices;
         }
 
-        // These two getters are for Jackson serialization only
+        // These getters are for Jackson serialization only
 
         public Boolean getIsMigrated() {
             return isMigrated;
@@ -308,6 +336,10 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             return isNotifiedForInvoices;
         }
 
+        public Boolean getIsPaymentDelegatedToParent() {
+            return isPaymentDelegatedToParent;
+        }
+
         @Override
         public boolean equals(final Object o) {
             if (this == o) {
@@ -346,6 +378,12 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
                 return false;
             }
+            if (parentAccountId != null ? !parentAccountId.equals(that.parentAccountId) : that.parentAccountId != null) {
+                return false;
+            }
+            if (isPaymentDelegatedToParent != null ? !isPaymentDelegatedToParent.equals(that.isPaymentDelegatedToParent) : that.isPaymentDelegatedToParent != null) {
+                return false;
+            }
             if (email != null ? !email.equals(that.email) : that.email != null) {
                 return false;
             }
@@ -367,6 +405,9 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
                 return false;
             }
+            if (notes != null ? !notes.equals(that.notes) : that.notes != null) {
+                return false;
+            }
             if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
                 return false;
             }
@@ -388,6 +429,8 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             result = 31 * result + (email != null ? email.hashCode() : 0);
             result = 31 * result + (billCycleDayLocal != null ? billCycleDayLocal.hashCode() : 0);
             result = 31 * result + (currency != null ? currency.hashCode() : 0);
+            result = 31 * result + (parentAccountId != null ? parentAccountId.hashCode() : 0);
+            result = 31 * result + (isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent.hashCode() : 0);
             result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
             result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
             result = 31 * result + (locale != null ? locale.hashCode() : 0);
@@ -399,6 +442,7 @@ public class DefaultAccountCreationEvent extends BusEventBase implements Account
             result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
             result = 31 * result + (country != null ? country.hashCode() : 0);
             result = 31 * result + (phone != null ? phone.hashCode() : 0);
+            result = 31 * result + (notes != null ? notes.hashCode() : 0);
             result = 31 * result + (isMigrated != null ? isMigrated.hashCode() : 0);
             result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
             return result;
diff --git a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
index bef0089..8c75df8 100644
--- a/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/org/killbill/billing/account/api/user/DefaultAccountUserApi.java
@@ -32,6 +32,7 @@ import org.killbill.billing.account.api.DefaultAccountEmail;
 import org.killbill.billing.account.dao.AccountDao;
 import org.killbill.billing.account.dao.AccountEmailModelDao;
 import org.killbill.billing.account.dao.AccountModelDao;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -66,7 +67,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
 
     @Override
     public Account getAccountByKey(final String key, final TenantContext context) throws AccountApiException {
-        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         return getAccountByKey(key, internalTenantContext);
     }
 
@@ -84,8 +85,20 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
             throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, data.getExternalKey());
         }
 
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
+
+        if (data.getParentAccountId() != null) {
+            // verify that parent account exists if parentAccountId is not null
+            getAccountById(data.getParentAccountId(), internalContext);
+        }
+
         final AccountModelDao account = new AccountModelDao(data);
-        accountDao.create(account, internalCallContextFactory.createInternalCallContext(context));
+
+        if (null != account.getExternalKey() && account.getExternalKey().length() > 255) {
+            throw new AccountApiException(ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED);
+        }
+
+        accountDao.create(account, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
 
         return new DefaultAccount(account);
     }
@@ -97,7 +110,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
                                               new SourcePaginationBuilder<AccountModelDao, AccountApiException>() {
                                                   @Override
                                                   public Pagination<AccountModelDao> build() {
-                                                      return accountDao.searchAccounts(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return accountDao.searchAccounts(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               new Function<AccountModelDao, Account>() {
@@ -115,7 +128,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
                                               new SourcePaginationBuilder<AccountModelDao, AccountApiException>() {
                                                   @Override
                                                   public Pagination<AccountModelDao> build() {
-                                                      return accountDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return accountDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               new Function<AccountModelDao, Account>() {
@@ -129,7 +142,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
 
     @Override
     public UUID getIdFromKey(final String externalKey, final TenantContext context) throws AccountApiException {
-        return accountDao.getIdFromKey(externalKey, internalCallContextFactory.createInternalTenantContext(context));
+        return accountDao.getIdFromKey(externalKey, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
@@ -170,7 +183,7 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
 
     @Override
     public List<AccountEmail> getEmails(final UUID accountId, final TenantContext context) {
-        return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, internalCallContextFactory.createInternalTenantContext(context)),
+        return ImmutableList.<AccountEmail>copyOf(Collections2.transform(accountDao.getEmailsByAccountId(accountId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
                                                                          new Function<AccountEmailModelDao, AccountEmail>() {
                                                                              @Override
                                                                              public AccountEmail apply(final AccountEmailModelDao input) {
@@ -188,4 +201,15 @@ public class DefaultAccountUserApi extends DefaultAccountApiBase implements Acco
     public void removeEmail(final UUID accountId, final AccountEmail email, final CallContext context) {
         accountDao.removeEmail(new AccountEmailModelDao(email, false), internalCallContextFactory.createInternalCallContext(accountId, context));
     }
+
+    @Override
+    public List<Account> getChildrenAccounts(final UUID parentAccountId, final TenantContext context) throws AccountApiException {
+        return ImmutableList.<Account>copyOf(Collections2.transform(accountDao.getAccountsByParentId(parentAccountId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
+                                                                         new Function<AccountModelDao, Account>() {
+                                                                             @Override
+                                                                             public Account apply(final AccountModelDao input) {
+                                                                                 return new DefaultAccount(input);
+                                                                             }
+                                                                         }));
+    }
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
index c5cae6d..1446204 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountDao.java
@@ -52,4 +52,6 @@ public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountA
     List<AccountEmailModelDao> getEmailsByAccountId(UUID accountId, InternalTenantContext context);
 
     Integer getAccountBCD(UUID accountId, InternalTenantContext context);
+
+    List<AccountModelDao> getAccountsByParentId(UUID parentAccountId, InternalTenantContext context);
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
index ac70362..91fe1ce 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountModelDao.java
@@ -43,6 +43,8 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
     private String name;
     private Integer firstNameLength;
     private Currency currency;
+    private UUID parentAccountId;
+    private Boolean isPaymentDelegatedToParent;
     private int billingCycleDayLocal;
     private UUID paymentMethodId;
     private DateTimeZone timeZone;
@@ -55,23 +57,28 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
     private String country;
     private String postalCode;
     private String phone;
+    private String notes;
     private Boolean migrated;
     private Boolean isNotifiedForInvoices;
 
+
     public AccountModelDao() { /* For the DAO mapper */ }
 
     public AccountModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final String externalKey,
                            final String email, final String name, final Integer firstNameLength, final Currency currency,
+                           final UUID parentAccountId, final Boolean isPaymentDelegatedToParent,
                            final int billingCycleDayLocal, final UUID paymentMethodId, final DateTimeZone timeZone,
                            final String locale, final String address1, final String address2, final String companyName,
                            final String city, final String stateOrProvince, final String country, final String postalCode,
-                           final String phone, final Boolean migrated, final Boolean notifiedForInvoices) {
+                           final String phone, final String notes, final Boolean migrated, final Boolean notifiedForInvoices) {
         super(id, createdDate, updatedDate);
         this.externalKey = MoreObjects.firstNonNull(externalKey, id.toString());
         this.email = email;
         this.name = name;
         this.firstNameLength = firstNameLength;
         this.currency = currency;
+        this.parentAccountId = parentAccountId;
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
         this.billingCycleDayLocal = billingCycleDayLocal;
         this.paymentMethodId = paymentMethodId;
         this.timeZone = MoreObjects.firstNonNull(timeZone, DateTimeZone.UTC);
@@ -84,6 +91,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         this.country = country;
         this.postalCode = postalCode;
         this.phone = phone;
+        this.notes = notes;
         this.migrated = migrated;
         this.isNotifiedForInvoices = notifiedForInvoices;
     }
@@ -97,6 +105,8 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
              account.getName(),
              account.getFirstNameLength(),
              account.getCurrency(),
+             account.getParentAccountId(),
+             account.isPaymentDelegatedToParent(),
              MoreObjects.firstNonNull(account.getBillCycleDayLocal(), DEFAULT_BILLING_CYCLE_DAY_LOCAL),
              account.getPaymentMethodId(),
              account.getTimeZone(),
@@ -109,6 +119,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
              account.getCountry(),
              account.getPostalCode(),
              account.getPhone(),
+             account.getNotes(),
              account.isMigrated(),
              // There is a NOT NULL constraint on the is_notified_for_invoices column
              MoreObjects.firstNonNull(account.isNotifiedForInvoices(), false));
@@ -162,6 +173,22 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         this.currency = currency;
     }
 
+    public UUID getParentAccountId() {
+        return parentAccountId;
+    }
+
+    public void setParentAccountId(final UUID parentAccountId) {
+        this.parentAccountId = parentAccountId;
+    }
+
+    public Boolean getIsPaymentDelegatedToParent() {
+        return isPaymentDelegatedToParent;
+    }
+
+    public void setIsPaymentDelegatedToParent(final Boolean paymentDelegatedToParent) {
+        this.isPaymentDelegatedToParent = paymentDelegatedToParent;
+    }
+
     public Integer getBillingCycleDayLocal() {
         return billingCycleDayLocal;
     }
@@ -258,6 +285,14 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         this.phone = phone;
     }
 
+    public String getNotes() {
+        return notes;
+    }
+
+    public void setNotes(final String notes) {
+        this.notes = notes;
+    }
+
     public Boolean getMigrated() {
         return migrated;
     }
@@ -285,6 +320,8 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         sb.append(", name='").append(name).append('\'');
         sb.append(", firstNameLength=").append(firstNameLength);
         sb.append(", currency=").append(currency);
+        sb.append(", parentAccountId=").append(parentAccountId);
+        sb.append(", isPaymentDelegatedToParent=").append(isPaymentDelegatedToParent);
         sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
         sb.append(", paymentMethodId=").append(paymentMethodId);
         sb.append(", timeZone=").append(timeZone);
@@ -297,6 +334,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         sb.append(", country='").append(country).append('\'');
         sb.append(", postalCode='").append(postalCode).append('\'');
         sb.append(", phone='").append(phone).append('\'');
+        sb.append(", notes='").append(notes).append('\'');
         sb.append(", migrated=").append(migrated);
         sb.append(", isNotifiedForInvoices=").append(isNotifiedForInvoices);
         sb.append('}');
@@ -338,6 +376,12 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         if (currency != that.currency) {
             return false;
         }
+        if (parentAccountId != null ? !parentAccountId.equals(that.parentAccountId) : that.parentAccountId != null) {
+            return false;
+        }
+        if (isPaymentDelegatedToParent != null ? !isPaymentDelegatedToParent.equals(that.isPaymentDelegatedToParent) : that.isPaymentDelegatedToParent != null) {
+            return false;
+        }
         if (email != null ? !email.equals(that.email) : that.email != null) {
             return false;
         }
@@ -365,6 +409,9 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
             return false;
         }
+        if (notes != null ? !notes.equals(that.notes) : that.notes != null) {
+            return false;
+        }
         if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
             return false;
         }
@@ -386,6 +433,8 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         result = 31 * result + (name != null ? name.hashCode() : 0);
         result = 31 * result + (firstNameLength != null ? firstNameLength.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (parentAccountId != null ? parentAccountId.hashCode() : 0);
+        result = 31 * result + (isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent.hashCode() : 0);
         result = 31 * result + billingCycleDayLocal;
         result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
         result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
@@ -398,6 +447,7 @@ public class AccountModelDao extends EntityModelDaoBase implements EntityModelDa
         result = 31 * result + (country != null ? country.hashCode() : 0);
         result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
         result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (notes != null ? notes.hashCode() : 0);
         result = 31 * result + (migrated != null ? migrated.hashCode() : 0);
         result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
         return result;
diff --git a/account/src/main/java/org/killbill/billing/account/dao/AccountSqlDao.java b/account/src/main/java/org/killbill/billing/account/dao/AccountSqlDao.java
index 5395c85..d474747 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/AccountSqlDao.java
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.account.dao;
 
+import java.util.List;
 import java.util.UUID;
 
 import org.killbill.billing.account.api.Account;
@@ -55,4 +56,8 @@ public interface AccountSqlDao extends EntitySqlDao<AccountModelDao, Account> {
     public void updatePaymentMethod(@Bind("id") String accountId,
                                     @Bind("paymentMethodId") String paymentMethodId,
                                     @BindBean final InternalCallContext context);
+
+    @SqlQuery
+    List<AccountModelDao> getAccountsByParentId(@Bind("parentAccountId") UUID parentAccountId,
+                                                @BindBean final InternalTenantContext context);
 }
diff --git a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
index 7d9815d..f1186a2 100644
--- a/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/org/killbill/billing/account/dao/DefaultAccountDao.java
@@ -259,4 +259,14 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
             }
         });
     }
+
+    @Override
+    public List<AccountModelDao> getAccountsByParentId(final UUID parentAccountId, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AccountModelDao>>() {
+            @Override
+            public List<AccountModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(AccountSqlDao.class).getAccountsByParentId(parentAccountId, context);
+            }
+        });
+    }
 }
diff --git a/account/src/main/resources/org/killbill/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/org/killbill/billing/account/dao/AccountSqlDao.sql.stg
index a500028..d48a16b 100644
--- a/account/src/main/resources/org/killbill/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/org/killbill/billing/account/dao/AccountSqlDao.sql.stg
@@ -11,6 +11,8 @@ tableFields(prefix) ::= <<
 , <prefix>first_name_length
 , <prefix>currency
 , <prefix>billing_cycle_day_local
+, <prefix>parent_account_id
+, <prefix>is_payment_delegated_to_parent
 , <prefix>payment_method_id
 , <prefix>time_zone
 , <prefix>locale
@@ -22,6 +24,7 @@ tableFields(prefix) ::= <<
 , <prefix>country
 , <prefix>postal_code
 , <prefix>phone
+, <prefix>notes
 , <prefix>migrated
 , <prefix>is_notified_for_invoices
 , <prefix>created_by
@@ -37,6 +40,8 @@ tableValues() ::= <<
 , :firstNameLength
 , :currency
 , :billingCycleDayLocal
+, :parentAccountId
+, :isPaymentDelegatedToParent
 , :paymentMethodId
 , :timeZone
 , :locale
@@ -48,6 +53,7 @@ tableValues() ::= <<
 , :country
 , :postalCode
 , :phone
+, :notes
 , :migrated
 , :isNotifiedForInvoices
 , :createdBy
@@ -66,7 +72,7 @@ update() ::= <<
         currency = :currency, billing_cycle_day_local = :billingCycleDayLocal,
         payment_method_id = :paymentMethodId, time_zone = :timeZone, locale = :locale,
         address1 = :address1, address2 = :address2, company_name = :companyName, city = :city, state_or_province = :stateOrProvince,
-        country = :country, postal_code = :postalCode, phone = :phone,
+        country = :country, postal_code = :postalCode, phone = :phone, notes = :notes,
         is_notified_for_invoices = :isNotifiedForInvoices, updated_date = :updatedDate, updated_by = :updatedBy
     WHERE id = :id <AND_CHECK_TENANT()>;
 >>
@@ -106,3 +112,11 @@ getIdFromKey() ::= <<
     WHERE external_key = :externalKey <AND_CHECK_TENANT()>;
 >>
 
+getAccountsByParentId() ::= <<
+    select <allTableFields()>
+    from accounts
+    where parent_account_id = :parentAccountId
+    <AND_CHECK_TENANT()>
+    <defaultOrderBy()>
+    ;
+>>
\ No newline at end of file
diff --git a/account/src/main/resources/org/killbill/billing/account/ddl.sql b/account/src/main/resources/org/killbill/billing/account/ddl.sql
index 01158e4..295a88d 100644
--- a/account/src/main/resources/org/killbill/billing/account/ddl.sql
+++ b/account/src/main/resources/org/killbill/billing/account/ddl.sql
@@ -4,12 +4,14 @@ DROP TABLE IF EXISTS accounts;
 CREATE TABLE accounts (
     record_id serial unique,
     id varchar(36) NOT NULL,
-    external_key varchar(128) NULL,
+    external_key varchar(255) NULL,
     email varchar(128) DEFAULT NULL,
     name varchar(100) DEFAULT NULL,
     first_name_length int DEFAULT NULL,
     currency varchar(3) DEFAULT NULL,
     billing_cycle_day_local int DEFAULT NULL,
+    parent_account_id varchar(36) DEFAULT NULL,
+    is_payment_delegated_to_parent boolean DEFAULT FALSE,
     payment_method_id varchar(36) DEFAULT NULL,
     time_zone varchar(50) NOT NULL,
     locale varchar(5) DEFAULT NULL,
@@ -21,6 +23,7 @@ CREATE TABLE accounts (
     country varchar(50) DEFAULT NULL,
     postal_code varchar(16) DEFAULT NULL,
     phone varchar(25) DEFAULT NULL,
+    notes varchar(4096) DEFAULT NULL,
     migrated boolean default false,
     is_notified_for_invoices boolean NOT NULL,
     created_date datetime NOT NULL,
@@ -39,13 +42,15 @@ CREATE TABLE account_history (
     record_id serial unique,
     id varchar(36) NOT NULL,
     target_record_id bigint /*! unsigned */ not null,
-    external_key varchar(128) NULL,
+    external_key varchar(255) NULL,
     email varchar(128) DEFAULT NULL,
     name varchar(100) DEFAULT NULL,
     first_name_length int DEFAULT NULL,
     currency varchar(3) DEFAULT NULL,
     billing_cycle_day_local int DEFAULT NULL,
+    parent_account_id varchar(36) DEFAULT NULL,
     payment_method_id varchar(36) DEFAULT NULL,
+    is_payment_delegated_to_parent boolean default false,
     time_zone varchar(50) NOT NULL,
     locale varchar(5) DEFAULT NULL,
     address1 varchar(100) DEFAULT NULL,
@@ -56,6 +61,7 @@ CREATE TABLE account_history (
     country varchar(50) DEFAULT NULL,
     postal_code varchar(16) DEFAULT NULL,
     phone varchar(25) DEFAULT NULL,
+    notes varchar(4096) DEFAULT NULL,
     migrated boolean default false,
     is_notified_for_invoices boolean NOT NULL,
     change_type varchar(6) NOT NULL,
diff --git a/account/src/main/resources/org/killbill/billing/account/migration/V20160822115745__account_notes.sql b/account/src/main/resources/org/killbill/billing/account/migration/V20160822115745__account_notes.sql
new file mode 100644
index 0000000..3941016
--- /dev/null
+++ b/account/src/main/resources/org/killbill/billing/account/migration/V20160822115745__account_notes.sql
@@ -0,0 +1,2 @@
+alter table accounts add column notes varchar(4096) DEFAULT NULL after phone;
+alter table account_history add column notes varchar(4096) DEFAULT NULL after phone;
\ No newline at end of file
diff --git a/account/src/main/resources/org/killbill/billing/account/migration/V20161004171445__account_external_keys.sql b/account/src/main/resources/org/killbill/billing/account/migration/V20161004171445__account_external_keys.sql
new file mode 100644
index 0000000..1809a23
--- /dev/null
+++ b/account/src/main/resources/org/killbill/billing/account/migration/V20161004171445__account_external_keys.sql
@@ -0,0 +1,2 @@
+alter table accounts modify external_key varchar(255);
+alter table account_history modify external_key varchar(255);
\ No newline at end of file
diff --git a/account/src/main/resources/org/killbill/billing/account/migration/V20163917173936__parent_account_456.sql b/account/src/main/resources/org/killbill/billing/account/migration/V20163917173936__parent_account_456.sql
new file mode 100644
index 0000000..889ece8
--- /dev/null
+++ b/account/src/main/resources/org/killbill/billing/account/migration/V20163917173936__parent_account_456.sql
@@ -0,0 +1,5 @@
+alter table accounts add column parent_account_id varchar(36) DEFAULT NULL after billing_cycle_day_local;
+alter table accounts add column is_payment_delegated_to_parent boolean DEFAULT FALSE after parent_account_id;
+
+alter table account_history add column parent_account_id varchar(36) DEFAULT NULL after billing_cycle_day_local;
+alter table account_history add column is_payment_delegated_to_parent boolean DEFAULT FALSE after parent_account_id;
diff --git a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
index 88bcf6b..1e146a5 100644
--- a/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
+++ b/account/src/test/java/org/killbill/billing/account/AccountTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -16,21 +18,13 @@
 
 package org.killbill.billing.account;
 
-import org.killbill.billing.ObjectType;
+import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-
-import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.dao.AccountDao;
 import org.killbill.billing.account.glue.TestAccountModuleWithEmbeddedDB;
-import org.killbill.bus.api.PersistentBus;
-import org.killbill.clock.Clock;
 import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.customfield.dao.CustomFieldDao;
@@ -38,6 +32,11 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.tag.api.user.TagEventBuilder;
 import org.killbill.billing.util.tag.dao.TagDao;
 import org.killbill.billing.util.tag.dao.TagDefinitionDao;
+import org.killbill.bus.api.PersistentBus;
+import org.killbill.clock.Clock;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
@@ -89,9 +88,7 @@ public abstract class AccountTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     protected Account createAccount(final AccountData accountData) throws AccountApiException {
         final Account account = accountUserApi.createAccount(accountData, callContext);
 
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        refreshCallContext(account.getId());
 
         return account;
     }
diff --git a/account/src/test/java/org/killbill/billing/account/AccountTestUtils.java b/account/src/test/java/org/killbill/billing/account/AccountTestUtils.java
index 9c6220d..7934e12 100644
--- a/account/src/test/java/org/killbill/billing/account/AccountTestUtils.java
+++ b/account/src/test/java/org/killbill/billing/account/AccountTestUtils.java
@@ -101,10 +101,11 @@ public abstract class AccountTestUtils {
         final String stateOrProvince = UUID.randomUUID().toString();
         final String country = Locale.GERMANY.getCountry();
         final String postalCode = UUID.randomUUID().toString().substring(0, 4);
+        final String notes = UUID.randomUUID().toString();
 
-        return new DefaultMutableAccountData(externalKey, email, name, firstNameLength, currency,
+        return new DefaultMutableAccountData(externalKey, email, name, firstNameLength, currency, null, false,
                                              billCycleDayLocal, paymentMethodId, timeZone,
                                              locale, address1, address2, companyName, city, stateOrProvince,
-                                             country, postalCode, phone, false, true);
+                                             country, postalCode, phone, notes, false, true);
     }
 }
diff --git a/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java b/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
index 1d8c654..f88b9e0 100644
--- a/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
+++ b/account/src/test/java/org/killbill/billing/account/api/TestDefaultAccount.java
@@ -151,6 +151,7 @@ public class TestDefaultAccount extends AccountTestSuiteNoDB {
         Assert.assertEquals(finalAccount.getCountry(), delegateAccount.getCountry());
         Assert.assertEquals(finalAccount.getPostalCode(), delegateAccount.getPostalCode());
         Assert.assertEquals(finalAccount.getPhone(), delegateAccount.getPhone());
+        Assert.assertEquals(finalAccount.getNotes(), delegateAccount.getNotes());
         Assert.assertEquals(finalAccount.isMigrated(), delegateAccount.isMigrated());
         Assert.assertEquals(finalAccount.isNotifiedForInvoices(), delegateAccount.isNotifiedForInvoices());
     }
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
index 1b4f12a..338c58c 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApi.java
@@ -23,8 +23,10 @@ import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Callable;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.AccountTestSuiteWithEmbeddedDB;
 import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.DefaultAccount;
 import org.killbill.billing.account.api.DefaultMutableAccountData;
@@ -40,6 +42,7 @@ import com.google.common.eventbus.Subscribe;
 import static com.jayway.awaitility.Awaitility.await;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.killbill.billing.account.AccountTestUtils.createTestAccount;
+import static org.testng.Assert.assertEquals;
 
 public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
 
@@ -70,9 +73,9 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
         final Account account = createAccount(new DefaultAccount(createTestAccount()));
 
         // Update the address and leave other fields null
-        final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, 0, null,
+        final MutableAccountData mutableAccountData = new DefaultMutableAccountData(null, null, null, 0, null, null, false, 0, null,
                                                                                     null, null, null, null, null, null,
-                                                                                    null, null, null, null, false, false);
+                                                                                    null, null, null, null, null, false, false);
         final String newAddress1 = UUID.randomUUID().toString();
         mutableAccountData.setAddress1(newAddress1);
 
@@ -129,4 +132,49 @@ public class TestDefaultAccountUserApi extends AccountTestSuiteWithEmbeddedDB {
             return accountCreationInternalEvents;
         }
     }
+
+    @Test(groups = "slow", description = "Test Account create Parent and Child")
+    public void testCreateParentAndChildAccounts() throws Exception {
+
+        final Account parentAccount = accountUserApi.createAccount(new DefaultAccount(createTestAccount()), callContext);
+
+        final AccountModelDao childAccountModel = createTestAccount();
+        childAccountModel.setParentAccountId(parentAccount.getId());
+        childAccountModel.setIsPaymentDelegatedToParent(true);
+        final AccountData childAccountData = new DefaultAccount(childAccountModel);
+        final Account childAccount = accountUserApi.createAccount(childAccountData, callContext);
+
+        final Account retrievedChildAccount = accountUserApi.getAccountById(childAccount.getId(), callContext);
+
+        Assert.assertNull(parentAccount.getParentAccountId());
+        Assert.assertNotNull(retrievedChildAccount.getParentAccountId());
+        Assert.assertEquals(retrievedChildAccount.getId(), childAccount.getId());
+        Assert.assertEquals(retrievedChildAccount.getParentAccountId(), parentAccount.getId());
+        Assert.assertEquals(retrievedChildAccount.isPaymentDelegatedToParent(), childAccount.isPaymentDelegatedToParent());
+    }
+
+    @Test(groups = "slow", description = "Test Account create Child with a non existing Parent",
+            expectedExceptions = AccountApiException.class, expectedExceptionsMessageRegExp = "Account does not exist for id .*")
+    public void testCreateChildAccountWithInvalidParent() throws Exception {
+
+        final AccountModelDao childAccountModel = createTestAccount();
+        childAccountModel.setParentAccountId(UUID.randomUUID());
+        final AccountData childAccountData = new DefaultAccount(childAccountModel);
+        final Account childAccount = accountUserApi.createAccount(childAccountData, callContext);
+
+    }
+
+    @Test(groups = "slow", description = "Test Account creation with External Key over limit")
+        public void testCreateAccountWithExternalKeyOverLimit() throws Exception {
+        AccountModelDao accountModelDao = createTestAccount();
+        // Set an externalKey of 256 characters (over limit)
+        accountModelDao.setExternalKey("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.");
+        final AccountData accountData = new DefaultAccount(accountModelDao);
+        try {
+            accountUserApi.createAccount(accountData, callContext);
+            Assert.fail();
+        } catch (final AccountApiException e) {
+            assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
+        }
+    }
 }
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
index 6cf12f7..793d689 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestDefaultAccountUserApiWithMocks.java
@@ -76,11 +76,12 @@ public class TestDefaultAccountUserApiWithMocks extends AccountTestSuiteNoDB {
         final String country = UUID.randomUUID().toString();
         final String postalCode = UUID.randomUUID().toString();
         final String phone = UUID.randomUUID().toString();
+        final String notes = UUID.randomUUID().toString();
         final Boolean isMigrated = true;
         final Boolean isNotifiedForInvoices = false;
-        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, billCycleDay,
+        final AccountData data = new DefaultAccount(id, externalKey, email, name, firstNameLength, currency, null, false, billCycleDay,
                                                     paymentMethodId, timeZone, locale, address1, address2, companyName,
-                                                    city, stateOrProvince, country, postalCode, phone, isMigrated, isNotifiedForInvoices);
+                                                    city, stateOrProvince, country, postalCode, phone, notes, isMigrated, isNotifiedForInvoices);
 
         accountUserApi.createAccount(data, callContext);
 
diff --git a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
index 23e3aa3..c5176da 100644
--- a/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
+++ b/account/src/test/java/org/killbill/billing/account/api/user/TestEventJson.java
@@ -50,8 +50,8 @@ public class TestEventJson extends AccountTestSuiteNoDB {
 
     @Test(groups = "fast", description="Test Account event serialization")
     public void testAccountCreationEvent() throws Exception {
-        final DefaultAccountData data = new DefaultAccountData("dsfdsf", "bobo", 3, "bobo@yahoo.com", 12, "USD", UUID.randomUUID(),
-                                                               "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887", false, false);
+        final DefaultAccountData data = new DefaultAccountData("dsfdsf", "bobo", 3, "bobo@yahoo.com", 12, "USD", null, false, UUID.randomUUID(),
+                                                               "UTC", "US", "21 avenue", "", "Gling", "San Franciso", "CA", "94110", "USA", "4126789887", "notes", false, false);
         final DefaultAccountCreationEvent e = new DefaultAccountCreationEvent(data, UUID.randomUUID(), 1L, 2L, null);
         final String json = mapper.writeValueAsString(e);
 
diff --git a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
index 0c33683..9d097c4 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/MockAccountDao.java
@@ -53,6 +53,7 @@ import com.google.inject.Inject;
 
 public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, AccountApiException> implements AccountDao {
 
+    private final MockEntityDaoBase<AccountModelDao, Account, AccountApiException> accountSqlDao = new MockEntityDaoBase<AccountModelDao, Account, AccountApiException>();
     private final MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException> accountEmailSqlDao = new MockEntityDaoBase<AccountEmailModelDao, AccountEmail, AccountApiException>();
     private final PersistentBus eventBus;
     private final Clock clock;
@@ -175,4 +176,14 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, 
         final AccountModelDao account = getById(accountId, context);
         return account != null ? account.getBillingCycleDayLocal() : 0;
     }
+
+    @Override
+    public List<AccountModelDao> getAccountsByParentId(final UUID parentAccountId, final InternalTenantContext context) {
+        return ImmutableList.<AccountModelDao>copyOf(Iterables.<AccountModelDao>filter(accountSqlDao.getAll(context), new Predicate<AccountModelDao>() {
+            @Override
+            public boolean apply(final AccountModelDao input) {
+                return parentAccountId.equals(input.getParentAccountId());
+            }
+        }));
+    }
 }
diff --git a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
index 1592f85..237b8f5 100644
--- a/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/org/killbill/billing/account/dao/TestAccountDao.java
@@ -33,8 +33,6 @@ import org.killbill.billing.account.api.DefaultAccount;
 import org.killbill.billing.account.api.DefaultAccountEmail;
 import org.killbill.billing.account.api.DefaultMutableAccountData;
 import org.killbill.billing.account.api.MutableAccountData;
-import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -64,10 +62,10 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
     public void testMinimalFields() throws Exception {
         final String email = UUID.randomUUID().toString();
         final String name = UUID.randomUUID().toString();
-        final AccountData accountData = new DefaultMutableAccountData(null, email, name, 0, null,
+        final AccountData accountData = new DefaultMutableAccountData(null, email, name, 0, null, null, false,
                                                                       0, null, null, null, null,
                                                                       null, null, null, null, null,
-                                                                      null, null, false, true);
+                                                                      null, null, null, false, true);
         final AccountModelDao account = new AccountModelDao(UUID.randomUUID(), accountData);
         accountDao.create(account, internalCallContext);
 
@@ -112,9 +110,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
         // Special test to verify audits - they are handled a bit differently due to the account record id (see EntitySqlDaoWrapperInvocationHandler#insertAudits)
         final AccountModelDao account1 = createTestAccount();
         accountDao.create(account1, internalCallContext);
-        final Long account1RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account1.getId(), ObjectType.ACCOUNT, null);
-        internalCallContext.setAccountRecordId(account1RecordId);
-        internalCallContext.setReferenceDateTimeZone(account1.getTimeZone());
+        refreshCallContext(account1.getId());
 
         // Verify audits via account record id
         final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId1 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
@@ -127,16 +123,14 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
 
         final AccountModelDao account2 = createTestAccount();
         accountDao.create(account2, internalCallContext);
-        final Long account2RecordId = nonEntityDao.retrieveAccountRecordIdFromObject(account2.getId(), ObjectType.ACCOUNT, null);
-        internalCallContext.setAccountRecordId(account2RecordId);
-        internalCallContext.setReferenceDateTimeZone(account2.getTimeZone());
+        refreshCallContext(account2.getId());
 
         // Verify audits via account record id
         final DefaultAccountAuditLogs auditLogsForAccount2ViaAccountRecordId = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().size(), 1);
         Assert.assertEquals(auditLogsForAccount2ViaAccountRecordId.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
 
-        internalCallContext.setAccountRecordId(account1RecordId);
+        refreshCallContext(account1.getId());
         final DefaultAccountAuditLogs auditLogsForAccount1ViaAccountRecordId2 = auditDao.getAuditLogsForAccountRecordId(AuditLevel.FULL, internalCallContext);
         Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().size(), 2);
         Assert.assertEquals(auditLogsForAccount1ViaAccountRecordId2.getAuditLogsForAccount().get(0).getChangeType(), ChangeType.INSERT);
diff --git a/account/src/test/java/org/killbill/billing/account/glue/TestAccountModule.java b/account/src/test/java/org/killbill/billing/account/glue/TestAccountModule.java
index 044e4f9..0524129 100644
--- a/account/src/test/java/org/killbill/billing/account/glue/TestAccountModule.java
+++ b/account/src/test/java/org/killbill/billing/account/glue/TestAccountModule.java
@@ -24,6 +24,7 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.AuditModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.TagStoreModule;
 
@@ -39,6 +40,7 @@ public class TestAccountModule extends DefaultAccountModule {
 
         install(new AuditModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new CustomFieldModule(configSource));
         install(new MockTenantModule(configSource));

api/pom.xml 6(+1 -5)

diff --git a/api/pom.xml b/api/pom.xml
index 5708b20..8936827 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>
@@ -53,10 +53,6 @@
             <artifactId>killbill-plugin-api-invoice</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.kill-bill.billing.plugin</groupId>
-            <artifactId>killbill-plugin-api-payment</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-clock</artifactId>
         </dependency>
diff --git a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
index ad70272..f935ae7 100644
--- a/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/account/api/AccountInternalApi.java
@@ -43,4 +43,6 @@ public interface AccountInternalApi extends ImmutableAccountInternalApi {
     void updatePaymentMethod(UUID accountId, UUID paymentMethodId, InternalCallContext context) throws AccountApiException;
 
     UUID getByRecordId(Long recordId, InternalTenantContext context) throws AccountApiException;
+
+    List<Account> getChildrenAccounts(UUID parentAccountId, InternalCallContext context) throws AccountApiException;
 }
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
index a06f003..7c9b1cd 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalCallContext.java
@@ -45,7 +45,8 @@ public class InternalCallContext extends InternalTenantContext {
 
     public InternalCallContext(final Long tenantRecordId,
                                @Nullable final Long accountRecordId,
-                               @Nullable final DateTimeZone referenceDateTimeZone,
+                               @Nullable final DateTimeZone fixedOffsetTimeZone,
+                               @Nullable final DateTime referenceDateTime,
                                final UUID userToken,
                                final String userName,
                                final CallOrigin callOrigin,
@@ -54,7 +55,7 @@ public class InternalCallContext extends InternalTenantContext {
                                final String comment,
                                final DateTime createdDate,
                                final DateTime updatedDate) {
-        super(tenantRecordId, accountRecordId, referenceDateTimeZone);
+        super(tenantRecordId, accountRecordId, fixedOffsetTimeZone, referenceDateTime);
         this.userToken = userToken;
         this.createdBy = userName;
         this.updatedBy = userName;
@@ -62,24 +63,42 @@ public class InternalCallContext extends InternalTenantContext {
         this.contextUserType = userType;
         this.reasonCode = reasonCode;
         this.comments = comment;
-        this.createdDate = toUTCDateTime(createdDate);
-        this.updatedDate = toUTCDateTime(updatedDate);
-    }
-
-    public InternalCallContext(final Long tenantRecordId, final CallContext callContext) {
-        this(tenantRecordId, null, null, callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(),
-             callContext.getUserType(), callContext.getReasonCode(), callContext.getComments(), callContext.getCreatedDate(),
-             callContext.getUpdatedDate());
-    }
-
-    public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final DateTimeZone referenceDateTimeZone) {
-        this(context.getTenantRecordId(), accountRecordId, referenceDateTimeZone, context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
-             context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(),
-             context.getUpdatedDate());
+        this.createdDate = createdDate;
+        this.updatedDate = updatedDate;
+    }
+
+    public InternalCallContext(final Long tenantRecordId, final CallContext callContext, final DateTime utcNow) {
+        this(tenantRecordId,
+             null,
+             null,
+             null,
+             callContext.getUserToken(),
+             callContext.getUserName(),
+             callContext.getCallOrigin(),
+             callContext.getUserType(),
+             callContext.getReasonCode(),
+             callContext.getComments(),
+             utcNow,
+             utcNow);
+    }
+
+    public InternalCallContext(final InternalCallContext context, final Long accountRecordId, final DateTimeZone fixedOffsetTimeZone, final DateTime referenceDateTime, final DateTime utcNow) {
+        this(context.getTenantRecordId(),
+             accountRecordId,
+             fixedOffsetTimeZone,
+             referenceDateTime,
+             context.getUserToken(),
+             context.getCreatedBy(),
+             context.getCallOrigin(),
+             context.getContextUserType(),
+             context.getReasonCode(),
+             context.getComments(),
+             utcNow,
+             utcNow);
     }
 
     public InternalCallContext(final InternalCallContext context, final DateTime updatedDate) {
-        this(context.getTenantRecordId(), context.getAccountRecordId(), context.getReferenceDateTimeZone(), context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
+        this(context.getTenantRecordId(), context.getAccountRecordId(), context.getFixedOffsetTimeZone(), context.getReferenceDateTime(), context.getUserToken(), context.getCreatedBy(), context.getCallOrigin(),
              context.getContextUserType(), context.getReasonCode(), context.getComments(), context.getCreatedDate(), updatedDate);
     }
 
diff --git a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
index 003fb19..8880c72 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/InternalTenantContext.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.util.callcontext.TenantContext;
 
@@ -35,14 +36,15 @@ public class InternalTenantContext extends TimeAwareContext {
 
     public InternalTenantContext(final Long tenantRecordId,
                                  @Nullable final Long accountRecordId,
-                                 @Nullable final DateTimeZone referenceDateTimeZone) {
-        super(referenceDateTimeZone);
+                                 @Nullable final DateTimeZone fixedOffsetTimeZone,
+                                 @Nullable final DateTime referenceDateTime) {
+        super(fixedOffsetTimeZone, referenceDateTime);
         this.tenantRecordId = tenantRecordId;
         this.accountRecordId = accountRecordId;
     }
 
     public InternalTenantContext(final Long defaultTenantRecordId) {
-        this(defaultTenantRecordId, null, null);
+        this(defaultTenantRecordId, null, null, null);
     }
 
     public TenantContext toTenantContext(final UUID tenantId) {
diff --git a/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
index b265a4b..2d23d02 100644
--- a/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
+++ b/api/src/main/java/org/killbill/billing/callcontext/TimeAwareContext.java
@@ -17,82 +17,62 @@
 
 package org.killbill.billing.callcontext;
 
-import java.util.Date;
+import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.joda.time.LocalTime;
+import org.killbill.clock.ClockUtil;
 
-// TODO Cache the reference time and clock in the context
 public class TimeAwareContext {
 
-    private final DateTimeZone referenceDateTimeZone;
+    private final DateTimeZone fixedOffsetTimeZone;
+    private final DateTime referenceDateTime;
+    private final LocalTime referenceTime;
 
-    public TimeAwareContext(final DateTimeZone referenceDateTimeZone) {
-        this.referenceDateTimeZone = referenceDateTimeZone;
+    public TimeAwareContext(@Nullable final DateTimeZone fixedOffsetTimeZone, @Nullable final DateTime referenceDateTime) {
+        this.fixedOffsetTimeZone = fixedOffsetTimeZone;
+        this.referenceDateTime = referenceDateTime;
+        this.referenceTime = computeReferenceTime(referenceDateTime);
     }
 
-    /// Generic functions
-    /// TODO Move to ClockUtil
-
-    // Create a DateTime object forcing the time zone to be UTC
-    protected DateTime toUTCDateTime(final DateTime dateTime) {
-        return toDateTime(dateTime, DateTimeZone.UTC);
-    }
+    public DateTime toUTCDateTime(final LocalDate localDate) {
+        validateContext();
 
-    // Create a DateTime object using the specified timezone (usually, the one on the account)
-    public DateTime toDateTime(final DateTime dateTime, final DateTimeZone accountTimeZone) {
-        return dateTime.toDateTime(accountTimeZone);
+        return ClockUtil.toUTCDateTime(localDate, getReferenceTime(), getFixedOffsetTimeZone());
     }
 
-    /// DateTime <-> LocalDate transformations
-
-    // Create a DateTime object using the specified reference time and timezone (usually, the one on the account)
-    public DateTime toUTCDateTime(final LocalDate localDate, final DateTime referenceDateTime) {
+    public LocalDate toLocalDate(final DateTime dateTime) {
         validateContext();
 
-        final DateTimeZone normalizedAccountTimezone = getNormalizedAccountTimezone(referenceDateTime);
-
-        final LocalTime referenceLocalTime = toDateTime(referenceDateTime, normalizedAccountTimezone).toLocalTime();
-
-        final DateTime targetDateTime = new DateTime(localDate.getYear(),
-                                                     localDate.getMonthOfYear(),
-                                                     localDate.getDayOfMonth(),
-                                                     referenceLocalTime.getHourOfDay(),
-                                                     referenceLocalTime.getMinuteOfHour(),
-                                                     referenceLocalTime.getSecondOfMinute(),
-                                                     normalizedAccountTimezone);
-
-        return toUTCDateTime(targetDateTime);
+        return ClockUtil.toLocalDate(dateTime, getFixedOffsetTimeZone());
     }
 
-    // Create a LocalDate object using the specified timezone (usually, the one on the account), respecting the offset at the time of the referenceDateTime
-    public LocalDate toLocalDate(final DateTime dateTime, final DateTime referenceDateTime) {
-        validateContext();
+    private void validateContext() {
+        if (getFixedOffsetTimeZone() == null || getReferenceTime() == null) {
+            throw new IllegalArgumentException(String.format("Context mis-configured: fixedOffsetTimeZone=%s, referenceTime=%s", getFixedOffsetTimeZone(), getReferenceTime()));
+        }
+    }
 
-        final DateTimeZone normalizedAccountTimezone = getNormalizedAccountTimezone(referenceDateTime);
-        return new LocalDate(dateTime, normalizedAccountTimezone);
+    DateTime getReferenceDateTime() {
+        return referenceDateTime;
     }
 
-    private DateTimeZone getNormalizedAccountTimezone(final DateTime referenceDateTime) {
-        // Check if DST was in effect at the reference date time
-        final boolean shouldUseDST = !getReferenceDateTimeZone().isStandardOffset(referenceDateTime.getMillis());
-        if (shouldUseDST) {
-            return DateTimeZone.forOffsetMillis(getReferenceDateTimeZone().getOffset(referenceDateTime.getMillis()));
-        } else {
-            return DateTimeZone.forOffsetMillis(getReferenceDateTimeZone().getStandardOffset(referenceDateTime.getMillis()));
-        }
+    // For convenience (used in tests)
+
+    //@VisibleForTesting
+    protected LocalTime computeReferenceTime(@Nullable final DateTime referenceTime) {
+        return referenceTime == null ? null : ClockUtil.toDateTime(referenceTime, getFixedOffsetTimeZone()).toLocalTime();
     }
 
-    private void validateContext() {
-        if (getReferenceDateTimeZone() == null) {
-            throw new IllegalArgumentException(String.format("Context mis-configured: getReferenceDateTimeZone()=%s", getReferenceDateTimeZone()));
-        }
+    //@VisibleForTesting
+    public DateTimeZone getFixedOffsetTimeZone() {
+        return fixedOffsetTimeZone;
     }
 
-    // For convenience, to be overridden in tests
-    protected DateTimeZone getReferenceDateTimeZone() {
-        return referenceDateTimeZone;
+    //@VisibleForTesting
+    public LocalTime getReferenceTime() {
+        return referenceTime;
     }
 }
diff --git a/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java b/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
index f461328..db775e2 100644
--- a/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
+++ b/api/src/main/java/org/killbill/billing/catalog/api/CatalogService.java
@@ -20,14 +20,13 @@ package org.killbill.billing.catalog.api;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.platform.api.KillbillService;
-import org.killbill.billing.util.callcontext.TenantContext;
 
 /**
  * The interface {@code CatalogService} is a {@code KillbillService} required to handle catalog operations.
  */
 public interface CatalogService extends KillbillService {
 
-    public Catalog getFullCatalog(InternalTenantContext context) throws CatalogApiException;
+    public Catalog getFullCatalog(boolean useDefaultCatalog, final boolean filterTemplateCatalog, InternalTenantContext context) throws CatalogApiException;
 
-    public StaticCatalog getCurrentCatalog(InternalTenantContext context) throws CatalogApiException;
+    public StaticCatalog getCurrentCatalog(boolean useDefaultCatalog, final boolean filterTemplateCatalog, InternalTenantContext context) throws CatalogApiException;
 }
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java b/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
index f2eef61..c524651 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EntitlementInternalApi.java
@@ -30,7 +30,7 @@ import org.killbill.billing.payment.api.PluginProperty;
 
 public interface EntitlementInternalApi {
 
-    AccountEntitlements getAllEntitlementsForAccountId(UUID accountId, InternalTenantContext context) throws EntitlementApiException;
+    AccountEntitlements getAllEntitlementsForAccount(InternalTenantContext context) throws EntitlementApiException;
 
     Entitlement getEntitlementForId(final UUID uuid, final InternalTenantContext tenantContext) throws EntitlementApiException;
 
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index b2e6d4b..42d9f4a 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -42,8 +42,14 @@ public interface EventsStream {
 
     EntitlementState getEntitlementState();
 
+    LocalDate getEntitlementEffectiveStartDate();
+
     LocalDate getEntitlementEffectiveEndDate();
 
+    DateTime getEntitlementEffectiveStartDateTime();
+
+    DateTime getEntitlementEffectiveEndDateTime();
+
     SubscriptionBase getSubscriptionBase();
 
     SubscriptionBase getBasePlanSubscriptionBase();
@@ -56,6 +62,8 @@ public interface EventsStream {
 
     boolean isSubscriptionCancelled();
 
+    int getDefaultBillCycleDayLocal();
+
     Collection<BlockingState> getPendingEntitlementCancellationEvents();
 
     BlockingState getEntitlementCancellationEvent();
diff --git a/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java b/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
index b31dbdd..010901d 100644
--- a/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
+++ b/api/src/main/java/org/killbill/billing/events/BlockingTransitionInternalEvent.java
@@ -18,6 +18,7 @@ package org.killbill.billing.events;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 
 // Event for effective blocking state changes (not entitlement specific)
@@ -27,6 +28,12 @@ public interface BlockingTransitionInternalEvent extends BusInternalEvent {
 
     public BlockingStateType getBlockingType();
 
+    public String getStateName();
+
+    public String getService();
+
+    public DateTime getEffectiveDate();
+
     public Boolean isTransitionedToBlockedBilling();
 
     public Boolean isTransitionedToUnblockedBilling();
diff --git a/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java b/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
index ea74974..1073d87 100644
--- a/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
+++ b/api/src/main/java/org/killbill/billing/events/SubscriptionInternalEvent.java
@@ -46,6 +46,8 @@ public interface SubscriptionInternalEvent extends BusInternalEvent {
 
     String getPreviousPhase();
 
+    Integer getPreviousBillCycleDayLocal();
+
     String getNextPlan();
 
     String getNextPhase();
@@ -54,6 +56,8 @@ public interface SubscriptionInternalEvent extends BusInternalEvent {
 
     String getNextPriceList();
 
+    Integer getNextBillCycleDayLocal();
+
     Integer getRemainingEventsForUserOperation();
 
     Long getTotalOrdering();
diff --git a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
index e782366..ce939ec 100644
--- a/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
+++ b/api/src/main/java/org/killbill/billing/glue/InvoiceModule.java
@@ -18,11 +18,11 @@ package org.killbill.billing.glue;
 
 public interface InvoiceModule {
 
+    static final String STATIC_CONFIG = "StaticConfig";
+
     public void installInvoiceUserApi();
 
     public void installInvoicePaymentApi();
 
-    public void installInvoiceMigrationApi();
-
     public void installInvoiceInternalApi();
 }
diff --git a/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java b/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
index d561355..b970560 100644
--- a/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
+++ b/api/src/main/java/org/killbill/billing/glue/SubscriptionModule.java
@@ -22,8 +22,6 @@ public interface SubscriptionModule {
 
     public void installSubscriptionTransferApi();
 
-    public void installSubscriptionMigrationApi();
-
     public void installSubscriptionInternalApi();
 
     public void installSubscriptionTimelineApi();
diff --git a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
index 5f9d7b5..8dabb05 100644
--- a/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/invoice/api/InvoiceInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2011 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -62,7 +62,9 @@ public interface InvoiceInternalApi {
     public InvoicePayment recordRefund(UUID paymentId, BigDecimal amount, boolean isInvoiceAdjusted, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts,
                                        String transactionExternalKey, InternalCallContext context) throws InvoiceApiException;
 
-    public InvoicePayment recordChargeback(UUID paymentId, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
+    public InvoicePayment recordChargeback(UUID paymentId, String chargebackTransactionExternalKey, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
+
+    public InvoicePayment recordChargebackReversal(UUID paymentId, String chargebackTransactionExternalKey, InternalCallContext context) throws InvoiceApiException;
 
     /**
      * Rebalance CBA for account which have credit and unpaid invoices
@@ -73,4 +75,6 @@ public interface InvoiceInternalApi {
     public void consumeExistingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) throws InvoiceApiException;
 
     public Map<UUID, BigDecimal> validateInvoiceItemAdjustments(final UUID paymentId, final Map<UUID, BigDecimal> idWithAmount, final InternalTenantContext context) throws InvoiceApiException;
+
+    public void commitInvoice(UUID invoiceId, InternalCallContext context) throws InvoiceApiException;
 }
diff --git a/api/src/main/java/org/killbill/billing/junction/BillingEvent.java b/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
index a08e8c3..4e32345 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingEvent.java
@@ -18,13 +18,16 @@ package org.killbill.billing.junction;
 
 import java.math.BigDecimal;
 import java.util.List;
+import java.util.Set;
 
 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.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -80,7 +83,7 @@ public interface BillingEvent extends Comparable<BillingEvent> {
     /**
      * @return the recurring price for the phase
      */
-    BigDecimal getRecurringPrice();
+    BigDecimal getRecurringPrice(DateTime effectiveDate) throws CatalogApiException;
 
     /**
      * @return the currency for the account being invoiced
@@ -107,4 +110,5 @@ public interface BillingEvent extends Comparable<BillingEvent> {
      * @return the list of {@code Usage} section
      */
     List<Usage> getUsages();
+
 }
diff --git a/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java b/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
index 6e1828d..b44faa9 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2011 Ning, Inc.
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -23,7 +25,6 @@ import java.util.UUID;
 
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.Usage;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
 
 public interface BillingEventSet extends SortedSet<BillingEvent> {
 
@@ -33,7 +34,5 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
 
     public List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
 
-    public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext();
-
     public Map<String, Usage> getUsages();
 }
diff --git a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
index 0265dc1..52d1ec3 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
@@ -22,11 +22,12 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.invoice.api.DryRunArguments;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 
 public interface BillingInternalApi {
 
     /**
      * @return an ordered list of billing event for the given accounts
      */
-    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, DryRunArguments dryRunArguments, InternalCallContext context) throws CatalogApiException, AccountApiException;
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, DryRunArguments dryRunArguments, InternalCallContext context) throws CatalogApiException, AccountApiException, SubscriptionBaseApiException;
 }
diff --git a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
index 07537b0..2567f94 100644
--- a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
+++ b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
@@ -25,7 +25,6 @@ import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.util.UUIDs;
-import org.killbill.clock.Clock;
 
 public class DefaultBlockingState extends EntityBase implements BlockingState {
 
@@ -87,6 +86,18 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
              0L);
     }
 
+    public DefaultBlockingState(final BlockingState input,
+                                final DateTime effectiveDate) {
+        this(input.getBlockedId(),
+             input.getType(),
+             input.getStateName(),
+             input.getService(),
+             input.isBlockChange(),
+             input.isBlockEntitlement(),
+             input.isBlockBilling(),
+             effectiveDate);
+    }
+
     @Override
     public UUID getBlockedId() {
         return blockedId;
@@ -204,6 +215,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
         return true;
     }
 
+
     @Override
     public int hashCode() {
         int result = super.hashCode();
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
index 02d6c5b..5cda4c4 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBase.java
@@ -26,6 +26,7 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -52,15 +53,15 @@ public interface SubscriptionBase extends Entity, Blockable {
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlan(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final CallContext context)
+    public DateTime changePlan(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final CallContext context)
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate, final CallContext context)
+    public DateTime changePlanWithDate(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate, final CallContext context)
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides,
+    public DateTime changePlanWithPolicy(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides,
                                          final BillingActionPolicy policy, final CallContext context)
             throws SubscriptionBaseApiException;
 
@@ -96,11 +97,17 @@ public interface SubscriptionBase extends Entity, Blockable {
 
     public DateTime getChargedThroughDate();
 
+    public boolean isMigrated();
+
     public ProductCategory getCategory();
 
+    public Integer getBillCycleDayLocal();
+
     public SubscriptionBaseTransition getPendingTransition();
 
     public SubscriptionBaseTransition getPreviousTransition();
 
     public List<SubscriptionBaseTransition> getAllTransitions();
+
+    public DateTime getDateOfFirstRecurringNonZeroCharge();
 }
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index df97745..fada42e 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -25,12 +25,16 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
 import org.killbill.billing.entitlement.api.EntitlementSpecifier;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
@@ -43,10 +47,10 @@ public interface SubscriptionBaseInternalApi {
 
 
     public SubscriptionBase createSubscription(UUID bundleId, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs,
-                                               InternalCallContext context) throws SubscriptionBaseApiException;
+                                               final boolean isMigrated, InternalCallContext context) throws SubscriptionBaseApiException;
 
-    public SubscriptionBase createBaseSubscriptionWithAddOns(UUID bundleId, Iterable<EntitlementSpecifier> entitlements, DateTime requestedDateWithMs,
-                                                             InternalCallContext context) throws SubscriptionBaseApiException;
+    public List<SubscriptionBase> createBaseSubscriptionWithAddOns(UUID bundleId, Iterable<EntitlementSpecifier> entitlements, DateTime requestedDateWithMs,
+                                                                   final boolean isMigrated, InternalCallContext context) throws SubscriptionBaseApiException;
 
     public void cancelBaseSubscriptions(Iterable<SubscriptionBase> subscriptions, BillingActionPolicy policy, InternalCallContext context) throws SubscriptionBaseApiException;
 
@@ -85,8 +89,7 @@ public interface SubscriptionBaseInternalApi {
 
     public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(SubscriptionBase subscription, InternalTenantContext context);
 
-    public DateTime getDryRunChangePlanEffectiveDate(SubscriptionBase subscription, String productName, BillingPeriod term,
-                                                     String priceList, DateTime requestedDate, BillingActionPolicy policy, InternalTenantContext context) throws SubscriptionBaseApiException;
+    public DateTime getDryRunChangePlanEffectiveDate(SubscriptionBase subscription, PlanSpecifier spec, DateTime requestedDate, BillingActionPolicy policy, List<PlanPhasePriceOverride> overrides, InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException;
 
     public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String baseProductName,
                                                                      DateTime requestedDate, InternalTenantContext context) throws SubscriptionBaseApiException;
@@ -96,4 +99,11 @@ public interface SubscriptionBaseInternalApi {
     public Iterable<DateTime> getFutureNotificationsForAccount(InternalCallContext context);
 
     public Map<UUID, DateTime> getNextFutureEventForSubscriptions(final SubscriptionBaseTransitionType eventType, final InternalCallContext internalCallContext);
+
+
+    public void updateBCD(final UUID subscriptionId, final int bcd, @Nullable final LocalDate effectiveFromDate, final InternalCallContext internalCallContext) throws SubscriptionBaseApiException;
+
+    public int getDefaultBillCycleDayLocal(final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal, final DateTime effectiveDate, final InternalTenantContext context) throws SubscriptionBaseApiException;
+
+
 }
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
index 11318ce..94606d0 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseTransitionType.java
@@ -21,18 +21,10 @@ package org.killbill.billing.subscription.api;
  */
 public enum SubscriptionBaseTransitionType {
     /**
-     * Occurs when a {@code SubscriptionBase} got migrated to mark the start of the subscription
-     */
-    MIGRATE_ENTITLEMENT,
-    /**
      * Occurs when a a user created a {@code SubscriptionBase} (not migrated)
      */
     CREATE,
     /**
-     * Occurs when a {@code SubscriptionBase} got migrated to mark the start of the billing
-     */
-    MIGRATE_BILLING,
-    /**
      * Occurs when a {@code SubscriptionBase} got transferred to mark the start of the subscription
      */
     TRANSFER,
@@ -41,10 +33,6 @@ public enum SubscriptionBaseTransitionType {
      */
     CHANGE,
     /**
-     * Occurs when a user restarted a {@code SubscriptionBase} after it had been cancelled
-     */
-    RE_CREATE,
-    /**
      * Occurs when a user cancelled the {@code SubscriptionBase}
      */
     CANCEL,
@@ -57,6 +45,10 @@ public enum SubscriptionBaseTransitionType {
      */
     PHASE,
     /**
+     * Update BCD for a specific subscription
+     */
+    BCD_CHANGE,
+    /**
      * Generated by the system to mark the start of blocked billing overdue state. This is not on disk but computed by junction to create the billing events.
      */
     START_BILLING_DISABLED,
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
index a0a981f..878b789 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/timeline/SubscriptionBaseTimeline.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.util.entity.Entity;
 
@@ -39,12 +40,6 @@ public interface SubscriptionBaseTimeline extends Entity {
      */
     public List<ExistingEvent> getExistingEvents();
 
-    /**
-     * @return the active version for the event stream
-     */
-    public long getActiveVersion();
-
-
 
     public interface ExistingEvent {
 
@@ -59,10 +54,6 @@ public interface SubscriptionBaseTimeline extends Entity {
          */
         public PlanPhaseSpecifier getPlanPhaseSpecifier();
 
-        /**
-         * @return the date at which this event should be inserted into the stream
-         */
-        public DateTime getRequestedDate();
 
         /**
          * @return the {@code SubscriptionBaseTransitionType} for the event
@@ -70,6 +61,12 @@ public interface SubscriptionBaseTimeline extends Entity {
         public SubscriptionBaseTransitionType getSubscriptionTransitionType();
 
         /**
+         *
+         * @return the product category
+         */
+        public ProductCategory getProductCategory();
+
+        /**
          * @return the date at which this event was effective
          */
         public DateTime getEffectiveDate();
@@ -83,5 +80,11 @@ public interface SubscriptionBaseTimeline extends Entity {
          * @return the name of the phase
          */
         public String getPlanPhaseName();
+
+        /**
+         *
+         * @return the new billCycleDayLocal
+         */
+        public Integer getBillCycleDayLocal();
     }
 }
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransition.java b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransition.java
index 035a349..508b495 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransition.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransition.java
@@ -62,5 +62,9 @@ public interface SubscriptionBaseTransition {
 
     public SubscriptionBaseTransitionType getTransitionType();
 
+    public Integer getPreviousBillingCycleDayLocal();
+
+    public Integer getNextBillingCycleDayLocal();
+
     public DateTime getCreatedDate();
 }
diff --git a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
index 2c6e6ea..73f06f0 100644
--- a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
@@ -41,6 +41,8 @@ public interface TenantInternalApi {
 
     public String getTenantOverdueConfig(InternalTenantContext tenantContext);
 
+    public String getTenantConfig(InternalTenantContext tenantContext);
+
     public String getInvoiceTemplate(Locale locale, InternalTenantContext tenantContext);
 
     public String getManualPayInvoiceTemplate(Locale locale, InternalTenantContext tenantContext);

beatrix/pom.xml 42(+41 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 9188b24..c752c0d 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
@@ -32,6 +32,10 @@
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
         <dependency>
@@ -62,6 +66,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
         </dependency>
@@ -75,12 +94,22 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-compress</artifactId>
             <version>1.5</version>
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.framework</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.javassist</groupId>
             <artifactId>javassist</artifactId>
             <scope>test</scope>
@@ -259,6 +288,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -281,6 +317,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index 98a8df8..03b8543 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -26,8 +26,9 @@ import javax.inject.Named;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.entitlement.EntitlementTransitionType;
+import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
 import org.killbill.billing.events.AccountChangeInternalEvent;
 import org.killbill.billing.events.AccountCreationInternalEvent;
 import org.killbill.billing.events.BlockingTransitionInternalEvent;
@@ -38,10 +39,11 @@ import org.killbill.billing.events.ControlTagCreationInternalEvent;
 import org.killbill.billing.events.ControlTagDeletionInternalEvent;
 import org.killbill.billing.events.CustomFieldCreationEvent;
 import org.killbill.billing.events.CustomFieldDeletionEvent;
-import org.killbill.billing.events.EntitlementInternalEvent;
 import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.events.InvoiceNotificationInternalEvent;
+import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
+import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
 import org.killbill.billing.events.OverdueChangeInternalEvent;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
 import org.killbill.billing.events.PaymentInfoInternalEvent;
@@ -52,8 +54,10 @@ import org.killbill.billing.events.TenantConfigDeletionInternalEvent;
 import org.killbill.billing.events.UserTagCreationInternalEvent;
 import org.killbill.billing.events.UserTagDeletionInternalEvent;
 import org.killbill.billing.lifecycle.glue.BusModule;
+import org.killbill.billing.notification.plugin.api.BlockingStateMetadata;
 import org.killbill.billing.notification.plugin.api.BroadcastMetadata;
 import org.killbill.billing.notification.plugin.api.ExtBusEventType;
+import org.killbill.billing.notification.plugin.api.PaymentMetadata;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -140,9 +144,7 @@ public class BeatrixListener {
                 objectType = ObjectType.SUBSCRIPTION;
                 objectId = realEventST.getSubscriptionId();
                 if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.CREATE ||
-                    realEventST.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE ||
-                    realEventST.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER ||
-                    realEventST.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+                    realEventST.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER) {
                     eventBusType = ExtBusEventType.SUBSCRIPTION_CREATION;
                 } else if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.CANCEL) {
                     eventBusType = ExtBusEventType.SUBSCRIPTION_CANCEL;
@@ -152,6 +154,8 @@ public class BeatrixListener {
                     eventBusType = ExtBusEventType.SUBSCRIPTION_CHANGE;
                 } else if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.UNCANCEL) {
                     eventBusType = ExtBusEventType.SUBSCRIPTION_UNCANCEL;
+                } else if (realEventST.getTransitionType() == SubscriptionBaseTransitionType.BCD_CHANGE) {
+                    eventBusType = ExtBusEventType.SUBSCRIPTION_BCD_CHANGE;
                 }
                 break;
 
@@ -165,17 +169,24 @@ public class BeatrixListener {
                     objectType = ObjectType.SUBSCRIPTION;
                 }
                 objectId = realEventBS.getBlockableId();
-                // Probably we should serialize the isTransitionedTo* from BlockingTransitionInternalEvent into the metdata section
-                break;
 
-            case ENTITLEMENT_TRANSITION:
-                final EntitlementInternalEvent realEventET = (EntitlementInternalEvent) event;
-                objectType = ObjectType.BUNDLE;
-                objectId = realEventET.getBundleId();
-                if (realEventET.getTransitionType() == EntitlementTransitionType.BLOCK_BUNDLE) {
-                    eventBusType = ExtBusEventType.BUNDLE_PAUSE;
-                } else if (realEventET.getTransitionType() == EntitlementTransitionType.UNBLOCK_BUNDLE) {
-                    eventBusType = ExtBusEventType.BUNDLE_RESUME;
+                if (EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(realEventBS.getService())) {
+                    if (DefaultEntitlementApi.ENT_STATE_START.equals(realEventBS.getStateName())) {
+                        eventBusType = ExtBusEventType.ENTITLEMENT_CREATION;
+                    } else if (DefaultEntitlementApi.ENT_STATE_BLOCKED.equals(realEventBS.getStateName())) {
+                        eventBusType = ExtBusEventType.BUNDLE_PAUSE;
+                    } else if (DefaultEntitlementApi.ENT_STATE_CLEAR.equals(realEventBS.getStateName())) {
+                        eventBusType = ExtBusEventType.BUNDLE_RESUME;
+                    } else if (DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(realEventBS.getStateName())) {
+                        eventBusType = ExtBusEventType.ENTITLEMENT_CANCEL;
+                    }
+                } else {
+                    eventBusType = ExtBusEventType.BLOCKING_STATE;
+
+                    final BlockingStateMetadata metaDataObj = new BlockingStateMetadata(realEventBS.getBlockableId(), realEventBS.getService(), realEventBS.getStateName(), realEventBS.getBlockingType(), realEventBS.getEffectiveDate(),
+                                                                                        realEventBS.isTransitionedToBlockedBilling(), realEventBS.isTransitionedToUnblockedBilling(),
+                                                                                        realEventBS.isTransitionedToBlockedEntitlement(), realEventBS.isTransitionedToUnblockedEntitlement());
+                    metaData = objectMapper.writeValueAsString(metaDataObj);
                 }
                 break;
 
@@ -202,11 +213,27 @@ public class BeatrixListener {
                 eventBusType = ExtBusEventType.INVOICE_ADJUSTMENT;
                 break;
 
+            case INVOICE_PAYMENT_INFO:
+                final InvoicePaymentInfoInternalEvent realEventInvPay = (InvoicePaymentInfoInternalEvent) event;
+                objectType = ObjectType.INVOICE;
+                objectId = realEventInvPay.getInvoiceId();
+                eventBusType = ExtBusEventType.INVOICE_PAYMENT_SUCCESS;
+                break;
+
+            case INVOICE_PAYMENT_ERROR:
+                final InvoicePaymentErrorInternalEvent realEventInvPayErr = (InvoicePaymentErrorInternalEvent) event;
+                objectType = ObjectType.INVOICE;
+                objectId = realEventInvPayErr.getInvoiceId();
+                eventBusType = ExtBusEventType.INVOICE_PAYMENT_FAILED;
+                break;
+
             case PAYMENT_INFO:
                 final PaymentInfoInternalEvent realEventPay = (PaymentInfoInternalEvent) event;
                 objectType = ObjectType.PAYMENT;
                 objectId = realEventPay.getPaymentId();
                 eventBusType = ExtBusEventType.PAYMENT_SUCCESS;
+                final PaymentMetadata paymentInfoMetaDataObj = new PaymentMetadata(realEventPay.getPaymentTransactionId(), realEventPay.getAmount(), realEventPay.getCurrency(), realEventPay.getStatus(), realEventPay.getTransactionType(), realEventPay.getEffectiveDate());
+                metaData = objectMapper.writeValueAsString(paymentInfoMetaDataObj);
                 break;
 
             case PAYMENT_ERROR:
@@ -215,6 +242,8 @@ public class BeatrixListener {
                 objectId = realEventPayErr.getPaymentId();
                 eventBusType = ExtBusEventType.PAYMENT_FAILED;
                 accountId = realEventPayErr.getAccountId();
+                final PaymentMetadata paymentErrorMetaDataObj = new PaymentMetadata(realEventPayErr.getPaymentTransactionId(), realEventPayErr.getAmount(), realEventPayErr.getCurrency(), realEventPayErr.getStatus(), realEventPayErr.getTransactionType(), realEventPayErr.getEffectiveDate());
+                metaData = objectMapper.writeValueAsString(paymentErrorMetaDataObj);
                 break;
 
             case PAYMENT_PLUGIN_ERROR:
@@ -222,6 +251,8 @@ public class BeatrixListener {
                 objectType = ObjectType.PAYMENT;
                 objectId = realEventPayPluginErr.getPaymentId();
                 eventBusType = ExtBusEventType.PAYMENT_FAILED;
+                final PaymentMetadata pluginErrorMetaDataObj = new PaymentMetadata(realEventPayPluginErr.getPaymentTransactionId(), realEventPayPluginErr.getAmount(), realEventPayPluginErr.getCurrency(), realEventPayPluginErr.getStatus(), realEventPayPluginErr.getTransactionType(), realEventPayPluginErr.getEffectiveDate());
+                metaData = objectMapper.writeValueAsString(pluginErrorMetaDataObj);
                 break;
 
             case OVERDUE_CHANGE:
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java b/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
index 77ab028..1703097 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/extbus/TestEventJson.java
@@ -27,7 +27,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.beatrix.BeatrixTestSuiteNoDB;
 import org.killbill.billing.util.jackson.ObjectMapper;
 
-public class TestEventJson extends BeatrixTestSuiteNoDB {
+public class  TestEventJson extends BeatrixTestSuiteNoDB {
 
     private final ObjectMapper mapper = new ObjectMapper();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
index db9efa1..6c9b9b0 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModule.java
@@ -42,13 +42,14 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
 import org.killbill.billing.tenant.glue.DefaultTenantModule;
 import org.killbill.billing.usage.glue.UsageModule;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.email.EmailModule;
 import org.killbill.billing.util.email.templates.TemplateModule;
 import org.killbill.billing.util.glue.AuditModule;
 import org.killbill.billing.util.glue.BroadcastModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
@@ -59,8 +60,6 @@ import org.killbill.billing.util.glue.NonEntityDaoModule;
 import org.killbill.billing.util.glue.RecordIdModule;
 import org.killbill.billing.util.glue.SecurityModule;
 import org.killbill.billing.util.glue.TagStoreModule;
-import org.killbill.billing.util.security.shiro.realm.KillBillJdbcRealm;
-import org.killbill.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
 
 public class BeatrixIntegrationModule extends KillBillModule {
 
@@ -78,6 +77,7 @@ public class BeatrixIntegrationModule extends KillBillModule {
         install(new GuicyKillbillTestWithEmbeddedDBModule(true, configSource));
         install(new GlobalLockerModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new EmailModule(configSource));
         install(new CallContextModule(configSource));
         install(new TagStoreModule(configSource));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModuleNoDB.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModuleNoDB.java
index 4c47137..d4e84f4 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModuleNoDB.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/BeatrixIntegrationModuleNoDB.java
@@ -21,8 +21,11 @@ import org.killbill.billing.GuicyKillbillTestNoDBModule;
 import org.killbill.billing.mock.glue.MockAccountModule;
 import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.glue.KillBillModule;
 
+import com.google.inject.util.Providers;
+
 public class BeatrixIntegrationModuleNoDB extends KillBillModule {
 
     public BeatrixIntegrationModuleNoDB(final KillbillConfigSource configSource) {
@@ -32,7 +35,9 @@ public class BeatrixIntegrationModuleNoDB extends KillBillModule {
     @Override
     protected void configure() {
         install(new GuicyKillbillTestNoDBModule(configSource));
+
         install(new MockNonEntityDaoModule(configSource));
         install(new MockAccountModule(configSource));
+        bind(CacheControllerDispatcher.class).toProvider(Providers.<CacheControllerDispatcher>of(null));
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
index 9ee5303..de9b23d 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestBillingAlignment.java
@@ -39,18 +39,18 @@ public class TestBillingAlignment extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testTransitionAccountBAToSubscriptionBA() throws Exception {
-        // Set the BCD to the 25th
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
-
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
+        // Set the BCD to the 25th
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
+
         //
         // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
         // (Start with monthly that has an 'Account' billing alignment)
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
index 5f5b4e2..f7fb6b3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueBase.java
@@ -63,16 +63,23 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
         final DefaultOverdueConfig config = XMLLoader.getObjectFromStreamNoValidation(is, DefaultOverdueConfig.class);
         overdueConfigCache.loadDefaultOverdueConfig(config);
 
+        productName = "Shotgun";
+        term = BillingPeriod.MONTHLY;
+        paymentPlugin.clear();
+    }
+
+    protected void setupAccount() throws Exception {
         account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
         assertNotNull(account);
 
         paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, paymentMethodPlugin, PLUGIN_PROPERTIES, callContext);
-        productName = "Shotgun";
-        term = BillingPeriod.MONTHLY;
-        paymentPlugin.clear();
     }
 
     protected void checkODState(final String expected) {
+        checkODState(expected, account.getId());
+    }
+
+    protected void checkODState(final String expected, final UUID accountId) {
         try {
             // This will test the overdue notification queue: when we move the clock, the overdue system
             // should get notified to refresh its state.
@@ -82,13 +89,13 @@ public abstract class TestOverdueBase extends TestIntegrationBase {
             await().atMost(10, SECONDS).until(new Callable<Boolean>() {
                 @Override
                 public Boolean call() throws Exception {
-                    final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
+                    final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(accountId, BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
                     final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
                     return expected.equals(stateName);
                 }
             });
         } catch (final Exception e) {
-            final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(account.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
+            final BlockingState blockingStateForService = blockingApi.getBlockingStateForService(accountId, BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalCallContext);
             final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
             Assert.assertEquals(stateName, expected, "Got exception: " + e.toString());
         }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueChildParentRelationship.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueChildParentRelationship.java
new file mode 100644
index 0000000..fba90a7
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueChildParentRelationship.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration.overdue;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
+import org.testng.annotations.Test;
+import org.weakref.jmx.internal.guava.collect.Iterables;
+
+import static org.testng.Assert.assertEquals;
+
+// For all the tests, we set the the property org.killbill.payment.retry.days=8,8,8,8,8,8,8,8 so that Payment retry logic does not end with an ABORTED state
+// preventing final instant payment to succeed.
+//
+// The tests are difficult to follow because there are actually two tracks of retry in logic:
+// - The payment retries
+// - The overdue notifications
+//
+
+public class TestOverdueChildParentRelationship extends TestOverdueBase {
+
+    @Override
+    public String getOverdueConfig() {
+        final String configXml = "<overdueConfig>" +
+                                 "   <accountOverdueStates>" +
+                                 "       <initialReevaluationInterval>" +
+                                 "           <unit>DAYS</unit><number>10</number>" +
+                                 "       </initialReevaluationInterval>" +
+                                 "       <state name=\"OD3\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>26</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD3</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                                 "       </state>" +
+                                 "       <state name=\"OD2\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>18</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD2</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>" +
+                                 "           <autoReevaluationInterval>" +
+                                 "               <unit>DAYS</unit><number>8</number>" +
+                                 "           </autoReevaluationInterval>" +
+                                 "       </state>" +
+                                 "       <state name=\"OD1\">" +
+                                 "           <condition>" +
+                                 "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "                   <unit>DAYS</unit><number>10</number>" +
+                                 "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+                                 "           </condition>" +
+                                 "           <externalMessage>Reached OD1</externalMessage>" +
+                                 "           <blockChanges>true</blockChanges>" +
+                                 "           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>" +
+                                 "           <autoReevaluationInterval>" +
+                                 "               <unit>DAYS</unit><number>8</number>" +
+                                 "           </autoReevaluationInterval>" +
+                                 "       </state>" +
+                                 "   </accountOverdueStates>" +
+                                 "</overdueConfig>";
+
+        return configXml;
+    }
+
+    @Test(groups = "slow", description = "Test overdue stages and return to clear on CTD for Parent and Child accounts")
+    public void testOverdueStagesParentChildAccounts() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        setupAccount();
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(0, account.getId(), true));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
+
+        invoiceChecker.checkInvoice(childAccount.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        //invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.PARENT_SUMMARY, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // 2012-05-2 => DAY 1 : Parent Invoice commit status
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(29, NextEvent.PHASE, NextEvent.INVOICE);
+
+        // 2012-06-01 => Parent Invoice payment attempt
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(childAccount.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        //invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.PARENT_SUMMARY, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // 2012-06-09 => DAY 8 : Retry P0
+        addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, account.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccount.getId());
+
+        // 2012-06-11 => Day 10 - Retry P0 - Move to OD1 state
+        addDaysAndCheckForCompletion(2, NextEvent.BLOCK, NextEvent.BLOCK);
+        checkODState("OD1", account.getId());
+        checkODState("OD1", childAccount.getId());
+
+        // 2012-06-17 => DAY 16 - Retry P1
+        addDaysAndCheckForCompletion(6, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState("OD1", account.getId());
+        checkODState("OD1", childAccount.getId());
+
+        // 2012-06-19 => Day 18 - Retry P0 - Move to OD2 state
+        addDaysAndCheckForCompletion(2, NextEvent.TAG, NextEvent.BLOCK, NextEvent.TAG, NextEvent.BLOCK);
+        checkODState("OD2", account.getId());
+        checkODState("OD2", childAccount.getId());
+
+        // 2012-06-25 => DAY 24 - Retry P2
+        addDaysAndCheckForCompletion(6, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState("OD2", account.getId());
+        checkODState("OD2", childAccount.getId());
+
+        // 2012-06-27 => Day 26 - Retry P2 - Move to OD3 state
+        addDaysAndCheckForCompletion(2, NextEvent.BLOCK, NextEvent.BLOCK);
+        checkODState("OD3", account.getId());
+        checkODState("OD3", childAccount.getId());
+
+        // Make sure the 'invoice-service:next-billing-date-queue' gets processed before we continue and since we are in AUTO_INVOICING_OFF
+        // no event (NULL_INVOICE) will be generated and so we can't synchronize on any event, and we need to add a small amount of sleep
+        Thread.sleep(1000);
+
+        // Verify the account balance is 0
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
+        assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(new BigDecimal("249.95")), 0);
+
+        allowPaymentsAndResetOverdueToClearByPayingAllUnpaidInvoices(1, 1, childAccount);
+
+        // check invoice generated after clear child account
+        invoiceChecker.checkInvoice(childAccount.getId(), 3, callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 19), new LocalDate(2012, 6, 27), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-66.65")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 27), new LocalDate(2012, 6, 27), InvoiceItemType.CBA_ADJ, new BigDecimal("66.65")));
+
+        // Verify the account balance is now 0
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoiceUserApi.getAccountBalance(childAccount.getId(), callContext).compareTo(BigDecimal.valueOf(-66.65)), 0);
+    }
+
+    @Test(groups = "slow", description = "Test overdue stages and return to clear on CTD for Parent and Child accounts")
+    public void testOverdueStagesParentMultiChildAccounts() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        setupAccount();
+        final Account childAccount1 = createAccountWithNonOsgiPaymentMethod(getChildAccountData(0, account.getId(), true));
+        final Account childAccount2 = createAccountWithNonOsgiPaymentMethod(getChildAccountData(0, account.getId(), true));
+        final Account childAccount3 = createAccountWithNonOsgiPaymentMethod(getChildAccountData(0, account.getId(), true));
+        final Account childAccountNoPaymentDelegated = createAccountWithNonOsgiPaymentMethod(getChildAccountData(0, account.getId(), false));
+
+        // Set next invoice to fail and create subscription
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(childAccount1.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
+
+        invoiceChecker.checkInvoice(childAccount1.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
+
+        // 2012-05-2 => DAY 1 : Parent Invoice commit status
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE);
+
+        // 2012-05-2 => DAY 2
+        addDaysAndCheckForCompletion(1);
+        final DefaultEntitlement baseEntitlement2 = createBaseEntitlementAndCheckForCompletion(childAccount2.getId(), "externalKey2", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        invoiceChecker.checkInvoice(childAccount2.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 3), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement2.getId(), new LocalDate(2012, 5, 3), callContext);
+
+        // 2012-05-3 => DAY 3 : Parent Invoice commit status  (Sub.2)
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE);
+
+        // 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
+        addDaysAndCheckForCompletion(27, NextEvent.PHASE, NextEvent.INVOICE);
+
+        // 2012-06-01 => Parent Invoice payment attempt
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+
+        // 2012-06-02 => DAY 30 have to get out of trial {I0, P0}  (Sub.2)
+        addDaysAndCheckForCompletion(1, NextEvent.PHASE, NextEvent.INVOICE);
+
+        // 2012-06-03 => Parent Invoice payment attempt  (Sub.2)
+        addDaysAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+
+        invoiceChecker.checkInvoice(childAccount1.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
+
+        // 2012-06-09 => DAY 8 : Retry P0
+        addDaysAndCheckForCompletion(6, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, account.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccount1.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccount2.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccount3.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccountNoPaymentDelegated.getId());
+
+        // 2012-06-11 => Day 10 - Retry P0 - Move to OD1 state
+        addDaysAndCheckForCompletion(2, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.BLOCK,
+                                     NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState("OD1", account.getId());
+        checkODState("OD1", childAccount1.getId());
+        checkODState("OD1", childAccount2.getId());
+        checkODState("OD1", childAccount3.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccountNoPaymentDelegated.getId());
+
+        // 2012-06-17 => DAY 16 : Retry P0
+        addDaysAndCheckForCompletion(6, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState("OD1", account.getId());
+        checkODState("OD1", childAccount1.getId());
+        checkODState("OD1", childAccount2.getId());
+        checkODState("OD1", childAccount3.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccountNoPaymentDelegated.getId());
+
+        // 2012-06-19 => Day 18 - Retry P0 - Move to OD2 state
+        addDaysAndCheckForCompletion(2, NextEvent.TAG, NextEvent.BLOCK, NextEvent.TAG, NextEvent.BLOCK, NextEvent.TAG,
+                                     NextEvent.BLOCK, NextEvent.TAG, NextEvent.BLOCK,
+                                     NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        checkODState("OD2", account.getId());
+        checkODState("OD2", childAccount1.getId());
+        checkODState("OD2", childAccount2.getId());
+        checkODState("OD2", childAccount3.getId());
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccountNoPaymentDelegated.getId());
+
+        allowPaymentsAndResetOverdueToClearByPayingAllUnpaidInvoices(0, 4, childAccount1, childAccount2, childAccount3);
+
+    }
+
+    private void allowPaymentsAndResetOverdueToClearByPayingAllUnpaidInvoices(final int expectedInvoicesCount, final int expectedNullInvoicesCount, final Account... childAccounts) {
+
+        // Reset plugin so payments should now succeed
+        paymentPlugin.makeAllInvoicesFailWithError(false);
+
+        // build expected event list
+        List<NextEvent> nextEventList = new ArrayList<NextEvent>();
+        nextEventList.addAll(Collections.nCopies(childAccounts.length + 1, NextEvent.BLOCK));
+        nextEventList.addAll(Collections.nCopies(childAccounts.length + 1, NextEvent.TAG));
+        nextEventList.addAll(Collections.nCopies(expectedInvoicesCount, NextEvent.INVOICE));
+        nextEventList.addAll(Collections.nCopies(expectedNullInvoicesCount, NextEvent.NULL_INVOICE));
+        nextEventList.addAll(Arrays.asList(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT));
+
+        //
+        // We now pay all unpaid invoices.
+        //
+        // Upon paying the last invoice, the overdue system will clear the state and notify invoice that it should re-generate a new invoice
+        // for the part that was unblocked, which explains why on the last payment we expect an additional invoice (and payment if needed).
+        //
+        final List<Invoice> sortedInvoices = getUnpaidInvoicesOrderFromRecent();
+
+        int remainingUnpaidInvoices = sortedInvoices.size();
+        for (final Invoice invoice : sortedInvoices) {
+            if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+                remainingUnpaidInvoices--;
+                if (remainingUnpaidInvoices > 0) {
+                    createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                } else {
+                    createPaymentAndCheckForCompletion(account, invoice, Iterables.toArray(nextEventList, NextEvent.class));
+                }
+            }
+        }
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+        for (Account childAccount : childAccounts) {
+            checkODState(OverdueWrapper.CLEAR_STATE_NAME, childAccount.getId());
+        }
+    }
+
+    private List<Invoice> getUnpaidInvoicesOrderFromRecent() {
+        final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+        // Sort in reverse order to first pay most recent invoice-- that way overdue state may only flip when we reach the last one.
+        final List<Invoice> sortedInvoices = new LinkedList<Invoice>(invoices);
+        Collections.sort(sortedInvoices, new Comparator<Invoice>() {
+            @Override
+            public int compare(final Invoice i1, final Invoice i2) {
+                return i2.getInvoiceDate().compareTo(i1.getInvoiceDate());
+            }
+        });
+        return sortedInvoices;
+    }
+
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 3cdd088..7b6b8da 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -34,6 +34,7 @@ import org.killbill.billing.beatrix.integration.BeatrixIntegrationModule;
 import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.BlockingApiException;
@@ -45,12 +46,16 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
+import org.killbill.billing.overdue.config.DefaultOverdueConfig;
 import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.xmlloader.XMLLoader;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.io.Resources;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
@@ -120,9 +125,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -207,9 +214,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -305,9 +314,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -406,9 +417,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
@@ -494,9 +507,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -572,11 +587,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Make sure the account doesn't have any payment method
         accountInternalApi.removePaymentMethod(account.getId(), internalCallContext);
 
         // Create subscription
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -597,8 +614,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // Should still be in clear state
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
-        // 2012-07-05 => DAY 65 - 35 days after invoice
-        addDaysAndCheckForCompletion(20, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT_ERROR);
+        // 2012-06-30 => DAY 65 - 30 days after invoice
+        addDaysAndCheckForCompletion(15, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT_ERROR);
 
         invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
@@ -614,14 +631,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState("OD1");
         checkChangePlanWithOverdueState(baseEntitlement, true, true);
 
-        // 2012-07-15 => DAY 75 - 45 days after invoice
+        // 2012-07-10 => DAY 75 - 40 days after invoice
         addDaysAndCheckForCompletion(8, NextEvent.BLOCK, NextEvent.TAG);
 
         // Should now be in OD2
         checkODState("OD2");
         checkChangePlanWithOverdueState(baseEntitlement, true, true);
 
-        // 2012-07-25 => DAY 85 - 55 days after invoice
+        // 2012-07-20 => DAY 85 - 50 days after invoice
         addDaysAndCheckForCompletion(10, NextEvent.BLOCK);
 
         // Should now be in OD3
@@ -639,35 +656,37 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         invoiceChecker.checkInvoice(account.getId(), 4, callContext,
                                     // Item for the blocked period
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 25), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 20), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
 
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
 
-        checkChangePlanWithOverdueState(baseEntitlement, false, false);
+        checkChangePlanWithOverdueState(baseEntitlement, false, true);
 
         invoiceChecker.checkInvoice(account.getId(), 4, callContext,
                                     // Item for the blocked period
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 25), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 20), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-80.63")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("80.63")));
 
         invoiceChecker.checkInvoice(account.getId(), 5, callContext,
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.12")),
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-48.38")),
-                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-67.74")));
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("212.89")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-88.69")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 20), new LocalDate(2012, 7, 20), InvoiceItemType.CBA_ADJ, new BigDecimal("-80.63")));
 
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
 
-        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-12.89")), 0);
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
     }
 
-    @Test(groups = "slow", description = "Test overdue from non paid external charge")
-    public void testShouldBeInOverdueAfterExternalCharge() throws Exception {
+    @Test(groups = "slow", description = "Test overdue for draft external charge")
+    public void testShouldNotBeInOverdueAfterDraftExternalCharge() throws Exception {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Create a subscription without failing payments
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -675,9 +694,8 @@ public class TestOverdueIntegration extends TestOverdueBase {
 
         // 2012-05-06 => Create an external charge on a new invoice
         addDaysAndCheckForCompletion(5);
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, account.getId(), bundle.getId(), "For overdue", new LocalDate(2012, 5, 6), BigDecimal.TEN, Currency.USD);
-        invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), false, callContext).get(0);
         assertListenerStatus();
         invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 6), null, InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
 
@@ -687,20 +705,16 @@ public class TestOverdueIntegration extends TestOverdueBase {
         invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
-        // Should still be in clear state - the invoice for the bundle has been paid, but not the invoice with the external charge
+        // Should still be in clear state - the invoice for the bundle has been paid, but not the invoice with the external charge (because it is in draft mode)
         // We refresh overdue just to be safe, see below
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // 2012-06-06 => Past 30 days since the external charge
-        addDaysAndCheckForCompletion(6, NextEvent.BLOCK);
-        // We should now be in OD1
-        checkODState("OD1");
-
-        // Pay the invoice
-        final Invoice externalChargeInvoice = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).iterator().next();
-        createExternalPaymentAndCheckForCompletion(account, externalChargeInvoice, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
-        // We should be clear now
+        addDaysAndCheckForCompletion(6);
+        // We should still be clear
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 0);
     }
 
     @Test(groups = "slow", description = "Test overdue after refund with no adjustment")
@@ -708,8 +722,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Create subscription and don't fail payments
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -740,7 +756,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // Now, refund the second (first non-zero dollar) invoice
-        final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), false, PLUGIN_PROPERTIES, callContext);
+        final Payment payment = paymentApi.getPayment(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).get(1).getPayments().get(0).getPaymentId(), false, false, PLUGIN_PROPERTIES, callContext);
         refundPaymentAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
         // We should now be in OD1
         checkODState("OD1");
@@ -752,8 +768,10 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Create subscription and don't fail payments
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -784,12 +802,16 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
 
         // Now, create a chargeback for the second (first non-zero dollar) invoice
-        final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
-        final Payment payment = paymentApi.getPayment(invoicePayment.getPaymentId(), false, ImmutableList.<PluginProperty>of(), callContext);
-        createChargeBackAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
+        final InvoicePayment invoicePayment = invoicePaymentApi.getInvoicePayments(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).get(1).getPayments().get(0).getPaymentId(), callContext).get(0);
+        Payment payment = paymentApi.getPayment(invoicePayment.getPaymentId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
+        payment = createChargeBackAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
         // We should now be in OD1
         checkODState("OD1");
         checkChangePlanWithOverdueState(baseEntitlement, true, true);
+
+        // Reverse the chargeback
+        createChargeBackReversalAndCheckForCompletion(account, payment, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.BLOCK);
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
     @Test(groups = "slow", description = "Test overdue clear after external payment")
@@ -797,9 +819,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -845,9 +869,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -926,6 +952,151 @@ public class TestOverdueIntegration extends TestOverdueBase {
         checkODState(OverdueWrapper.CLEAR_STATE_NAME);
     }
 
+    @Test(groups = "slow", description = "Test overdue state with number of unpaid invoices condition")
+    public void testOverdueStateWithNumberOfUnpaidInvoicesCondition() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final DefaultOverdueConfig config = XMLLoader.getObjectFromString(Resources.getResource("overdueWithNumberOfUnpaidInvoicesCondition.xml").toExternalForm(), DefaultOverdueConfig.class);
+        overdueConfigCache.loadDefaultOverdueConfig(config);
+
+        setupAccount();
+
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+
+        createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE,
+                                                   term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2012-05-31 => DAY 30 have to get out of trial before first payment
+        addMonthsAndCheckForCompletion(1, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Verify that number of unpaid invoices is 1
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 1);
+        // Should still be in clear state
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.BLOCK, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Verify that number of unpaid invoices is 2
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 2);
+        // Now we should be in OD1
+        checkODState("OD1");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.BLOCK, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Verify that number of unpaid invoices is 3
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 3);
+        // Now we should be in OD2
+        checkODState("OD2");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Verify that number of unpaid invoices is 4
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 4);
+        // We should still be in OD2
+        checkODState("OD2");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.TAG,  NextEvent.BLOCK);
+        // Verify that number of unpaid invoices is 5
+        Assert.assertEquals(invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext).size(), 5);
+        // Now we should be in OD3
+        checkODState("OD3");
+
+        // Get all unpaid invoices and pay them to clear the overdue state
+        paymentPlugin.makeAllInvoicesFailWithError(false);
+        List<Invoice> unpaidInvoices = getUnpaidInvoicesOrderFromRecent();
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(0), NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(1), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(2), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(3), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(4), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        // We should be clear now
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+    }
+
+    @Test(groups = "slow", description = "Test overdue state with total unpaid invoice balance condition")
+    public void testOverdueStateWithTotalUnpaidInvoiceBalanceCondition() throws Exception {
+        // 2012-05-01T00:03:42.000Z
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+        final DefaultOverdueConfig config = XMLLoader.getObjectFromString(Resources.getResource("overdueWithTotalUnpaidInvoiceBalanceCondition.xml").toExternalForm(), DefaultOverdueConfig.class);
+        overdueConfigCache.loadDefaultOverdueConfig(config);
+
+        setupAccount();
+
+        paymentPlugin.makeAllInvoicesFailWithError(true);
+
+        createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE,
+                                                   term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2012-05-31 => DAY 30 have to get out of trial before first payment
+        addMonthsAndCheckForCompletion(1, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+
+        // Amount balance should be USD 249.95
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.valueOf(249.95)), 0);
+        // Should still be in clear state
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.BLOCK, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Amount balance should be USD 499.90
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.valueOf(499.90)), 0);
+        // Now we should be in OD1
+        checkODState("OD1");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.BLOCK, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Amount balance should be USD 749.85
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.valueOf(749.85)), 0);
+        // Now we should be in OD2
+        checkODState("OD2");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        // Amount balance should be USD 999.80
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.valueOf(999.80)), 0);
+        // We should still be in OD2
+        checkODState("OD2");
+
+        // Add 1 month
+        addMonthsAndCheckForCompletion(1, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR,
+                                       NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.TAG,  NextEvent.BLOCK);
+        // Amount balance should be USD 1249.75
+        assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.valueOf(1249.75)), 0);
+        // Now we should be in OD3
+        checkODState("OD3");
+
+        // Get all unpaid invoices and pay them to clear the overdue state
+        paymentPlugin.makeAllInvoicesFailWithError(false);
+        List<Invoice> unpaidInvoices = getUnpaidInvoicesOrderFromRecent();
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(0), NextEvent.BLOCK, NextEvent.TAG, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(1), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(2), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(3), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.BLOCK);
+        createPaymentAndCheckForCompletion(account, unpaidInvoices.get(4), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        // We should be clear now
+        checkODState(OverdueWrapper.CLEAR_STATE_NAME);
+    }
+
     private void allowPaymentsAndResetOverdueToClearByPayingAllUnpaidInvoices(final boolean extraPayment) {
 
         // Reset plugin so payments should now succeed
@@ -973,7 +1144,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
     private void checkChangePlanWithOverdueState(final Entitlement entitlement, final boolean shouldFail, final boolean expectedPayment) {
         if (shouldFail) {
             try {
-                entitlement.changePlan("Pistol", term, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), callContext);
+                entitlement.changePlan(new PlanSpecifier("Pistol", term, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), callContext);
             } catch (EntitlementApiException e) {
                 assertTrue(e.getCause() instanceof BlockingApiException || e.getCode() == ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode(),
                            String.format("Cause is %s, message is %s", e.getCause(), e.getMessage()));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
index 7011c8a..a2ea242 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithSubscriptionCancellation.java
@@ -69,17 +69,19 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
     public void testCheckSubscriptionCancellation() throws Exception {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
-        final DefaultEntitlement addOn1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement addOn1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
-        final DefaultEntitlement addOn2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement addOn2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Holster", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         // Cancel addOn1 one day after
         clock.addDays(1);
@@ -115,19 +117,21 @@ public class TestOverdueWithSubscriptionCancellation extends TestOverdueBase {
         // 2012-05-01T00:03:53.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
-        final DefaultEntitlement baseEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey2", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey2", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         final SubscriptionBundle bundle2 = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
         invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement2.getId(), new LocalDate(2012, 5, 1), callContext);
 
-        final DefaultEntitlement baseEntitlement3 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey3", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement3 = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey3", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         final SubscriptionBundle bundle3 = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
         invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement2.getId(), new LocalDate(2012, 5, 1), callContext);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
index 3385436..797eac4 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueWithTags.java
@@ -69,9 +69,11 @@ public class TestOverdueWithTags extends TestOverdueBase {
     public void testOverdueStateWith_WRITTEN_OFF() throws Exception {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -80,7 +82,7 @@ public class TestOverdueWithTags extends TestOverdueBase {
         // DAY 30 have to get out of trial before first payment
         addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         final Invoice nonNullInvoice = invoices.get(1);
@@ -106,9 +108,10 @@ public class TestOverdueWithTags extends TestOverdueBase {
 
     @Test(groups = "slow")
     public void testNonOverdueAccountWith_OVERDUE_ENFORCEMENT_OFF() throws Exception {
-
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set the OVERDUE_ENFORCEMENT_OFF tag (we set the clear state, hence the blocking event)
         busHandler.pushExpectedEvents(NextEvent.TAG, NextEvent.BLOCK);
         tagUserApi.addTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.OVERDUE_ENFORCEMENT_OFF.getId(), callContext);
@@ -116,7 +119,7 @@ public class TestOverdueWithTags extends TestOverdueBase {
 
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -143,12 +146,13 @@ public class TestOverdueWithTags extends TestOverdueBase {
 
     @Test(groups = "slow")
     public void testOverdueAccountWithOverdueEnforcementOffTag() throws Exception {
-
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
+        setupAccount();
+
         // Set next invoice to fail and create subscription
         paymentPlugin.makeAllInvoicesFailWithError(true);
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
 
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
index e32ab28..413d676 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestBundleTransfer.java
@@ -55,24 +55,22 @@ public class TestBundleTransfer extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testBundleTransferWithBPAnnualOnly() throws Exception {
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
         final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(3));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
         // Move out of trials for interesting invoices adjustments
@@ -87,7 +85,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, false, callContext);
         assertListenerStatus();
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
@@ -102,24 +100,22 @@ public class TestBundleTransfer extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testBundleTransferWithBPMonthlyOnly() throws Exception {
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
         final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
         assertListenerStatus();
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -142,7 +138,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         // Day of the transfer
         assertEquals(newBCD, (Integer) 3);
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         final List<InvoiceItem> invoiceItems = invoices.get(0).getInvoiceItems();
@@ -158,26 +154,24 @@ public class TestBundleTransfer extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testBundleTransferWithBPMonthlyOnlyWIthCancellationImm() throws Exception {
-
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(9));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-
         final DateTime initialDate = new DateTime(2012, 4, 1, 0, 15, 42, 0, testTimeZone);
-
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(9));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null);
+        final PlanPhaseSpecifier bpPlanPhaseSpecifier = new PlanPhaseSpecifier(productName, term, planSetName, null);
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
         assertListenerStatus();
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -193,7 +187,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         transferApi.transferBundle(account.getId(), newAccount.getId(), "externalKey", clock.getUTCNow(), false, true, callContext);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         // CHECK OLD AND NEW ACCOUNTS ITEMS
@@ -208,7 +202,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
         // CHECK NEW ACCOUNT ITEMS
-        invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(newAccount.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -235,13 +229,13 @@ public class TestBundleTransfer extends TestIntegrationBase {
         // Create the base plan
         final String bundleExternalKey = UUID.randomUUID().toString();
         final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), bundleExternalKey, bpProductName, ProductCategory.BASE, term,
-                                                                                            NextEvent.CREATE, NextEvent.INVOICE);
+                                                                                            NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         subscriptionChecker.checkSubscriptionCreated(bpEntitlement.getId(), internalCallContext);
         final Invoice firstInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
 
         // Create the add-on
         final DefaultEntitlement aoEntitlement = addAOEntitlementAndCheckForCompletion(bpEntitlement.getBundleId(), aoProductName, ProductCategory.ADD_ON, term,
-                                                                                       NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                                                                                       NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         final Invoice secondInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
         paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, secondInvoice.getId(), Currency.USD));
 
@@ -267,7 +261,7 @@ public class TestBundleTransfer extends TestIntegrationBase {
         final DateTime now = clock.getUTCNow();
         final LocalDate transferDay = now.toLocalDate();
 
-        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.TRANSFER, NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.BLOCK,  NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         final UUID newBundleId = entitlementApi.transferEntitlements(account.getId(), newAccount.getId(), bundleExternalKey, transferDay, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
index 5c0e974..f893c7e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCatalogRetireElements.java
@@ -20,6 +20,7 @@ package org.killbill.billing.beatrix.integration;
 import java.util.List;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.api.BillingPeriod;
@@ -45,7 +46,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
     @Override
     protected KillbillConfigSource getConfigSource() {
-        return super.getConfigSource("/beatrixVersionedCatalog.properties");
+        return super.getConfigSource("/beatrixCatalogRetireElements.properties");
     }
 
     @Test(groups = "slow")
@@ -65,10 +66,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final DefaultEntitlement bpEntitlement =
                 createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
-                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -79,13 +80,12 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         // Catalog v2 should start now.
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         try {
-            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
         } catch (final EntitlementApiException e) {
-            assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'"));
+            assertEquals(e.getCode(), ErrorCode.CAT_PLAN_NOT_FOUND.getCode());
         }
 
         // Move out a month and verify 'Pistol' plan continue working as expected.
@@ -93,7 +93,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         for (final Invoice invoice : invoices) {
             assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
@@ -117,10 +117,10 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final DefaultEntitlement bpEntitlement =
                 createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName,
-                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+                                                           ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -136,10 +136,9 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         // Catalog v3 should start now.
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, PriceListSet.DEFAULT_PRICELIST_NAME, null);
         try {
-            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
         } catch (final EntitlementApiException e) {
             assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
@@ -150,7 +149,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
         for (final Invoice invoice : invoices) {
             assertEquals(invoice.getInvoiceItems().get(0).getPlanName(), "pistol-monthly");
@@ -172,15 +171,14 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         final String productName = "Pistol";
         final BillingPeriod term = BillingPeriod.MONTHLY;
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, "SpecialDiscount", null);
-        LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, term, "SpecialDiscount", null);
 
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
-        final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        final Entitlement bpEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         // Move out a month.
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -194,9 +192,8 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
 
         // PriceList "SpecialDiscount" at this point.
 
-        effectiveDate = new LocalDate(clock.getUTCNow());
         try {
-            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+            entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
             fail(); // force to fail is there is not an exception
         } catch (final EntitlementApiException e) {
             assertTrue(e.getLocalizedMessage().startsWith("Could not find any product named 'Pistol'"));
@@ -212,7 +209,7 @@ public class TestCatalogRetireElements extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 5);
 
         assertTrue(invoices.get(0).getInvoiceItems().get(0).getPhaseName().equals("discount-pistol-monthly-trial"));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
index ed6564c..c6976cd 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestCustomFieldApi.java
@@ -83,10 +83,10 @@ public class TestCustomFieldApi extends TestIntegrationBase {
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         Assert.assertEquals(invoices.size(), 1);
 
         final Invoice invoice = invoices.get(0);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index 366de44..fd03318 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -63,27 +63,27 @@ public class TestIntegration extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCancelBPWithAOTheSameDay() throws Exception {
-        final AccountData accountData = getAccountData(1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
-        accountChecker.checkAccount(account.getId(), accountData, callContext);
-
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
         final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
         TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
-                                                             SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+                                                             SubscriptionEventType.START_BILLING, null, null, null, null);
         Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
 
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
@@ -93,12 +93,12 @@ public class TestIntegration extends TestIntegrationBase {
         // ADD ADD_ON ON THE SAME DAY
         //
         dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, null, null,
-                                         SubscriptionEventType.START_BILLING, null, bpSubscription.getBundleId(), clock.getUTCNow(), null);
+                                         SubscriptionEventType.START_BILLING, null, bpSubscription.getBundleId(), null, null);
         dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("399.95")));
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
 
-        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         final Invoice invoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
         paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 4, 1), new BigDecimal("399.95"), TransactionStatus.SUCCESS, invoice.getId(), Currency.USD));
@@ -109,7 +109,7 @@ public class TestIntegration extends TestIntegrationBase {
         // There is no invoice created as we only adjust the previous invoice.
         //
         dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, null, null, null, null, null, SubscriptionEventType.STOP_BILLING, bpSubscription.getId(),
-                                         bpSubscription.getBundleId(), clock.getUTCNow(), null);
+                                         bpSubscription.getBundleId(), null, null);
         dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-399.95")));
         // The second invoice should be adjusted for the AO (we paid for the full period) and since we paid we should also see a CBA
@@ -131,19 +131,19 @@ public class TestIntegration extends TestIntegrationBase {
     public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
         final int billingDay = 31;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
-        // set clock to the initial start date
-        clock.setTime(initialCreationDate);
         int invoiceItemCount = 1;
 
         final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -153,7 +153,7 @@ public class TestIntegration extends TestIntegrationBase {
         // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
         //
         TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null, SubscriptionEventType.CHANGE,
-                                                             subscription.getId(), subscription.getBundleId(), clock.getUTCNow(), null);
+                                                             subscription.getId(), subscription.getBundleId(), null, null);
         Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
@@ -243,17 +243,17 @@ public class TestIntegration extends TestIntegrationBase {
     public void testBasePlanCompleteWithBillingDayAlignedWithTrial() throws Exception {
         final int billingDay = 2;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
-        // set clock to the initial start date
-        clock.setTime(initialCreationDate);
         int invoiceItemCount = 1;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -334,17 +334,17 @@ public class TestIntegration extends TestIntegrationBase {
     public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
         final int billingDay = 3;
         final DateTime initialCreationDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
-        // set clock to the initial start date
-        clock.setTime(initialCreationDate);
         int invoiceItemCount = 1;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
 
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -472,17 +472,17 @@ public class TestIntegration extends TestIntegrationBase {
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
         final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
         clock.addDays(3);
 
         final DefaultEntitlement aoEntitlement1 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
-                                                                                        NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                                                                                        NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         final DefaultEntitlement aoEntitlement2 = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
-                                                                                        NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                                                                                        NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         // MOVE CLOCK A LITTLE BIT MORE -- EITHER STAY IN TRIAL OR GET OUT
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -517,7 +517,7 @@ public class TestIntegration extends TestIntegrationBase {
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         final SubscriptionBundle initialBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
 
         busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE);
@@ -525,7 +525,7 @@ public class TestIntegration extends TestIntegrationBase {
         assertListenerStatus();
 
         final String newProductName = "Pistol";
-        final DefaultEntitlement newBaseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", newProductName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement newBaseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", newProductName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         final SubscriptionBundle newBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey("bundleKey", callContext);
 
@@ -545,22 +545,21 @@ public class TestIntegration extends TestIntegrationBase {
     public void testWithPauseResume() throws Exception {
         final DateTime initialDate = new DateTime(2012, 2, 1, 0, 3, 42, 0, testTimeZone);
         final int billingDay = 2;
+        // set clock to the initial start date
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
         final UUID accountId = account.getId();
         assertNotNull(account);
 
-        // set clock to the initial start date
-        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
-
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(baseEntitlement);
 
         //
@@ -582,7 +581,7 @@ public class TestIntegration extends TestIntegrationBase {
 
         // PAUSE THE ENTITLEMENT
         DefaultEntitlement entitlement = (DefaultEntitlement) entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
-        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
         entitlementApi.pause(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -599,7 +598,7 @@ public class TestIntegration extends TestIntegrationBase {
         // MOVE CLOCK FORWARD ADN CHECK THERE IS NO NEW INVOICE
         clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
 
-        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
         entitlementApi.resume(entitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -621,16 +620,16 @@ public class TestIntegration extends TestIntegrationBase {
         final String productName = "Blowdart";
         final String planSetName = "DEFAULT";
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
         assertNotNull(invoices);
         assertTrue(invoices.size() == 1);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
         assertListenerStatus();
-        invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
         assertNotNull(invoices);
         assertEquals(invoices.size(), 2);
 
@@ -645,7 +644,7 @@ public class TestIntegration extends TestIntegrationBase {
         clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
         assertNotNull(invoices);
         assertEquals(invoices.size(), 8);
 
@@ -656,7 +655,7 @@ public class TestIntegration extends TestIntegrationBase {
             assertListenerStatus();
         }
 
-        invoices = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
         assertNotNull(invoices);
         assertEquals(invoices.size(), 14);
 
@@ -677,11 +676,11 @@ public class TestIntegration extends TestIntegrationBase {
 
 
         // First invoice
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
 
         // Second invoice -> first recurring for Refurbish-Maintenance
-        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, /* NextEvent.BLOCK, */ NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
 
@@ -733,4 +732,117 @@ public class TestIntegration extends TestIntegrationBase {
         invoiceChecker.checkInvoice(account.getId(), 14, callContext, expectedInvoices);
         expectedInvoices.clear();
     }
+
+    @Test(groups = "slow")
+    public void testThirtyDaysPlanWithFixedTermMonthlyAddOn() throws Exception {
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2015, 4, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        // First invoice
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.THIRTY_DAYS, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Second invoice -> first recurring for Refurbish-Maintenance
+        addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Refurbish-Maintenance", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("599.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), new LocalDate(2015, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2015-5-1
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30); // Also = 1 month because or initial date 2015, 4, 1
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 3, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Next 20 invoices, including last recurring for Refurbish-Maintenance
+        LocalDate startDateBase = new LocalDate(2015, 5, 31);
+        LocalDate startDateAddOn = new LocalDate(2015, 6, 1);
+        for (int i = 0; i < 10; i++) {
+            final LocalDate endDateBase = startDateBase.plusDays(30);
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDateBase, endDateBase, InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+            clock.setDay(startDateBase);
+            assertListenerStatus();
+            invoiceChecker.checkInvoice(account.getId(), 4 + 2 * i, callContext, expectedInvoices);
+            expectedInvoices.clear();
+
+            final LocalDate endDateAddOn = startDateAddOn.plusMonths(1);
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDateAddOn, endDateAddOn, InvoiceItemType.RECURRING, new BigDecimal("199.95")));
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+            clock.setDay(startDateAddOn);
+            assertListenerStatus();
+            invoiceChecker.checkInvoice(account.getId(), 5 + 2 * i, callContext, expectedInvoices);
+            expectedInvoices.clear();
+
+            startDateBase = endDateBase;
+            startDateAddOn = endDateAddOn;
+        }
+        // clock at 2016-03-01
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 3, 26), new LocalDate(2016, 4, 25), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.setDay(new LocalDate(2016, 3, 26));
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 24, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // We check there is no more recurring for Refurbish-Maintenance
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+        clock.setDay(new LocalDate(2016, 4, 1));
+        assertListenerStatus();
+    }
+
+    @Test(groups = "slow")
+    public void testWeeklyPlan() throws Exception {
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2015, 4, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        // First invoice
+        createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.WEEKLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2015-5-1
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 5, 1), new LocalDate(2015, 5, 8), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30); // Also = 1 month because or initial date 2015, 4, 1
+        assertListenerStatus();
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        LocalDate startDateBase = new LocalDate(2015, 5, 8);
+        for (int i = 0; i < 10; i++) {
+            final LocalDate endDateBase = startDateBase.plusDays(7);
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDateBase, endDateBase, InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+            clock.setDay(startDateBase);
+            assertListenerStatus();
+            invoiceChecker.checkInvoice(account.getId(), 3 + i, callContext, expectedInvoices);
+            expectedInvoices.clear();
+
+            startDateBase = endDateBase;
+        }
+    }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index 453884c..bab10ab 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -53,6 +53,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
@@ -65,6 +66,7 @@ import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
@@ -74,7 +76,7 @@ import org.killbill.billing.lifecycle.api.Lifecycle;
 import org.killbill.billing.lifecycle.glue.BusModule;
 import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.osgi.config.OSGIConfig;
-import org.killbill.billing.overdue.OverdueInternalApi;
+import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
 import org.killbill.billing.overdue.listener.OverdueListener;
@@ -84,8 +86,11 @@ import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TestPaymentMethodPluginBase;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.subscription.api.SubscriptionBase;
@@ -99,16 +104,12 @@ import org.killbill.billing.util.api.RecordIdApi;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
-import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.callcontext.TestCallContext;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.nodes.KillbillNodesApi;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.bus.api.PersistentBus;
-import org.skife.jdbi.v2.IDBI;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterMethod;
@@ -117,7 +118,10 @@ import org.testng.annotations.BeforeMethod;
 
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
@@ -127,6 +131,7 @@ import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
+
 public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
 
     protected static final DateTimeZone testTimeZone = DateTimeZone.UTC;
@@ -180,7 +185,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     protected SubscriptionBaseTimelineApi repairApi;
 
     @Inject
-    protected OverdueInternalApi overdueUserApi;
+    protected OverdueApi overdueUserApi;
 
     @Inject
     protected InvoiceUserApi invoiceUserApi;
@@ -200,6 +205,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     @Inject
     protected SubscriptionApi subscriptionApi;
 
+
     @Named(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME)
     @Inject
     protected MockPaymentProviderPlugin paymentPlugin;
@@ -359,9 +365,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         final Account account = accountUserApi.createAccount(accountData, callContext);
         assertNotNull(account);
 
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        refreshCallContext(account.getId());
 
         final PaymentMethodPlugin info = createPaymentMethodPlugin();
 
@@ -388,6 +392,23 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                                        .build();
     }
 
+    protected AccountData getChildAccountData(final int billingDay, final UUID parentAccountId, final boolean isPaymentDelegatedToParent) {
+        return new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+                                       .firstNameLength(6)
+                                       .email(UUID.randomUUID().toString().substring(1, 8))
+                                       .phone(UUID.randomUUID().toString().substring(1, 8))
+                                       .migrated(false)
+                                       .isNotifiedForInvoices(false)
+                                       .externalKey(UUID.randomUUID().toString().substring(1, 8))
+                                       .billingCycleDayLocal(billingDay)
+                                       .currency(Currency.USD)
+                                       .paymentMethodId(UUID.randomUUID())
+                                       .timeZone(DateTimeZone.UTC)
+                                       .parentAccountId(parentAccountId)
+                                       .isPaymentDelegatedToParent(isPaymentDelegatedToParent)
+                                       .build();
+    }
+
     protected void addMonthsAndCheckForCompletion(final int nbMonth, final NextEvent... events) {
         doCallAndCheckForCompletion(new Function<Void, Void>() {
             @Override
@@ -433,7 +454,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                     final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false);
                     properties.add(prop1);
                     return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, amount, currency, UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, refreshedCallContext());
+                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -452,7 +473,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                     properties.add(prop1);
 
                     return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, refreshedCallContext());
+                                                                       UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -472,7 +493,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                     properties.add(prop1);
 
                     return paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, invoice.getBalance(), invoice.getCurrency(), UUID.randomUUID().toString(),
-                                                                       UUID.randomUUID().toString(), properties, EXTERNAL_PAYMENT_OPTIONS, refreshedCallContext());
+                                                                       UUID.randomUUID().toString(), properties, EXTERNAL_PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -491,7 +512,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             public Payment apply(@Nullable final Void input) {
                 try {
                     return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
-                                                                     PLUGIN_PROPERTIES, PAYMENT_OPTIONS, refreshedCallContext());
+                                                                     PLUGIN_PROPERTIES, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -500,24 +521,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }, events);
     }
 
-    protected Payment refundPaymentWithAdjustmentAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
-        return doCallAndCheckForCompletion(new Function<Void, Payment>() {
-            @Override
-            public Payment apply(@Nullable final Void input) {
-
-                final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-                final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
-                properties.add(prop1);
-                try {
-                    return paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
-                                                                     properties, PAYMENT_OPTIONS, refreshedCallContext());
-                } catch (final PaymentApiException e) {
-                    fail(e.toString());
-                    return null;
-                }
-            }
-        }, events);
-    }
 
     protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
         return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
@@ -535,7 +538,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
 
                 try {
                     return paymentApi.createRefundWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
-                                                                     properties, PAYMENT_OPTIONS, refreshedCallContext());
+                                                                     properties, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -554,7 +557,33 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             public Payment apply(@Nullable final Void input) {
                 try {
                     return paymentApi.createChargebackWithPaymentControl(account, payment.getId(), amount, currency, UUID.randomUUID().toString(),
-                                                                         PAYMENT_OPTIONS, refreshedCallContext());
+                                                                         PAYMENT_OPTIONS, callContext);
+                } catch (final PaymentApiException e) {
+                    fail(e.toString());
+                    return null;
+                }
+            }
+        }, events);
+    }
+
+    protected Payment createChargeBackReversalAndCheckForCompletion(final Account account, final Payment payment, final NextEvent... events) {
+        final PaymentTransaction chargeback = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+                                                                                 new Predicate<PaymentTransaction>() {
+                                                                                     @Override
+                                                                                     public boolean apply(final PaymentTransaction input) {
+                                                                                         return TransactionType.CHARGEBACK.equals(input.getTransactionType()) &&
+                                                                                                TransactionStatus.SUCCESS.equals(input.getTransactionStatus());
+                                                                                     }
+                                                                                 });
+        return createChargeBackReversalAndCheckForCompletion(account, payment, chargeback.getExternalKey(), events);
+    }
+
+    protected Payment createChargeBackReversalAndCheckForCompletion(final Account account, final Payment payment, final String chargebackTransactionExternalKey, final NextEvent... events) {
+        return doCallAndCheckForCompletion(new Function<Void, Payment>() {
+            @Override
+            public Payment apply(@Nullable final Void input) {
+                try {
+                    return paymentApi.createChargebackReversalWithPaymentControl(account, payment.getId(), chargebackTransactionExternalKey, PAYMENT_OPTIONS, callContext);
                 } catch (final PaymentApiException e) {
                     fail(e.toString());
                     return null;
@@ -578,12 +607,12 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             @Override
             public Entitlement apply(@Nullable final Void dontcare) {
                 try {
-                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-                    final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, ImmutableList.<PluginProperty>of(), callContext);
+                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+                    final Entitlement entitlement = entitlementApi.createBaseEntitlement(accountId, spec, bundleExternalKey, overrides, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
                     assertNotNull(entitlement);
                     return entitlement;
                 } catch (final EntitlementApiException e) {
-                    fail();
+                    fail("Unable to create entitlement", e);
                     return null;
                 }
             }
@@ -612,8 +641,8 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             @Override
             public Entitlement apply(@Nullable final Void dontcare) {
                 try {
-                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, productCategory, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-                    final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, null, ImmutableList.<PluginProperty>of(), callContext);
+                    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+                    final Entitlement entitlement = entitlementApi.addEntitlement(bundleId, spec, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
                     assertNotNull(entitlement);
                     return entitlement;
                 } catch (final EntitlementApiException e) {
@@ -637,9 +666,9 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                     // Need to fetch again to get latest CTD updated from the system
                     Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
                     if (billingPolicy == null) {
-                        refreshedEntitlement = refreshedEntitlement.changePlan(productName, billingPeriod, priceList, null, ImmutableList.<PluginProperty>of(), callContext);
+                        refreshedEntitlement = refreshedEntitlement.changePlan(new PlanSpecifier(productName, billingPeriod, priceList), null, ImmutableList.<PluginProperty>of(), callContext);
                     } else {
-                        refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(productName, billingPeriod, priceList, null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
+                        refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(new PlanSpecifier(productName, billingPeriod, priceList), null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
                     }
                     return refreshedEntitlement;
                 } catch (final EntitlementApiException e) {
@@ -687,8 +716,15 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             @Override
             public Void apply(@Nullable final Void input) {
                 try {
-                    invoiceUserApi.insertCreditForInvoice(account.getId(), invoice.getId(), invoice.getBalance(), invoice.getInvoiceDate(),
-                                                          account.getCurrency(), callContext);
+                    for (final InvoiceItem invoiceItem : invoice.getInvoiceItems()) {
+                        invoiceUserApi.insertInvoiceItemAdjustment(account.getId(),
+                                                                   invoice.getId(),
+                                                                   invoiceItem.getId(),
+                                                                   invoice.getInvoiceDate(),
+                                                                   null,
+                                                                   callContext);
+                    }
+
                 } catch (final InvoiceApiException e) {
                     fail(e.toString());
                 }
@@ -703,7 +739,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             public Void apply(@Nullable final Void input) {
                 try {
                     invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoice.getId(), invoice.getInvoiceItems().get(itemNb - 1).getId(),
-                                                               invoice.getInvoiceDate(), callContext);
+                                                               invoice.getInvoiceDate(), null, callContext);
                 } catch (final InvoiceApiException e) {
                     fail(e.toString());
                 }
@@ -728,11 +764,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         assertListenerStatus();
     }
 
-    // Update the context dates (matters for payments ordering for instance)
-    protected CallContext refreshedCallContext() {
-        return new TestCallContext(callContext, clock.getUTCNow());
-    }
-
     private <T> T doCallAndCheckForCompletion(final Function<Void, T> f, final NextEvent... events) {
         final Joiner joiner = Joiner.on(", ");
         log.debug("            ************    STARTING BUS HANDLER CHECK : {} ********************", joiner.join(events));
@@ -753,7 +784,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         private final SubscriptionEventType action;
         private final UUID subscriptionId;
         private final UUID bundleId;
-        private final DateTime effectiveDate;
+        private final LocalDate effectiveDate;
         private final BillingActionPolicy billingPolicy;
 
         public TestDryRunArguments(final DryRunType dryRunType) {
@@ -775,10 +806,10 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
                                    final SubscriptionEventType action,
                                    final UUID subscriptionId,
                                    final UUID bundleId,
-                                   final DateTime effectiveDate,
+                                   final LocalDate effectiveDate,
                                    final BillingActionPolicy billingPolicy) {
             this.dryRunType = dryRunType;
-            this.spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceList, phaseType);
+            this.spec = new PlanPhaseSpecifier(productName, billingPeriod, priceList, phaseType);
             this.action = action;
             this.subscriptionId = subscriptionId;
             this.bundleId = bundleId;
@@ -807,7 +838,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
         }
 
         @Override
-        public DateTime getEffectiveDate() {
+        public LocalDate getEffectiveDate() {
             return effectiveDate;
         }
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
index cfb35ce..442d316 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
@@ -35,12 +35,12 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -55,21 +55,20 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
     //
     @Test(groups = "slow")
     public void testDryRunWithNoTargetDate() throws Exception {
-
         final int billingDay = 14;
         final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
 
         log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
-        // set clock to the initial start date
-        clock.setTime(initialCreationDate);
         int invoiceItemCount = 1;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -87,7 +86,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
         assertListenerStatus();
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
         expectedInvoices.clear();
 
@@ -101,7 +100,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addMonths(1);
         assertListenerStatus();
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
         expectedInvoices.clear();
 
@@ -111,7 +110,6 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
     }
 
-
     //
     // More sophisticated test with two non aligned subscriptions that verifies the behavior of using invoice dryRun api with no date
     // - The first subscription is an annual (SUBSCRIPTION aligned) whose billingDate is the first (we start on Jan 2nd to take into account the 30 days trial)
@@ -121,20 +119,19 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
     //
     @Test(groups = "slow")
     public void testDryRunWithNoTargetDateAndMultipleNonAlignedSubscriptions() throws Exception {
-
-        // billing date for the monthly
-        final int billingDay = 14;
-
         // Set in such a way that annual billing date will be the 1st
         final DateTime initialCreationDate = new DateTime(2014, 1, 2, 0, 0, 0, 0, testTimeZone);
         clock.setTime(initialCreationDate);
 
+        // billing date for the monthly
+        final int billingDay = 14;
+
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         int invoiceItemCount = 1;
 
         // Create ANNUAL BP
-        DefaultEntitlement baseEntitlementAnnual = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKeyAnnual", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlementAnnual = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKeyAnnual", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscriptionAnnual = subscriptionDataFromSubscription(baseEntitlementAnnual.getSubscriptionBase());
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -167,7 +164,7 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         clock.setTime(secondSubscriptionCreationDate);
 
         // Create the monthly
-        DefaultEntitlement baseEntitlementMonthly = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement baseEntitlementMonthly = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         DefaultSubscriptionBase subscriptionMonthly = subscriptionDataFromSubscription(baseEntitlementMonthly.getSubscriptionBase());
         invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext, new ExpectedInvoiceItemCheck(secondSubscriptionCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         // No end date for the trial item (fixed price of zero), and CTD should be today (i.e. when the trial started)
@@ -211,23 +208,20 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
     }
 
-
     @Test(groups = "slow")
     public void testApplyCreditOnExistingBalance() throws Exception {
-
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
 
         final int billingDay = 14;
-        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
 
         log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
 
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
-        // set clock to the initial start date
-        clock.setTime(initialCreationDate);
-
-        createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         // Move through time and verify we get the same invoice
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
@@ -244,8 +238,8 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
 
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
-        invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), callContext);
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), true, null, callContext);
         assertListenerStatus();
 
         final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
@@ -264,11 +258,71 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
         assertTrue(accountBalance3.compareTo(BigDecimal.ZERO) == 0);
 
 
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(payments.size(), 1);
 
         final Payment payment = payments.get(0);
         assertTrue(payment.getPurchasedAmount().compareTo(new BigDecimal("199.90")) == 0);
     }
 
+    @Test(groups = "slow")
+    public void testDraftInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        log.info("Beginning test with BCD of " + billingDay);
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+
+        int invoiceItemCount = 1;
+
+        DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY,  NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 6, 14), new LocalDate(2015, 7, 14), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+        // Move through time and verify we get the same invoice
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // This will verify that the upcoming invoice notification is found and the invoice is generated at the right date, with correct items
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 7, 14), new LocalDate(2015, 8, 14), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+        // add create external charge
+        final LocalDate date = clock.getToday(account.getTimeZone());
+        final List<InvoiceItem> invoiceItemList = new ArrayList<InvoiceItem>();
+        ExternalChargeInvoiceItem item = new ExternalChargeInvoiceItem(null, account.getId(), subscription.getBundleId(), "", date, BigDecimal.TEN, account.getCurrency());
+        invoiceItemList.add(item);
+        final List<InvoiceItem> draftInvoiceItems = invoiceUserApi.insertExternalCharges(account.getId(), date, invoiceItemList, false, callContext);
+
+        // add expected invoice
+        final List<ExpectedInvoiceItemCheck> expectedDraftInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        expectedDraftInvoices.add(new ExpectedInvoiceItemCheck(InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
+
+        // Move through time and verify invoices
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedDraftInvoices);
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        invoiceUserApi.commitInvoice(draftInvoiceItems.get(0).getInvoiceId(), callContext);
+        assertListenerStatus();
+
+        final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, null, callContext);
+        assertEquals(accountPayments.size(), 3);
+        assertEquals(accountPayments.get(2).getPurchasedAmount(), new BigDecimal("10.00"));
+    }
+
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
index e90cf77..8fd9f9d 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoiceWithRepairLogic.java
@@ -19,11 +19,14 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.inject.Inject;
+
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.Account;
@@ -37,8 +40,14 @@ import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
+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.payment.api.Payment;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
@@ -52,6 +61,10 @@ import static org.testng.Assert.assertNotNull;
 
 public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
+    @Inject
+    protected InvoiceDao invoiceDao;
+
+
     @AfterMethod(groups = "slow")
     public void afterMethod() throws Exception {
         super.afterMethod();
@@ -61,23 +74,23 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
     public void testSimplePartialRepairWithItemAdjustment() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(today);
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String pricelistName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
@@ -91,7 +104,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(31);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -107,10 +120,10 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         //
         busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
         invoiceUserApi.insertInvoiceItemAdjustment(account.getId(), invoices.get(1).getId(), invoices.get(1).getInvoiceItems().get(0).getId(), clock.getUTCToday(),
-                                                   BigDecimal.TEN, account.getCurrency(), callContext);
+                                                   BigDecimal.TEN, account.getCurrency(), null, callContext);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -120,15 +133,14 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10")),
-                // TODO PIERRE The cba start_date/end_date are created using the callcontext
-                new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
         //
         // Force a plan change
         //
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Blowdart", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -138,8 +150,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.ITEM_ADJ, new BigDecimal("-10")),
-                // TODO PIERRE The cba start_date/end_date are created using the callcontext
-                new ExpectedInvoiceItemCheck(callContext.getCreatedDate().toLocalDate(), callContext.getCreatedDate().toLocalDate(), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 2), new LocalDate(2012, 5, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("10")));
         invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -157,22 +168,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
     public void testMultiplePartialRepairs() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(today);
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.MONTHLY;
         final String pricelistName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
@@ -187,7 +198,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         //
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -206,7 +217,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(28);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -228,7 +239,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(5);
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Blowdart", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -256,7 +267,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(1);
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Pistol", term, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 5);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -291,7 +302,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 6);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -331,7 +342,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 7);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -373,25 +384,24 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testPartialRepairWithCompleteRefund() throws Exception {
-
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -400,7 +410,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(30);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -413,7 +423,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -430,7 +440,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -452,7 +462,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addMonths(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 5);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -480,23 +490,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testRepairWithFullItemAdjustment() throws Exception {
-
         final LocalDate today = new LocalDate(2013, 7, 19);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -505,7 +514,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(30);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -517,7 +526,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         bpEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -532,7 +541,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         // ITEM ADJUSTMENT PRIOR TO DOING THE REPAIR
         //
         final Invoice invoice1 = invoices.get(1);
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         final ExpectedPaymentCheck expectedPaymentCheck = new ExpectedPaymentCheck(clock.getUTCNow().toLocalDate(), new BigDecimal("2399.95"), TransactionStatus.SUCCESS, invoice1.getId(), Currency.USD);
         final Payment payment1 = payments.get(0);
 
@@ -551,23 +560,22 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
     //
     @Test(groups = "slow")
     public void testRepairWithPartialItemAdjustment() throws Exception {
-
         final LocalDate today = new LocalDate(2013, 7, 19);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -576,7 +584,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         clock.addDays(30);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -588,7 +596,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         bpEntitlement.cancelEntitlementWithPolicyOverrideBillingPolicy(EntitlementActionPolicy.IMMEDIATE, BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2013, 8, 18), new LocalDate(2014, 8, 18), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -603,7 +611,7 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         // ITEM ADJUSTMENT PRIOR TO DOING THE REPAIR
         //
         final Invoice invoice1 = invoices.get(1);
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         final ExpectedPaymentCheck expectedPaymentCheck = new ExpectedPaymentCheck(clock.getUTCNow().toLocalDate(), new BigDecimal("2399.95"), TransactionStatus.SUCCESS, invoice1.getId(), Currency.USD);
         final Payment payment1 = payments.get(0);
 
@@ -612,4 +620,130 @@ public class TestIntegrationInvoiceWithRepairLogic extends TestIntegrationBase {
         refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment1, iias, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
         checkNoMoreInvoiceToGenerate(account);
     }
+
+    @Test(groups = "slow")
+    public void testWithSuperflousRepairedItems() throws Exception {
+
+        // We take april as it has 30 days (easier to play with BCD)
+        final LocalDate today = new LocalDate(2012, 4, 1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(today);
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String pricelistName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        //
+
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.BLOCK);
+        assertNotNull(bpEntitlement);
+
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 1);
+        ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
+        invoiceChecker.checkInvoice(invoices.get(0).getId(), callContext, toBeChecked);
+
+        //
+        // Check we get the first invoice at the phase event
+        //
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        // Move the clock to 2012-05-02
+        clock.addDays(31);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 2);
+
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
+        invoiceChecker.checkInvoice(invoices.get(0).getId(), callContext, toBeChecked);
+
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        final Invoice lastInvoice = invoices.get(1);
+        invoiceChecker.checkInvoice(lastInvoice.getId(), callContext, toBeChecked);
+
+
+        //
+        // Let's add a bunch of items by hand to pretend we have lots of cancelling items that should be cleaned (data issue after a potential invoice bug)
+        //
+        // We test both full and partial repair.
+
+        /*
+        final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
+                           @Nullable final Integer invoiceNumber, final LocalDate invoiceDate, final LocalDate targetDate,
+                           final Currency currency, final boolean migrated, final InvoiceStatus status, final boolean isParentInvoice
+         */
+        final InvoiceModelDao shellInvoice = new InvoiceModelDao(UUID.randomUUID(), lastInvoice.getCreatedDate(), lastInvoice.getAccountId(), null,
+                                                                 lastInvoice.getInvoiceDate(), lastInvoice.getTargetDate(), lastInvoice.getCurrency(), false, InvoiceStatus.COMMITTED, false);
+
+        final InvoiceItemModelDao recurring1 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.RECURRING, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                       bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), "", "shotgun-monthly", "shotgun-monthly-evergreen",
+                                                                       null, new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), new BigDecimal("249.95"), new BigDecimal("249.95"), account.getCurrency(), null);
+
+        final InvoiceItemModelDao repair1 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.REPAIR_ADJ, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                    bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), null, null, null,
+                                                                    null, new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), new BigDecimal("-249.95"), new BigDecimal("-249.95"), account.getCurrency(), recurring1.getId());
+
+        final InvoiceItemModelDao recurring2 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.RECURRING, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                       bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), "", "shotgun-monthly", "shotgun-monthly-evergreen",
+                                                                       null, new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), new BigDecimal("249.95"), new BigDecimal("249.95"), account.getCurrency(), null);
+
+
+        final InvoiceItemModelDao repair21 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.REPAIR_ADJ, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                     bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), null, null, null,
+                                                                     null, new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 13), new BigDecimal("-100.95"), new BigDecimal("-100.95"), account.getCurrency(), recurring2.getId());
+
+        final InvoiceItemModelDao repair22 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.REPAIR_ADJ, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                     bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), null, null, null,
+                                                                     null, new LocalDate(2012, 5, 13), new LocalDate(2012, 5, 22), new BigDecimal("-100"), new BigDecimal("-100"), account.getCurrency(), recurring2.getId());
+
+        final InvoiceItemModelDao repair23 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.REPAIR_ADJ, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                     bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), null, null, null,
+                                                                     null, new LocalDate(2012, 5, 22), new LocalDate(2012, 6, 1), new BigDecimal("-49"), new BigDecimal("-49"), account.getCurrency(), recurring2.getId());
+
+
+
+        final InvoiceItemModelDao recurring3 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.RECURRING, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                       bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), "", "shotgun-monthly", "shotgun-monthly-evergreen",
+                                                                       null, new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), new BigDecimal("249.95"), new BigDecimal("249.95"), account.getCurrency(), null);
+
+
+        final InvoiceItemModelDao repair3 = new InvoiceItemModelDao(lastInvoice.getCreatedDate(), InvoiceItemType.REPAIR_ADJ, lastInvoice.getId(), lastInvoice.getAccountId(),
+                                                                    bpEntitlement.getBundleId(), bpEntitlement.getBaseEntitlementId(), null, null, null,
+                                                                    null, new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), new BigDecimal("-249.95"), new BigDecimal("-249.95"), account.getCurrency(), recurring3.getId());
+
+        List<InvoiceItemModelDao> newItems = new ArrayList<InvoiceItemModelDao>();
+        newItems.add(recurring1);
+        newItems.add(repair1);
+        newItems.add(recurring2);
+        newItems.add(repair21);
+        newItems.add(repair22);
+        newItems.add(repair23);
+        newItems.add(recurring3);
+        newItems.add(repair3);
+        invoiceDao.createInvoice(shellInvoice, newItems, false, new FutureAccountNotifications(new HashMap<UUID, List<SubscriptionNotification>>()), internalCallContext);
+
+
+        // Move ahead one month, verify nothing from previous data was generated
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 3);
+
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
+
+
+
+    }
+
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
new file mode 100644
index 0000000..e759270
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationParentInvoice.java
@@ -0,0 +1,1035 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
+import org.killbill.billing.payment.api.Payment;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class TestIntegrationParentInvoice extends TestIntegrationBase {
+
+
+
+    @Test(groups = "slow")
+    public void testParentInvoiceGeneration() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        log.info("Beginning test with BCD of " + billingDay);
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account child1Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+        final Account child2Account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTIONS AND EXPECT BOTH EVENTS EACH: NextEvent.CREATE NextEvent.INVOICE
+        createBaseEntitlementAndCheckForCompletion(child1Account.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        createBaseEntitlementAndCheckForCompletion(child2Account.getId(), "bundleKey2", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // First Parent invoice over TRIAL period
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 1);
+
+        Invoice parentInvoice = parentInvoices.get(0);
+        assertEquals(parentInvoice.getNumberOfItems(), 2);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // reload parent invoice
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+
+        // Move through time and verify new parent Invoice. No payments are expected yet.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE,
+                                      NextEvent.INVOICE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // Second Parent invoice over Recurring period
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 2);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        // total 279.95
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        // Check Child Balance. It should be > 0 here because Parent invoice is unpaid yet.
+        List<Invoice> child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
+        assertEquals(child1Invoices.size(), 2);
+        // child balance is 0 because parent invoice status is DRAFT at this point
+        assertEquals(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO), 0);
+
+        // Moving a day the NotificationQ calls the commitInvoice. Payment is expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+
+        // Check Child Balance. It should be = 0 because parent invoice had already paid.
+        child1Invoices = invoiceUserApi.getInvoicesByAccount(child1Account.getId(), false, callContext);
+        assertEquals(child1Invoices.size(), 2);
+        assertTrue(parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
+        assertTrue(child1Invoices.get(1).getBalance().compareTo(BigDecimal.ZERO) == 0);
+
+        // load children invoice items
+        final List<InvoiceItem> childrenInvoiceItems = invoiceUserApi.getInvoiceItemsByParentInvoice(parentInvoice.getId(), callContext);
+        assertEquals(childrenInvoiceItems.size(), 2);
+        assertEquals(childrenInvoiceItems.get(0).getAccountId(), child1Account.getId());
+        assertEquals(childrenInvoiceItems.get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childrenInvoiceItems.get(1).getAccountId(), child2Account.getId());
+        assertEquals(childrenInvoiceItems.get(1).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        // loading children items from non parent account should return empty list
+        assertEquals(invoiceUserApi.getInvoiceItemsByParentInvoice(child1Invoices.get(1).getId(), callContext).size(), 0);
+    }
+
+    @Test(groups = "slow")
+    public void testParentInvoiceGenerationMultipleActionsSameDay() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // Move through time and verify new parent Invoice. No payments are expected yet.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // check parent Invoice with child plan amount
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        // upgrade plan
+        busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
+        baseEntitlementChild.changePlanOverrideBillingPolicy(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, baseEntitlementChild.getLastActivePriceList().getName()), null, clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
+        assertListenerStatus();
+
+        // check parent invoice. Expected to have the same invoice item with the amount updated
+        final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+
+
+        // Moving a day the NotificationQ calls the commitInvoice. Now payment is expected
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+
+    }
+
+    @Test(groups = "slow")
+    public void testParentInvoiceGenerationChildCreditUnpaidInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        log.info("Beginning test with BCD of " + billingDay);
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // Move through time and verify new parent Invoice. No payments are expected yet.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // add credit to child account when invoice is still unpaid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        invoiceUserApi.insertCredit(childAccount.getId(), BigDecimal.TEN, clock.getUTCToday(), Currency.USD, true, "test", callContext);
+        assertListenerStatus();
+
+        final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        assertEquals(childInvoices.size(), 3);
+
+        // invoice monthly with credit
+        final Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10.00)), 0);
+        // child balance is 0 because parent invoice status is DRAFT at this point
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+
+        // check parent Invoice with child plan amount
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(19.95)), 0);
+
+        // Moving a day the NotificationQ calls the commitInvoice.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(19.95)), 0);
+        assertEquals(parentInvoice.getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
+
+        final List<Payment> accountPayments = paymentApi.getAccountPayments(parentAccount.getId(), false, false, null, callContext);
+        assertEquals(accountPayments.size(), 1);
+        assertEquals(accountPayments.get(0).getPurchasedAmount().setScale(2).compareTo(BigDecimal.valueOf(19.95)), 0);
+        assertEquals(accountPayments.get(0).getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
+
+    }
+
+    @Test(groups = "slow")
+    public void testParentInvoiceGenerationChildCreditPaidInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        log.info("Beginning test with BCD of " + billingDay);
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // Move through time and verify new parent Invoice. No payments are expected yet.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // Moving a day the NotificationQ calls the commitInvoice.
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // add credit to child account after invoice has been paid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        invoiceUserApi.insertCredit(childAccount.getId(), BigDecimal.TEN, clock.getUTCToday(), Currency.USD, true, "test", callContext);
+        assertListenerStatus();
+
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        assertEquals(childInvoices.size(), 3);
+
+        // invoice monthly with credit
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        // check parent Invoice with child plan amount
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(29.95)), 0);
+
+        final List<Payment> accountPayments = paymentApi.getAccountPayments(parentAccount.getId(), false, false, null, callContext);
+        assertEquals(accountPayments.size(), 1);
+        assertEquals(accountPayments.get(0).getPurchasedAmount().setScale(2).compareTo(BigDecimal.valueOf(29.95)), 0);
+        assertEquals(accountPayments.get(0).getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
+
+    }
+
+    // Scenario 1.a: Follow up Invoice Item Adjustment on unpaid DRAFT invoice
+    @Test(groups = "slow")
+    public void testParentInvoiceItemAdjustmentUnpaidDraftInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+
+        // issue a $10 adj when invoice is unpaid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
+        invoiceUserApi.insertInvoiceItemAdjustment(childAccount.getId(), childInvoice.getId(),
+                                                   childInvoice.getInvoiceItems().get(0).getId(),
+                                                   clock.getToday(childAccount.getTimeZone()), BigDecimal.TEN,
+                                                   childAccount.getCurrency(), "test adjustment", callContext);
+        assertListenerStatus();
+
+        // expected child invoice
+        // RECURRING : $ 249.95
+        // ITEM_ADJ : $ -10
+
+        childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        // child balance is 0 because parent invoice status is DRAFT at this point
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10.00)), 0);
+
+        // reload parent invoice
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        // check parent invoice is updated and still in DRAFT status
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(239.95)), 0);
+
+    }
+
+    // Scenario 1.b: Follow up Invoice Item Adjustment on unpaid COMMITTED invoice
+    @Test(groups = "slow")
+    public void testParentInvoiceItemAdjustmentUnpaidCommittedInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        paymentPlugin.makeNextPaymentFailWithError();
+
+        // move one day to have parent invoice paid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // issue a $10 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+        // make sure there is time difference between item adjustments.
+        // Otherwise they are created with same id and createdDate and it's used to sort them.
+        clock.addDeltaFromReality(1000);
+
+        // issue a $5 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
+        clock.addDeltaFromReality(1000);
+
+        // issue a $10 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+
+        // move one day
+        busHandler.pushExpectedEvents();
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // issue a $5 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.valueOf(5));
+        clock.addDeltaFromReality(1000);
+
+        // issue a $10 adj when invoice is unpaid
+        insertInvoiceItemAdjustmentToChildInvoice(childAccount, childInvoice, BigDecimal.TEN);
+
+        // expected child invoice
+        // RECURRING : $ 249.95
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+
+        // expected parent invoice
+        // PARENT_SUMMARY : $ 249.95
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+        // ITEM_ADJ : $ -5
+        // ITEM_ADJ : $ -10
+
+        childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
+        assertEquals(childInvoice.getNumberOfItems(), 6);
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.valueOf(209.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(3).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(4).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(5).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+
+        // reload parent invoice
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        assertEquals(parentInvoice.getNumberOfItems(), 6);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.valueOf(209.95)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(3).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(3).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(4).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(4).getAmount().compareTo(BigDecimal.valueOf(-5)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(5).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(5).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+
+    }
+
+    private void insertInvoiceItemAdjustmentToChildInvoice(final Account childAccount, final Invoice childInvoice, BigDecimal amount) throws InvoiceApiException {
+        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT, NextEvent.INVOICE_ADJUSTMENT);
+        invoiceUserApi.insertInvoiceItemAdjustment(childAccount.getId(), childInvoice.getId(),
+                                                   childInvoice.getInvoiceItems().get(0).getId(),
+                                                   clock.getToday(childAccount.getTimeZone()), amount,
+                                                   childAccount.getCurrency(), "test adjustment", callContext);
+        assertListenerStatus();
+    }
+
+    // Scenario 2: Follow up Invoice Item Adjustment on PAID invoice
+    @Test(groups = "slow")
+    public void testParentInvoiceItemAdjustmentPaidInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // move one day to have parent invoice paid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        final List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // issue a $10 adj in a paid invoice
+        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
+        invoiceUserApi.insertInvoiceItemAdjustment(childAccount.getId(),
+                                                   childInvoice.getId(),
+                                                   childInvoice.getInvoiceItems().get(0).getId(),
+                                                   clock.getToday(childAccount.getTimeZone()),
+                                                   BigDecimal.TEN,
+                                                   childAccount.getCurrency(), "test adjustment", callContext);
+        assertListenerStatus();
+
+        // expected child invoice
+        // RECURRING : $ 249.95
+        // ITEM_ADJ : $ -10
+        // CBA_ADJ : $ +10
+
+        childInvoice = invoiceUserApi.getInvoice(childInvoice.getId(), callContext);
+        assertEquals(childInvoice.getNumberOfItems(), 3);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-10)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(2).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(2).getAmount().compareTo(BigDecimal.valueOf(10)), 0);
+
+        // check parent invoices
+        parentInvoice = invoiceUserApi.getInvoice(parentInvoice.getId(), callContext);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getPaidAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
+
+    }
+
+    // Scenario 3: Repair (early cancellation, plan downgrade) on unpaid invoice
+    @Test(groups = "slow")
+    public void testParentInvoiceEarlyCancellationUnpaidInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // moving one extra day to get some cancellation difference
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // cancel subscription
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
+        baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
+        assertListenerStatus();
+
+        // expected invoices
+
+        // invoice 2:
+        // REPAIR_ADJ $ -233.29
+        // CBA_ADJ $ 233.29
+
+        // Invoice 1:
+        // RECURRING : $ 249.95
+        // CBA_ADJ $ -233.29
+
+        childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // invoice 1
+        childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        // child balance is 0 because parent invoice status is DRAFT at this point
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-233.29)), 0);
+
+        // invoice 2
+        childInvoice = childInvoices.get(2);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-233.29)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(233.29)), 0);
+
+        // check if parent invoice was updated
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.PARENT_SUMMARY);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(16.66)), 0);
+
+    }
+
+    // Scenario 4: Repair (early cancellation, plan downgrade) on PAID invoice
+    @Test(groups = "slow")
+    public void testParentInvoiceEarlyCancellationPaidInvoice() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // move one day to have parent invoice paid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
+        baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
+        assertListenerStatus();
+
+        // expected child invoices
+
+        // Invoice 2:
+        // REPAIR_ADJ $ -241.62
+        // CBA_ADJ $ 241.62
+
+        // Invoice 1: # unchanged
+        // RECURRING : $ 249.95
+
+        childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // invoice 1
+        childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+        assertEquals(childInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+
+        // invoice 2
+        childInvoice = childInvoices.get(2);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(241.62)), 0);
+
+        // check equal parent invoice
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+    }
+
+    // Scenario 5: Use of credit
+    @Test(groups = "slow")
+    public void testParentInvoiceEarlyCancellationUseCredit() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        DefaultEntitlement baseEntitlementChild = createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // ---- trial period ----
+        // Moving a day the NotificationQ calls the commitInvoice. No payment is expected because balance is 0
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        // ---- recurring period ----
+        // Move through time and verify new parent Invoice. No payments are expected.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(29);
+        assertListenerStatus();
+
+        // move one day to have parent invoice paid
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // get last child invoice
+        Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+
+        // Second Parent invoice over Recurring period
+        assertEquals(parentInvoices.size(), 2);
+
+        Invoice parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.INVOICE);
+        baseEntitlementChild.cancelEntitlementWithDateOverrideBillingPolicy(clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
+        assertListenerStatus();
+
+        // expected child invoices
+
+        // Invoice 2:
+        // REPAIR_ADJ $ -241.62
+        // CBA_ADJ $ 241.62
+
+        // Invoice 1: # unchanged
+        // RECURRING : $ 249.95
+
+        childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        // invoice 1
+        childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 1);
+        assertEquals(childInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+
+        // invoice 2
+        childInvoice = childInvoices.get(2);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.REPAIR_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(241.62)), 0);
+
+        // check equal parent invoice
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 2);
+
+        parentInvoice = parentInvoices.get(1);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.COMMITTED);
+        assertTrue(parentInvoice.isParentInvoice());
+        assertEquals(parentInvoice.getChargedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // ------
+
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+        final DateTime date = new DateTime(2014, 7, 15, 0, 0, 0, 0, testTimeZone);
+        clock.setTime(date);
+        assertListenerStatus();
+
+        createBaseEntitlementAndCheckForCompletion(childAccount.getId(), "bundleKey1", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // Move through time and verify new parent Invoice.
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        assertEquals(childInvoices.size(), 5);
+
+        childInvoice = childInvoices.get(4);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-241.62)), 0);
+
+        // check equal parent invoice
+        parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 4);
+
+        parentInvoice = parentInvoices.get(3);
+        assertEquals(parentInvoice.getNumberOfItems(), 1);
+        assertEquals(parentInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertTrue(parentInvoice.isParentInvoice());
+        // balance is 0 because parent invoice status is DRAFT
+        assertEquals(parentInvoice.getBalance().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(8.33)), 0);
+
+    }
+
+    // Scenario 6: Transfer credit
+    @Test(groups = "slow")
+    public void testParentInvoiceTransferCredit() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
+        invoiceUserApi.insertCredit(childAccount.getId(), new BigDecimal("250"), new LocalDate(clock.getUTCNow(), childAccount.getTimeZone()), childAccount.getCurrency(), true, null, callContext);
+        assertListenerStatus();
+
+        BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
+        assertEquals(childAccountCBA.compareTo(BigDecimal.valueOf(250)), 0);
+
+        BigDecimal parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
+        assertEquals(parentAccountCBA.compareTo(BigDecimal.ZERO), 0);
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE);
+        invoiceUserApi.transferChildCreditToParent(childAccount.getId(), callContext);
+        assertListenerStatus();
+
+        childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
+        assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
+
+        parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
+        assertEquals(parentAccountCBA.compareTo(BigDecimal.valueOf(250)), 0);
+
+        final List<Invoice> childInvoices = invoiceUserApi.getInvoicesByAccount(childAccount.getId(), false, callContext);
+        assertEquals(childInvoices.size(), 2);
+
+        final Invoice childInvoice = childInvoices.get(1);
+        assertEquals(childInvoice.getNumberOfItems(), 2);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.EXTERNAL_CHARGE);
+        assertEquals(childInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(250)), 0);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(childInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(-250)), 0);
+
+        // check equal parent invoice
+        final List<Invoice> parentInvoices = invoiceUserApi.getInvoicesByAccount(parentAccount.getId(), false, callContext);
+        assertEquals(parentInvoices.size(), 1);
+
+        final Invoice parentInvoice = parentInvoices.get(0);
+        assertEquals(parentInvoice.getNumberOfItems(), 2);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(0).getAmount().compareTo(BigDecimal.valueOf(-250)), 0);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
+        assertEquals(parentInvoice.getInvoiceItems().get(1).getAmount().compareTo(BigDecimal.valueOf(250)), 0);
+    }
+
+    // Scenario 6-b: Transfer credit
+    @Test(groups = "slow", expectedExceptions = InvoiceApiException.class,
+        expectedExceptionsMessageRegExp = ".* does not have credit")
+    public void testParentInvoiceTransferCreditAccountWithoutCredit() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account parentAccount = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+        final Account childAccount = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, parentAccount.getId(), true));
+
+        BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(childAccount.getId(), callContext);
+        assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
+
+        BigDecimal parentAccountCBA = invoiceUserApi.getAccountCBA(parentAccount.getId(), callContext);
+        assertEquals(parentAccountCBA.compareTo(BigDecimal.ZERO), 0);
+
+        invoiceUserApi.transferChildCreditToParent(childAccount.getId(), callContext);
+
+    }
+
+    // Scenario 6-c: Transfer credit
+    @Test(groups = "slow", expectedExceptions = InvoiceApiException.class,
+            expectedExceptionsMessageRegExp = ".* does not have a Parent Account associated")
+    public void testParentInvoiceTransferCreditAccountNoParent() throws Exception {
+
+        final int billingDay = 14;
+        final DateTime initialCreationDate = new DateTime(2014, 5, 15, 0, 0, 0, 0, testTimeZone);
+        // set clock to the initial start date
+        clock.setTime(initialCreationDate);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getChildAccountData(billingDay, null, true));
+
+        BigDecimal childAccountCBA = invoiceUserApi.getAccountCBA(account.getId(), callContext);
+        assertEquals(childAccountCBA.compareTo(BigDecimal.ZERO), 0);
+
+        invoiceUserApi.transferChildCreditToParent(account.getId(), callContext);
+
+    }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
index 7c34a8e..729e277 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoInvoiceOffTag.java
@@ -73,30 +73,30 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         add_AUTO_INVOICING_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
         // set next invoice to fail and create network
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK);
         assertNotNull(bpEntitlement);
 
-        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 0);
 
         clock.addDays(10); // DAY 10 still in trial
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 0);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE);
         clock.addDays(30); // DAY 40 out of trial
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 0);
 
         busHandler.pushExpectedEvents(NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         remove_AUTO_INVOICING_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
     }
 
@@ -105,10 +105,10 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
         // set next invoice to fail and create network
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1); // first invoice is generated immediately after creation can't reliably stop it
 
         add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
@@ -117,7 +117,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         clock.addDays(40); // DAY 40 out of trial
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1); //No additional invoices generated
     }
 
@@ -126,13 +126,13 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
         // set next invoice to fail and create network
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        final DefaultEntitlement bpEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "whatever", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "whatever", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement2);
 
-        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2); // first invoice is generated immediately after creation can't reliably stop it
 
         add_AUTO_INVOICING_OFF_Tag(bpEntitlement.getSubscriptionBase().getBundleId(), ObjectType.BUNDLE);
@@ -141,7 +141,7 @@ public class TestIntegrationWithAutoInvoiceOffTag extends TestIntegrationBase {
         clock.addDays(40); // DAY 40 out of trial
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3); // Only one additional invoice generated
     }
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
index 2338747..4d911f6 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithAutoPayOff.java
@@ -34,7 +34,7 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.google.inject.Inject;
 
@@ -69,10 +69,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE);
@@ -81,7 +81,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
 
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -93,7 +93,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         addDelayBceauseOfLackOfCorrectSynchro();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -110,10 +110,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE);
@@ -122,7 +122,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
 
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -135,7 +135,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
         addDelayBceauseOfLackOfCorrectSynchro();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -145,14 +145,14 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         }
         assertListenerStatus();
 
-        int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays().get(0);
+        int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(0);
 
         // MOVE TIME FOR RETRY TO HAPPEN
         busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(nbDaysBeforeRetry + 1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
                 continue;
@@ -169,10 +169,10 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         // CREATE FIRST NON NULL INVOICE + FIRST PAYMENT/ATTEMPT -> AUTO_PAY_OFF
@@ -181,7 +181,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         clock.addDays(31); // After trial
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -195,7 +195,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
         addDelayBceauseOfLackOfCorrectSynchro();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -206,14 +206,14 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         assertListenerStatus();
 
         // RE-ADD AUTO_PAY_OFF to ON
-        int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays().get(0);
+        int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(0);
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
         // MOVE TIME FOR RETRY TO HAPPEN -> WILL BE DISCARDED SINCE AUTO_PAY_OFF IS SET
         clock.addDays(nbDaysBeforeRetry + 1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -229,7 +229,7 @@ public class TestIntegrationWithAutoPayOff extends TestIntegrationBase {
         remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         addDelayBceauseOfLackOfCorrectSynchro();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         for (Invoice cur : invoices) {
             if (cur.getChargedAmount().compareTo(BigDecimal.ZERO) == 0) {
                 continue;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
new file mode 100644
index 0000000..c314ac2
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithCatalogUpdate.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.callcontext.DefaultCallContext;
+import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogUserApi;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PaymentMethodPlugin;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.tenant.api.DefaultTenant;
+import org.killbill.billing.tenant.api.Tenant;
+import org.killbill.billing.tenant.api.TenantApiException;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
+import org.killbill.billing.util.callcontext.UserType;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+
+public class TestIntegrationWithCatalogUpdate extends TestIntegrationBase {
+
+    @Inject
+    private CatalogUserApi catalogUserApi;
+
+    private Tenant tenant;
+    private CallContext testCallContext;
+    private Account account;
+
+    private DateTime init;
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+
+        // Set original time
+        clock.setDay(new LocalDate(2016, 6, 1));
+        init = clock.getUTCNow();
+
+        // Setup tenant
+        setupTenant();
+
+        // Setup account in right tenant
+        setupAccount();
+    }
+
+    @Test(groups = "slow")
+    public void testBasic() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final Entitlement baseEntitlement = createEntitlement("foo-monthly", true);
+
+        invoiceChecker.checkInvoice(account.getId(), 1, testCallContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, BigDecimal.TEN));
+
+        // Add another Plan in the catalog
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("superfoo-monthly", "SuperFoo", ProductCategory.BASE, account.getCurrency(), new BigDecimal("20.00"), BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc2, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 2);
+
+        // Change Plan to the newly added Plan and verify correct default rules behavior (IMMEDIATE change)
+        busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        baseEntitlement.changePlan(new PlanSpecifier("SuperFoo", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), testCallContext);
+        assertListenerStatus();
+
+        invoiceChecker.checkInvoice(account.getId(), 2, testCallContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("20.00")),
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-10.00")));
+        assertListenerStatus();
+    }
+
+    @Test(groups = "slow")
+    public void testWithMultiplePlansForOneProduct() throws CatalogApiException, EntitlementApiException {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("xxx-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentProducts().size(), 1);
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final Entitlement baseEntitlement1 = createEntitlement("xxx-monthly", true);
+
+        // Add a second plan for same product but with a 14 days trial
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("xxx-14-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc2, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentProducts().size(), 1);
+        assertEquals(catalog.getCurrentPlans().size(), 2);
+
+        final Entitlement baseEntitlement2 = createEntitlement("xxx-14-monthly", false);
+
+        // Add a second plan for same product but with a 30 days trial
+        final SimplePlanDescriptor desc3 = new DefaultSimplePlanDescriptor("xxx-30-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc3, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentProducts().size(), 1);
+        assertEquals(catalog.getCurrentPlans().size(), 3);
+
+        final Entitlement baseEntitlement3 = createEntitlement("xxx-30-monthly", false);
+
+        // Move clock 14 days
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        clock.addDays(14);
+        assertListenerStatus();
+
+        // Move clock 16 days
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        clock.addDays(16);
+        assertListenerStatus();
+    }
+
+    @Test(groups = "slow")
+    public void testError_CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("zoe-monthly", "Zoe", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("zoe-14-monthly", "Zoe", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc2, init, testCallContext);
+        catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 2);
+
+        try {
+            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Zoe", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+            entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+            fail("Creating entitlement should fail");
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST.getCode());
+        }
+    }
+
+    @Test(groups = "slow")
+    public void testWithPriceOverride() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("bar-monthly", "Bar", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final Plan plan = catalog.getCurrentPlans().iterator().next();
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("bar-monthly", null);
+
+        final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
+        overrides.add(new DefaultPlanPhasePriceOverride(plan.getFinalPhase().getName(), account.getCurrency(), null, BigDecimal.ONE));
+        final Entitlement baseEntitlement = createEntitlement(spec, overrides, true);
+
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
+        assertEquals(invoices.size(), 1);
+        assertEquals(invoices.get(0).getChargedAmount().compareTo(BigDecimal.ONE), 0);
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
+        assertEquals(invoices.size(), 2);
+        assertEquals(invoices.get(1).getChargedAmount().compareTo(BigDecimal.ONE), 0);
+
+        // Change plan to original (non overridden plan)
+        busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        baseEntitlement.changePlan(spec, null, ImmutableList.<PluginProperty>of(), testCallContext);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
+        assertEquals(invoices.size(), 3);
+        assertEquals(invoices.get(2).getChargedAmount().compareTo(new BigDecimal("9.00")), 0); // 10 (recurring) - 1 (repair)
+    }
+
+
+
+    // Use custom plan definition to create a THIRTY_DAYS plan with no trial and test issue #598
+    @Test(groups = "slow")
+    public void testWithThirtyDaysPlan() throws Exception {
+
+        // Create a per-tenant catalog with one plan
+        final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("thirty-monthly", "Thirty", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.THIRTY_DAYS, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUserApi.addSimplePlan(desc1, init, testCallContext);
+        StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("thirty-monthly", null);
+
+        createEntitlement(spec, null, true);
+
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
+        assertEquals(invoices.size(), 1);
+        assertEquals(invoices.get(0).getChargedAmount().compareTo(BigDecimal.TEN), 0);
+        assertEquals(invoices.get(0).getInvoiceItems().size(), 1);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, BigDecimal.TEN));
+        invoiceChecker.checkInvoiceNoAudits(invoices.get(0), callContext, expectedInvoices);
+
+        int invoiceSize = 2;
+        LocalDate startDate = new LocalDate(2016, 7, 1);
+        for (int i = 0; i < 14; i++) {
+
+            expectedInvoices.clear();
+
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+            clock.addDays(30);
+            assertListenerStatus();
+
+            LocalDate endDate = startDate.plusDays(30);
+            invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, testCallContext);
+            assertEquals(invoices.size(), invoiceSize);
+
+            expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, BigDecimal.TEN));
+            invoiceChecker.checkInvoiceNoAudits(invoices.get(invoices.size() - 1), callContext, expectedInvoices);
+
+            startDate = endDate;
+            invoiceSize++;
+        }
+    }
+
+    private Entitlement createEntitlement(final String planName, final boolean expectPayment) throws EntitlementApiException {
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, null);
+        return createEntitlement(spec, null, expectPayment);
+    }
+
+
+    private Entitlement createEntitlement(final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final boolean expectPayment) throws EntitlementApiException {
+        if (expectPayment) {
+            busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+        } else {
+            busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        }
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), overrides, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
+        assertListenerStatus();
+        return entitlement;
+    }
+
+    private void setupTenant() throws TenantApiException {
+        final UUID uuid = UUID.randomUUID();
+        final String externalKey = uuid.toString();
+        final String apiKey = externalKey + "-Key";
+        final String apiSecret = externalKey + "-$3cr3t";
+        // Only place where we use callContext
+        tenant = tenantUserApi.createTenant(new DefaultTenant(uuid, init, init, externalKey, apiKey, apiSecret), callContext);
+
+        testCallContext = new DefaultCallContext(tenant.getId(), "tester", CallOrigin.EXTERNAL, UserType.TEST,
+                                                 "good reason", "trust me", uuid, clock);
+    }
+
+    private void setupAccount() throws Exception {
+
+        final AccountData accountData = getAccountData(1);
+        account = accountUserApi.createAccount(accountData, testCallContext);
+        assertNotNull(account);
+
+        final PaymentMethodPlugin info = createPaymentMethodPlugin();
+        paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, info, PLUGIN_PROPERTIES, testCallContext);
+    }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
index 270f9a1..52a8257 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithDifferentBillingPeriods.java
@@ -23,9 +23,6 @@ import java.util.List;
 
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -36,7 +33,9 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.tag.ControlTagType;
+import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
@@ -58,12 +57,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         final String productName = "Shotgun";
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -72,7 +71,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addDays(31);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
@@ -85,7 +84,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
 
         changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE,  NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -100,6 +99,70 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         checkNoMoreInvoiceToGenerate(account);
     }
 
+
+    @Test(groups = "slow")
+    public void testChangeMonthlyToAnnualWithDifferentBCD() throws Exception {
+
+        // We take april as it has 30 days
+        final LocalDate today = new LocalDate(2016, 6, 1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(10));
+
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
+
+        final String productName = "Shotgun";
+
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
+        //
+
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        assertNotNull(bpEntitlement);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
+
+        assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
+
+        // Move out of trials for interesting invoices adjustments
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 2);
+        ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 1), new LocalDate(2016, 7, 10), InvoiceItemType.RECURRING, new BigDecimal("74.99")));
+        invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, toBeChecked);
+
+
+        // Invoice for a full month
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 3);
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 10), new LocalDate(2016, 8, 10), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
+
+        //
+        // MOVE MONTHLY TO ANNUAL: Because of different Billing Alignment the BCD now becomes the 1 (first non null recurring phase)
+        //
+        changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE,  NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 4);
+
+        toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
+                new ExpectedInvoiceItemCheck(new LocalDate(2016, 8, 1), new LocalDate(2017, 8, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+                new ExpectedInvoiceItemCheck(new LocalDate(2016, 8, 1), new LocalDate(2016, 8, 10), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-72.57")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, toBeChecked);
+
+        checkNoMoreInvoiceToGenerate(account);
+    }
+
+
     @Test(groups = "slow")
     public void testChangeMonthlyToQuarterly() throws Exception {
 
@@ -113,12 +176,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         final String productName = "Pistol";
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
 
@@ -127,7 +190,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addDays(31);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
@@ -140,7 +203,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
 
         changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.QUARTERLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -158,7 +221,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addMonths(2);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -182,12 +245,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         final String productName = "Shotgun";
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -199,11 +262,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         // 2012-5-12
         clock.addDays(10);
 
-        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
         entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -218,11 +281,11 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         // 2012-6-4
         clock.addDays(23);
 
-        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -234,7 +297,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addYears(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 5);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -257,12 +320,12 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         final String productName = "Shotgun";
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -279,13 +342,13 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         // 2012-5-12
         clock.addDays(10);
 
-        busHandler.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK);
         entitlementApi.pause(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // 2012-6-4
         clock.addDays(23);
-        busHandler.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+        busHandler.pushExpectedEvents(NextEvent.BLOCK);
         entitlementApi.resume(bpEntitlement.getBundleId(), clock.getUTCNow().toLocalDate(), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -294,7 +357,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         tagUserApi.removeTag(account.getId(), ObjectType.ACCOUNT, ControlTagType.AUTO_INVOICING_OFF.getId(), callContext);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
 
 
@@ -311,7 +374,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addYears(1);
         assertListenerStatus();
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -337,9 +400,9 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.setDeltaFromReality(startDate.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
         // Create subscription and check we get the initial invoice for the 30 days TRIAL
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
         // Move out of TRIAL and verify we invioice for a full year
@@ -347,7 +410,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addDays(30);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2015, 1, 1), new LocalDate(2016, 1, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -363,7 +426,7 @@ public class TestIntegrationWithDifferentBillingPeriods extends TestIntegrationB
         clock.addDays(73);
 
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE,  NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2015, 3, 15), new LocalDate(2016, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("5770.44")),
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
index f5d854c..3bfcf02 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
@@ -77,17 +77,17 @@ public class TestIntegrationWithWrittenOffTag extends TestIntegrationBase {
         add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
 
         // set next invoice to fail and create network
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 1);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
         clock.addDays(31);
         assertListenerStatus();
 
-        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         // Tag non $0 invoice with WRITTEN_OFF and remove AUTO_PAY_OFF => System should still not pay anything
@@ -95,7 +95,7 @@ public class TestIntegrationWithWrittenOffTag extends TestIntegrationBase {
         remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
         assertListenerStatus();
 
-        List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments.size(), 0);
 
     }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
index 481952d..c51b21e 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceNotifications.java
@@ -41,16 +41,15 @@ public class TestInvoiceNotifications extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testInvoiceNotificationBasic() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         // Move to end of trial =>  2012, 4, 24
         addDaysAndCheckForCompletion(23, NextEvent.INVOICE_NOTIFICATION);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index 1364114..40a5f31 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -71,7 +71,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -127,7 +127,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -195,7 +195,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
     }
 
     @Test(groups = "slow")
-    public void testPartialPaymentByPaymentPluginThenChargeback() throws Exception {
+    public void testPartialPaymentByPaymentPluginThenChargebackThenChargebackReversal() throws Exception {
         // 2012-05-01T00:03:42.000Z
         clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
 
@@ -203,7 +203,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -235,6 +235,20 @@ public class TestInvoicePayment extends TestIntegrationBase {
         invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
         Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
         Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Trigger chargeback reversal
+        payment1 = createChargeBackReversalAndCheckForCompletion(account, payment1, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 3);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
+        Assert.assertNull(payment1.getTransactions().get(2).getAmount());
+        Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
     }
 
     @Test(groups = "slow")
@@ -246,7 +260,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -260,7 +274,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);
 
         // Invoice is not paid
-        Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
+        Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
         Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
         Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
 
@@ -294,7 +308,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -334,6 +348,26 @@ public class TestInvoicePayment extends TestIntegrationBase {
         invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
         Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
         Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
+
+        // Reverse the chargeback
+        payment1 = createChargeBackReversalAndCheckForCompletion(account, payment1, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        Assert.assertEquals(payment1.getPurchasedAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().size(), 3);
+        Assert.assertEquals(payment1.getTransactions().get(0).getAmount().compareTo(new BigDecimal("249.95")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getCurrency(), Currency.USD);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(0).getProcessedCurrency(), Currency.EUR);
+        Assert.assertEquals(payment1.getTransactions().get(1).getAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getCurrency(), Currency.EUR);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedAmount().compareTo(new BigDecimal("225.44")), 0);
+        Assert.assertEquals(payment1.getTransactions().get(1).getProcessedCurrency(), Currency.EUR);
+        Assert.assertNull(payment1.getTransactions().get(2).getAmount());
+        Assert.assertNull(payment1.getTransactions().get(2).getCurrency());
+        Assert.assertEquals(payment1.getTransactions().get(2).getProcessedAmount().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertNull(payment1.getTransactions().get(2).getProcessedCurrency());
+        invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
+        Assert.assertEquals(invoice2.getBalance().compareTo(BigDecimal.ZERO), 0);
+        Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
     }
 
     @Test(groups = "slow")
@@ -346,9 +380,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
 
         clock.setDay(new LocalDate(2012, 4, 1));
 
-        busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
+        busHandler.pushExpectedEvents(NextEvent.INVOICE);
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, account.getId(), null, "Initial external charge", clock.getUTCToday(), BigDecimal.TEN, Currency.USD);
-        final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         assertListenerStatus();
 
         // Trigger first partial payment ($4) on first invoice
@@ -436,13 +470,13 @@ public class TestInvoicePayment extends TestIntegrationBase {
 
         paymentPlugin.makeNextPaymentFailWithError();
 
-        createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
         clock.addDays(30);
         assertListenerStatus();
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
 
         final Invoice invoice1 = invoices.get(0).getInvoiceItems().get(0).getInvoiceItemType() == InvoiceItemType.RECURRING ?
@@ -459,7 +493,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
 
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(payments.size(), 1);
         assertEquals(payments.get(0).getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
         assertEquals(payments.get(0).getTransactions().size(), 1);
@@ -483,7 +517,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
         assertTrue(accountBalance2.compareTo(BigDecimal.ZERO) == 0);
 
-        final List<Payment> payments2 = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments2 = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(payments2.size(), 1);
         assertEquals(payments2.get(0).getTransactions().size(), 2);
         assertEquals(payments2.get(0).getTransactions().get(1).getAmount().compareTo(new BigDecimal("249.95")), 0);
@@ -501,7 +535,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);
 
@@ -547,7 +581,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         properties.add(prop1);
         try {
             paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, updateInvoice2.getBalance(), updateInvoice2.getCurrency(), UUID.randomUUID().toString(),
-                                                        UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, refreshedCallContext());
+                                                        UUID.randomUUID().toString(), properties, PAYMENT_OPTIONS, callContext);
             Assert.fail("The payment should not succeed (and yet it will repair the broken state....)");
         } catch (final PaymentApiException expected) {
             Assert.assertEquals(expected.getCode(), ErrorCode.PAYMENT_PLUGIN_EXCEPTION.getCode());
@@ -561,7 +595,7 @@ public class TestInvoicePayment extends TestIntegrationBase {
         Assert.assertTrue(updateInvoice3.getPayments().get(0).isSuccess());
         Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);
 
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         Assert.assertEquals(payments.size(), 1);
         Assert.assertEquals(payments.get(0).getTransactions().size(), 1);
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
new file mode 100644
index 0000000..3069314
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestMigrationSubscriptions.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.entitlement.api.EntitlementSpecifier;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+//
+// These scenarios emulate commons migrations problems (they go on verifying proper entitlement startDate, and proper billing startDate along with invoices, ..)
+//
+// Note that all events associated to entitlement are called 'BLOCK' because of the underlying `BlockingState` mechanism used to implement those
+// (See http://killbill.io/blog/blockingstate-abstractions/)
+//
+public class TestMigrationSubscriptions extends TestIntegrationBase {
+
+    //
+    // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2016-1-10 (10 days in the future) and a billing date of 2016-1-31
+    // (note that 2016-1-31 + 30 days trial = 2016-03-01, which aligns well with the BCD=1 we set on our test account)
+    //
+    //
+    @Test(groups = "slow")
+    public void testSimpleMigrationBP() throws Exception {
+
+        clock.setDay(new LocalDate(2016, 1, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        // We set both entitlement and billing date with desired value
+        final LocalDate entitlementMigrationDate = new LocalDate(2016, 1, 10);
+        final LocalDate billingMigrationDate = new LocalDate(2016, 1, 31);
+
+        // Entitlement wil be created in PENDING state
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        Assert.assertEquals(entitlement.getState(), EntitlementState.PENDING);
+
+        // Move clock to entitlementMigrationDate (migration cutOverDate), and expect the associated event
+        busHandler.pushExpectedEvents(NextEvent.BLOCK);
+        clock.addDays(10);
+        assertListenerStatus();
+
+        final Entitlement activeEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        Assert.assertEquals(activeEntitlement.getState(), EntitlementState.ACTIVE);
+
+        // Move clock to billingMigrationDate and expect the CREATE event along a $0 invoice for the trial
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE);
+        clock.addDays(21);
+        assertListenerStatus();
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(billingMigrationDate, null, InvoiceItemType.FIXED, new BigDecimal("0")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Move clock next month for first RECURRING invoice
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        final LocalDate startDate = billingMigrationDate.plusDays(30);
+        final LocalDate endDate = startDate.plusMonths(1);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 2, callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
+
+    //
+    // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2015-12-20 (12 days in the past) and a billing date of 2016-2-01, we we want to skip the trial
+    // (note that since we skip the trial billingDate = 2016-2-01 aligns well with the BCD=1 we set on our test account)
+    //
+    //
+    @Test(groups = "slow")
+    public void testSimpleMigrationBPSkipTrial() throws Exception {
+
+        clock.setDay(new LocalDate(2016, 1, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        // We set both entitlement and billing date with desired value
+        final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+        final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+
+        // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
+        busHandler.pushExpectedEvents(NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        Assert.assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+
+        // Move clock next month for first RECURRING invoice (note that TRIAL was correctly skipped, we directly start RECURRING on billingMigrationDate
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        final LocalDate startDate = billingMigrationDate;
+        final LocalDate endDate = startDate.plusMonths(1);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+    }
+
+    //
+    // Scenario: On 2016-1-1, we decide to migrate a subscription with a cutOverDate of 2015-12-20 (12 days in the past) and a billing date of 2016-2-01, we we want to skip the trial.
+    //           In addition we subscription needs to be future cancelled (2016-2-15) at the time we migrate it
+    // (note that since we skip the trial billingDate = 2016-2-01 aligns well with the BCD=1 we set on our test account)
+    //
+    //
+    @Test(groups = "slow")
+    public void testSimpleMigrationBPSkipTrialWithPendingCancellation() throws Exception {
+
+        clock.setDay(new LocalDate(2016, 1, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+        final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+
+        // We set both entitlement and billing date with desired value
+        final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+        final LocalDate effectiveCancellationDate = new LocalDate(2016, 2, 15);
+
+        // Entitlement wil be created in ACTIVE state because entitlementMigrationDate was set in the past
+        busHandler.pushExpectedEvents(NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "bundleKey", null, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+        Assert.assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+
+        // Perform the cancellation (we did not move the clock, the is is future cancellation done at the time we decide to migrate)
+        entitlement.cancelEntitlementWithDate(effectiveCancellationDate, true, ImmutableList.<PluginProperty>of(), callContext);
+
+        // Billing starts straight on EVERGREEN
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+
+        // The invoice will be pro-rated up to the cancellation date
+        final LocalDate startDate = billingMigrationDate;
+        final LocalDate endDate = effectiveCancellationDate;
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("120.67")));
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Move to cancellation date
+        busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
+        clock.addMonths(14);
+        assertListenerStatus();
+
+        final Entitlement cancelledEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        Assert.assertEquals(cancelledEntitlement.getState(), EntitlementState.CANCELLED);
+    }
+
+
+    //
+    // Scenario: On 2016-1-1, we decide to migrate a bundle (one BP and one AO). We migrate straight to EVERGREEN phase. The scenario is very similar to previous one
+    //           but with an additional AO (by using the createBaseEntitlementWithAddOns api).
+    //
+    //  Note that while convenient to migrate a bundle at once (BP + AOS), one could do several calls for each subscription. The advantage of the later approach is that
+    //  the granularity in terms of alignments for when things start and which phase are skipped can be better controlled.
+    //
+    @Test(groups = "slow")
+    public void testSimpleMigrationBundle() throws Exception {
+
+        clock.setDay(new LocalDate(2016, 1, 1));
+
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        // We set both entitlement and billing date with desired value
+        final LocalDate entitlementMigrationDate = new LocalDate(2015, 12, 20);
+        final LocalDate billingMigrationDate = new LocalDate(2016, 2, 1);
+
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN);
+
+        final String externalKey = "baseExternalKey";
+        EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
+        EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(addOnSpec1, null);
+
+        final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
+        specifierList.add(baseEntitlementSpecifier);
+        specifierList.add(addOnEntitlementSpecifier1);
+
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, entitlementMigrationDate, billingMigrationDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+        Assert.assertEquals(baseEntitlement.getState(), EntitlementState.ACTIVE);
+
+        // Billing starts straight on EVERGREEN
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addMonths(1);
+        assertListenerStatus();
+    }
+
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
index d428c8a..b501af7 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPaymentRefund.java
@@ -19,8 +19,10 @@
 package org.killbill.billing.beatrix.integration;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -29,6 +31,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
@@ -42,7 +45,11 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -51,6 +58,7 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
 
 public class TestPaymentRefund extends TestIntegrationBase {
 
@@ -92,14 +100,34 @@ public class TestPaymentRefund extends TestIntegrationBase {
     }
 
     @Test(groups = "slow")
-    public void testRefundWithInvoiceAdjustment() throws Exception {
-        refundPaymentWithAdjustmentAndCheckForCompletion(account, payment, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
-        refundChecker.checkRefund(payment.getId(), callContext, new ExpectedRefundCheck(payment.getId(), true, new BigDecimal("233.82"), Currency.USD, initialCreationDate.toLocalDate()));
-        invoice = invoiceChecker.checkInvoice(account.getId(), invoiceItemCount++, callContext,
-                                              new ExpectedInvoiceItemCheck(new LocalDate(2012, 3, 2),
-                                                                           new LocalDate(2012, 3, 31), InvoiceItemType.RECURRING, new BigDecimal("233.82")),
-                                              new ExpectedInvoiceItemCheck(InvoiceItemType.REFUND_ADJ, new BigDecimal("-233.82"))
-                                             );
+    public void testFailedRefundWithInvoiceAdjustment() throws Exception {
+
+        final List<PluginProperty> properties = new ArrayList<PluginProperty>();
+        final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_WITH_ADJUSTMENTS, "true", false);
+        properties.add(prop1);
+        try {
+            paymentApi.createRefundWithPaymentControl(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(), UUID.randomUUID().toString(),
+                                                             properties, PAYMENT_OPTIONS, callContext);
+            fail("Refund with invoice adjustment should now throw an Exception");
+        } catch (final PaymentApiException e) {
+            Assert.assertEquals(e.getCause(), null);
+            // Unfortunately we lose the original error code : INVOICE_ITEMS_ADJUSTMENT_MISSING
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_PLUGIN_EXCEPTION.getCode());
+        }
+    }
+
+    @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/255",
+            expectedExceptions = PaymentApiException.class, expectedExceptionsMessageRegExp = "Payment method .* does not exist")
+    public void testRefundWithDeletedPaymentMethod() throws Exception {
+
+        // delete payment method
+        busHandler.pushExpectedEvent(NextEvent.TAG);
+        paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), true, true, new ArrayList<PluginProperty>(), callContext);
+        assertListenerStatus();
+
+        // try to create a refund for a payment with its payment method deleted
+        paymentApi.createRefund(account, payment.getId(), payment.getPurchasedAmount(), payment.getCurrency(),
+                                UUID.randomUUID().toString(), PLUGIN_PROPERTIES, callContext);
     }
 
     private void setupRefundTest() throws Exception {
@@ -114,9 +142,9 @@ public class TestPaymentRefund extends TestIntegrationBase {
         invoiceItemCount = 0;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
         invoiceChecker.checkInvoice(account.getId(), ++invoiceItemCount, callContext, new ExpectedInvoiceItemCheck(initialCreationDate.toLocalDate(), null, InvoiceItemType.FIXED, new BigDecimal("0")));
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
index f38238b..ae8b7a3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestPublicBus.java
@@ -138,29 +138,36 @@ public class TestPublicBus extends TestIntegrationBase {
 
         log.info("Beginning test with BCD of " + billingDay);
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
-        final UUID accountId = account.getId();
         assertNotNull(account);
 
         // set clock to the initial start date
         clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
 
-        String productName = "Shotgun";
-        BillingPeriod term = BillingPeriod.MONTHLY;
-        String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
-
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
         await().atMost(10, SECONDS).until(new Callable<Boolean>() {
             @Override
             public Boolean call() throws Exception {
-                // expecting ACCOUNT_CREATE, ACCOUNT_CHANGE, SUBSCRIPTION_CREATION, INVOICE_CREATION
-                return externalBusCount.get() >= 4;
+                // expecting ACCOUNT_CREATE, ACCOUNT_CHANGE, SUBSCRIPTION_CREATION (2), ENTITLEMENT_CREATE INVOICE_CREATION
+                return externalBusCount.get() == 6;
+            }
+        });
+
+        addDaysAndCheckForCompletion(31, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+
+        await().atMost(10, SECONDS).until(new Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                // 5 + SUBSCRIPTION_TRANSITION, INVOICE, PAYMENT, INVOICE_PAYMENT
+                return externalBusCount.get() == 10;
             }
         });
+
     }
 
     @Test(groups = "slow")
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
index 7970eab..17d84b4 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestSubscription.java
@@ -39,6 +39,7 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
 import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.EntitlementSpecifier;
 import org.killbill.billing.entitlement.api.Subscription;
@@ -63,22 +64,22 @@ public class TestSubscription extends TestIntegrationBase {
     public void testForcePolicy() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -87,7 +88,7 @@ public class TestSubscription extends TestIntegrationBase {
         clock.addDays(40);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -109,7 +110,7 @@ public class TestSubscription extends TestIntegrationBase {
 
         changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.MONTHLY, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, toBeChecked);
 
@@ -118,7 +119,7 @@ public class TestSubscription extends TestIntegrationBase {
         //
         changeEntitlementAndCheckForCompletion(bpEntitlement, productName, BillingPeriod.ANNUAL, BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE);
 
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 4);
 
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
@@ -141,22 +142,22 @@ public class TestSubscription extends TestIntegrationBase {
     public void testChangeOfPlan() throws Exception {
         // We take april as it has 30 days (easier to play with BCD)
         final LocalDate today = new LocalDate(2012, 4, 1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
-
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDeltaFromReality(today.toDateTimeAtCurrentTime(DateTimeZone.UTC).getMillis() - clock.getUTCNow().getMillis());
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
         final String productName = "Shotgun";
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
-        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), callContext).size(), 1);
+        assertEquals(invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext).size(), 1);
 
         assertEquals(bpEntitlement.getSubscriptionBase().getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.ANNUAL);
 
@@ -165,7 +166,7 @@ public class TestSubscription extends TestIntegrationBase {
         clock.addDays(40);
         assertListenerStatus();
 
-        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 2);
         ImmutableList<ExpectedInvoiceItemCheck> toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -176,7 +177,7 @@ public class TestSubscription extends TestIntegrationBase {
         // (Note that, the catalog is configured to use  CHANGE_OF_PLAN when moving to that plan and Not CHANGE_OF_PRICELIST which has not been implement;
         // this is a bit misleading since we are changing pricelist, but in that case pricelist change has no effect)
         changeEntitlementAndCheckForCompletion(bpEntitlement, "Assault-Rifle", BillingPeriod.ANNUAL, "rescue", BillingActionPolicy.IMMEDIATE, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertEquals(invoices.size(), 3);
         toBeChecked = ImmutableList.<ExpectedInvoiceItemCheck>of(
                 new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
@@ -197,9 +198,9 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -211,8 +212,8 @@ public class TestSubscription extends TestIntegrationBase {
         specifierList.add(addOnEntitlementSpecifier1);
         specifierList.add(addOnEntitlementSpecifier2);
 
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.CREATE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         checkNoMoreInvoiceToGenerate(account);
 
@@ -234,7 +235,7 @@ public class TestSubscription extends TestIntegrationBase {
         assertEquals(addOnEntitlement2.getLastActiveProduct().getName(), "Laser-Scope");
         assertEquals(addOnEntitlement2.getLastActiveProductCategory(), ProductCategory.ADD_ON);
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         assertTrue(invoices.size() == 1); // ONLY ONE INVOICE
         assertTrue(invoices.get(0).getInvoiceItems().size() == 3);
 
@@ -244,10 +245,111 @@ public class TestSubscription extends TestIntegrationBase {
                 new ExpectedInvoiceItemCheck(initialDate, null, InvoiceItemType.FIXED, new BigDecimal("0"))); // Shotgun
 
         invoiceChecker.checkInvoice(invoices.get(0).getId(), callContext, toBeChecked);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateSubscriptionWithAddOnsWithLimitException() throws Exception {
+        final LocalDate initialDate = new LocalDate(2015, 10, 1);
+        clock.setDay(initialDate);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec3 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
+        final String externalKey = "baseExternalKey";
+        EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
+        EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(addOnSpec1, null);
+        EntitlementSpecifier addOnEntitlementSpecifier2 = new DefaultEntitlementSpecifier(addOnSpec2, null);
+        EntitlementSpecifier addOnEntitlementSpecifier3 = new DefaultEntitlementSpecifier(addOnSpec3, null);
+
+        final List<EntitlementSpecifier> specifierList = new ArrayList<EntitlementSpecifier>();
+        specifierList.add(baseEntitlementSpecifier);
+        specifierList.add(addOnEntitlementSpecifier1);
+        specifierList.add(addOnEntitlementSpecifier2);
+        specifierList.add(addOnEntitlementSpecifier3);
+
+        // Trying to add the third add_on with the same plan should throw an exception (the limit is 2 for this plan)
+        try {
+            entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE.getCode());
+        }
     }
 
+    @Test(groups = "slow")
+    public void testCreateBaseSubscriptionAndAddOnsWithLimitException() throws Exception {
+        final LocalDate initialDate = new LocalDate(2015, 10, 1);
+        clock.setDay(initialDate);
 
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec3 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Create base subscription
+        final Entitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), account.getExternalKey(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        assertNotNull(baseEntitlement);
+
+        // Create first add_on subscription
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Create second add_on subscription with the same plan
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec2, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Trying to add the third add_on with the same plan should throw an exception (the limit is 2 for this plan)
+        try {
+            entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec3, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE.getCode());
+        }
+    }
+
+    @Test(groups = "slow")
+    public void testChangePlanWithLimitException() throws Exception {
+        final LocalDate initialDate = new LocalDate(2015, 10, 1);
+        clock.setDay(initialDate);
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
+
+        final PlanPhaseSpecifier addOnSpec1 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec2 = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec3 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Create base subscription
+        final Entitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), account.getExternalKey(), "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+        assertNotNull(baseEntitlement);
+
+        // Create first add_on subscription
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Create second add_on subscription with the same plan
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec2, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Create third add_on subscription with another plan
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        Entitlement addOn3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec3, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Trying to change the plan of the third add_on to 'Laser-Scope' plan, should throw an exception (the limit is 2 for this plan)
+        try {
+            final PlanPhaseSpecifier addOnSpecChangedPlan = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+            addOn3.changePlan(addOnSpecChangedPlan, null, ImmutableList.<PluginProperty>of(), callContext);
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE.getCode());
+        }
+    }
 
     @Test(groups = "slow")
     public void testCancelFutureSubscriptionWithPolicy() throws Exception {
@@ -256,12 +358,12 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2015, 10, 1);
 
         // No CREATE event as this is set in the future
-        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
         assertEquals(createdEntitlement.getEffectiveEndDate(), null);
         assertListenerStatus();
@@ -272,7 +374,7 @@ public class TestSubscription extends TestIntegrationBase {
         assertListenerStatus();
 
         // Move off trial and reach start/cancellation date
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         clock.addDays(30);
         assertListenerStatus();
 
@@ -288,12 +390,13 @@ public class TestSubscription extends TestIntegrationBase {
 
         final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2015, 10, 1);
 
         // No CREATE event as this is set in the future
-        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertEquals(createdEntitlement.getState(), EntitlementState.PENDING);
         assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
         assertEquals(createdEntitlement.getEffectiveEndDate(), null);
         assertListenerStatus();
@@ -312,7 +415,7 @@ public class TestSubscription extends TestIntegrationBase {
         assertListenerStatus();
 
         // Move off trial and reach start/cancellation date
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE);
         clock.addDays(30);
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
index 80b5505..0654926 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestTagApi.java
@@ -82,10 +82,10 @@ public class TestTagApi extends TestIntegrationBase {
         final BillingPeriod term = BillingPeriod.ANNUAL;
         final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
 
-        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         assertNotNull(bpEntitlement);
 
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         Assert.assertEquals(invoices.size(), 1);
 
         final Invoice invoice = invoices.get(0);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithBCDUpdate.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithBCDUpdate.java
new file mode 100644
index 0000000..fcc3d52
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithBCDUpdate.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertNotNull;
+
+public class TestWithBCDUpdate extends TestIntegrationBase {
+
+    @Inject
+    protected SubscriptionBaseInternalApi subscriptionBaseInternalApi;
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeInTrial() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-4-4 : (BP still in TRIAL)
+        clock.addDays(3);
+
+        // Set next BCD to be the 15
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15, null, internalCallContext);
+        Thread.sleep(1000);
+        assertListenerStatus();
+
+        // 2016-5-15 : Catch BCD_CHANGE event
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.NULL_INVOICE);
+        clock.addDays(11);
+        assertListenerStatus();
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(16);
+        assertListenerStatus();
+
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 15), InvoiceItemType.RECURRING, new BigDecimal("116.64")));
+        invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2016-5-15 : NEW BCD
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(14);
+        assertListenerStatus();
+
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 15), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeAfterTrialFollowOtherBCDChange() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        // Set next BCD to be the 15
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15,  null, internalCallContext);
+        Thread.sleep(1000);
+        assertListenerStatus();
+
+        // 2016-5-15 : Catch BCD_CHANGE event and repair invoice accordingly
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(14);
+        assertListenerStatus();
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 15), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 15), new LocalDate(2016, 6, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-137.07")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2016-6-01 : Original notification for 2016-6-01 (prior BCD change)
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+        clock.addDays(17);
+        assertListenerStatus();
+
+
+        // 2016-6-15
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(14);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 15), new LocalDate(2016, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Set next BCD to be the 10
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 10,  null, internalCallContext);
+        Thread.sleep(1000);
+        assertListenerStatus();
+
+        // 2016-7-10
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(25);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 10), new LocalDate(2016, 8, 10), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 10), new LocalDate(2016, 7, 15), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-41.66")));
+        invoiceChecker.checkInvoice(invoices.get(4).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeBeforeChangePlan() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 10,  null, internalCallContext);
+
+        // 2016-5-5
+        clock.addDays(4);
+        changeEntitlementAndCheckForCompletion(baseEntitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 5), new LocalDate(2016, 5, 10), InvoiceItemType.RECURRING, new BigDecimal("99.99")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 5), new LocalDate(2016, 6, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-217.70")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 5), new LocalDate(2016, 5, 5), InvoiceItemType.CBA_ADJ, new BigDecimal("117.71")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2016-5-10
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(5);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2016, 6, 10), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2016, 5, 10), InvoiceItemType.CBA_ADJ, new BigDecimal("-117.71")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+    }
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeAfterChangePlan() throws Exception {
+
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        // 2016-5-5
+        clock.addDays(4);
+        changeEntitlementAndCheckForCompletion(baseEntitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 5), new LocalDate(2016, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("522.54")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 5), new LocalDate(2016, 6, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-217.70")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 10,  null, internalCallContext);
+
+        // 2016-5-10
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(5);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2016, 6, 10), InvoiceItemType.RECURRING, new BigDecimal("599.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2016, 6, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-425.77")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+    }
+
+    @Test(groups = "slow")
+    public void testBCDChangeForAnnualSubscriptionAndCancellation() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.ANNUAL;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 10,  null, internalCallContext);
+
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(9);
+        assertListenerStatus();
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2017, 5, 10), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 10), new LocalDate(2017, 5, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2340.77")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2017, 5, 1 (at 13, 42, 0)
+        busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
+        clock.setTime(new DateTime(2017, 5, 1, 0, 13, 42, 0, testTimeZone));
+        assertListenerStatus();
+
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        //clock.setDay(new LocalDate(2017, 5, 10));
+        clock.addDays(9);
+        assertListenerStatus();
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2017, 5, 10), new LocalDate(2018, 5, 10), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+    }
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeForAO() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-4-4 : (BP still in TRIAL)
+        // Laser-Scope has 1 month DISCOUNT
+        clock.addDays(3);
+        final DefaultEntitlement aoEntitlement = addAOEntitlementAndCheckForCompletion(baseEntitlement.getBundleId(), "Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
+                                                                                       NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        // 2016-5-1 : BP out of TRIAL + AO
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(27);
+        assertListenerStatus();
+
+        // 2016-5-4: Laser-Scope out of DISCOUNT
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(3);
+        assertListenerStatus();
+
+        // 2016-6-1 : BP + AO invoice
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(28);
+        assertListenerStatus();
+
+        // 2016-6-4 : Change BCD for AO and
+        clock.addDays(3);
+
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        subscriptionBaseInternalApi.updateBCD(aoEntitlement.getId(), 4,  null, internalCallContext);
+        assertListenerStatus();
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = null;
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 4), new LocalDate(2016, 7, 4), InvoiceItemType.RECURRING, new BigDecimal("1999.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 4), new LocalDate(2016, 7, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-1799.96")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(5).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2016-7-1 : BP only
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(27);
+        assertListenerStatus();
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 1), new LocalDate(2016, 8, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(6).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // 2016-7-4 : AO only
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(3);
+        assertListenerStatus();
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 4), new LocalDate(2016, 8, 4), InvoiceItemType.RECURRING, new BigDecimal("1999.95")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(7).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        checkNoMoreInvoiceToGenerate(account);
+    }
+
+    @Test(groups = "slow")
+    public void testBlockPastUnpaidPeriodAndRealignBCD() throws Exception {
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = null;
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+
+        paymentPlugin.makeNextPaymentFailWithError();
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR, NextEvent.INVOICE_PAYMENT_ERROR);
+        clock.addDays(30);
+        assertListenerStatus();
+
+
+        //
+        // Let's assume 15 days later, the customer comes back and wants to continue using the service (after he updated his payment method)
+        //
+        // The company 'a.b.c' decides to block both the billing and entitlement for the past 15 days and also move his BCD to
+        // the 16 so he gets to pay right away and for a full period (MONTHLY)
+        //
+        // 2016-5-16
+        busHandler.pushExpectedEvents(NextEvent.INVOICE_PAYMENT_ERROR, NextEvent.PAYMENT_ERROR);
+        paymentPlugin.makeNextPaymentFailWithError();
+        clock.addDays(15);
+        assertListenerStatus();
+
+
+        // First BLOCK subscription starting from the 2016-5-1
+        // This will generate the credit for the full period, bringing by account balance to 0
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE);
+        final BlockingState blockingState = new DefaultBlockingState(baseEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "COURTESY_BLOCK", "company.a.b.c", true, true, true, null);
+        subscriptionApi.addBlockingState(blockingState,  new LocalDate(2016, 5, 1),  ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 6, 1), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-249.95")));
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 16), new LocalDate(2016, 5, 16), InvoiceItemType.CBA_ADJ, new BigDecimal("249.95")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        // Second, move the BCD to the 16
+        // Because we did not unblock yet, we don't have a new invoice but we see the NULL_INVOICE event
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.NULL_INVOICE);
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 16,  null, internalCallContext);
+        assertListenerStatus();
+
+        // Third, unblock starting at the 16, will generate a full period invoice
+        busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.INVOICE,  NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        final BlockingState unblockingState = new DefaultBlockingState(baseEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "END_OF_COURTESY_BLOCK", "company.a.b.c", false, false, false, null);
+        subscriptionApi.addBlockingState(unblockingState,  new LocalDate(2016, 5, 16),  ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 16), new LocalDate(2016, 6, 16), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
+
+
+    @Test(groups = "slow")
+    public void testBCDChangeWithEffectiveDateFromInTheFuture() throws Exception {
+
+        final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
+        assertNotNull(account);
+
+        // BP creation : Will set Account BCD to the first (2016-4-1 + 30 days = 2016-5-1)
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
+
+        // 2016-5-1 : BP out of TRIAL
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(30);
+        assertListenerStatus();
+
+        // Set next BCD to be the 15 but only starting from 2016-5-31
+        subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15,  new LocalDate(2016, 5, 31), internalCallContext);
+        Thread.sleep(1000);
+        assertListenerStatus();
+
+        // 2016-5-15 : We don't expect anything yet because of effectiveDateFrom = 2016-6-1
+        clock.addDays(14);
+        Thread.sleep(1000);
+        assertListenerStatus();
+
+        // 2016-6-1 : We expect a pro-ration from 2016-6-1 -> 2016-6-15
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(17);
+        assertListenerStatus();
+
+        final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+        List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("116.64")));
+        invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+
+        //  2016-6-15 : Finally we get the BCD_CHANGE event and start building for full monthly period
+        busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.NULL_INVOICE,  NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(14);
+        assertListenerStatus();
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 15), new LocalDate(2016, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+        expectedInvoices.clear();
+    }
+}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
index 7afcb20..b2f1779 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -35,7 +35,6 @@ import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
@@ -100,18 +99,16 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCreateSubscriptionWithCatalogPlugin() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
-        //
         // Create original subscription (Trial PHASE) -> $0 invoice.
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
     }
@@ -122,10 +119,11 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
 
         public TestCatalogPluginApi(final PriceOverride priceOverride, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) throws Exception {
             final StandaloneCatalog inputCatalog = XMLLoader.getObjectFromString(Resources.getResource("WeaponsHire.xml").toExternalForm(), StandaloneCatalog.class);
-            final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+            final List<StandaloneCatalog> versions = new ArrayList<StandaloneCatalog>();
             final StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(inputCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
             versions.add(standaloneCatalogWithPriceOverride);
-            versionedCatalog = new VersionedCatalog(getClock(), inputCatalog.getCatalogName(), inputCatalog.getRecurringBillingMode(), versions, internalTenantContext);
+            versionedCatalog = new VersionedCatalog(getClock());
+            versionedCatalog.addAll(versions);
         }
 
         @Override
@@ -133,23 +131,19 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
             return new TestModelVersionedPluginCatalog(versionedCatalog.getCatalogName(), versionedCatalog.getRecurringBillingMode(), toStandalonePluginCatalogs(versionedCatalog.getVersions()));
         }
 
-        private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalogWithPriceOverride> input) {
-            return Iterables.transform(input, new Function<StandaloneCatalogWithPriceOverride, StandalonePluginCatalog>() {
+        private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalog> input) {
+            return Iterables.transform(input, new Function<StandaloneCatalog, StandalonePluginCatalog>() {
                 @Override
-                public StandalonePluginCatalog apply(final StandaloneCatalogWithPriceOverride input) {
-                    try {
-
-                        return new TestModelStandalonePluginCatalog(new DateTime(input.getEffectiveDate()),
-                                                                    ImmutableList.copyOf(input.getCurrentSupportedCurrencies()),
-                                                                    ImmutableList.<Product>copyOf(input.getCurrentProducts()),
-                                                                    ImmutableList.<Plan>copyOf(input.getCurrentPlans()),
-                                                                    input.getStandaloneCatalog().getPriceLists().getDefaultPricelist(),
-                                                                    ImmutableList.<PriceList>copyOf(input.getStandaloneCatalog().getPriceLists().getChildPriceLists()),
-                                                                    input.getStandaloneCatalog().getPlanRules(),
-                                                                    null /*ImmutableList.<Unit>copyOf(input.getStandaloneCatalog().getCurrentUnits()) */);
-                    } catch (CatalogApiException e) {
-                        throw new IllegalStateException(e);
-                    }
+                public StandalonePluginCatalog apply(final StandaloneCatalog input) {
+
+                    return new TestModelStandalonePluginCatalog(new DateTime(input.getEffectiveDate()),
+                                                                ImmutableList.copyOf(input.getCurrentSupportedCurrencies()),
+                                                                ImmutableList.<Product>copyOf(input.getCurrentProducts()),
+                                                                ImmutableList.<Plan>copyOf(input.getCurrentPlans()),
+                                                                input.getPriceLists().getDefaultPricelist(),
+                                                                ImmutableList.<PriceList>copyOf(input.getPriceLists().getChildPriceLists()),
+                                                                input.getPlanRules(),
+                                                                null /* ImmutableList.<Unit>copyOf(input.getCurrentUnits()) */);
                 }
 
                 private <I, C extends I> List<I> listOf(@Nullable C[] input) {
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
index 93920a0..6a88e13 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithEntilementPlugin.java
@@ -29,6 +29,7 @@ import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
 import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
@@ -90,18 +91,16 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
         testEntitlementPluginApi.setPlanPhasePriceOverride(null);
     }
 
-
-        @Test(groups = "slow")
+    @Test(groups = "slow")
     public void testCreateSubscriptionWithEntitlementPlugin() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
         overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, BigDecimal.TEN, null));
 
@@ -109,7 +108,7 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
 
         //
         // Create original subscription (Trial PHASE) -> $0 invoice.
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
 
@@ -146,17 +145,22 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
                     public boolean isAborted() {
                         return false;
                     }
-
                     @Override
-                    public LocalDate getAdjustedEffectiveDate() {
+                    public LocalDate getAdjustedEntitlementEffectiveDate() {
+                        return null;
+                    }
+                    @Override
+                    public LocalDate getAdjustedBillingEffectiveDate() {
+                        return null;
+                    }
+                    @Override
+                    public BillingActionPolicy getAdjustedBillingActionPolicy() {
                         return null;
                     }
-
                     @Override
                     public List<EntitlementSpecifier> getAdjustedEntitlementSpecifiers() {
                         return entitlementSpecifiers;
                     }
-
                     @Override
                     public Iterable<PluginProperty> getAdjustedPluginProperties() {
                         return null;
@@ -180,5 +184,4 @@ public class TestWithEntilementPlugin extends TestIntegrationBase {
             this.planPhasePriceOverride = planPhasePriceOverride;
         }
     }
-
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
index 4a351ac..4113d01 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithFakeKPMPlugin.java
@@ -37,6 +37,7 @@ import org.killbill.billing.notification.plugin.api.ExtBusEvent;
 import org.killbill.billing.notification.plugin.api.ExtBusEventType;
 import org.killbill.billing.osgi.BundleRegistry;
 import org.killbill.billing.osgi.BundleWithConfig;
+import org.killbill.billing.osgi.FileInstall;
 import org.killbill.billing.osgi.PureOSGIBundleFinder;
 import org.killbill.billing.osgi.api.PluginInfo;
 import org.killbill.billing.osgi.api.PluginStateChange;
@@ -50,6 +51,7 @@ import org.killbill.billing.osgi.pluginconf.PluginConfigException;
 import org.killbill.billing.osgi.pluginconf.PluginFinder;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.jackson.ObjectMapper;
+import org.killbill.billing.util.nodes.KillbillNodesApi;
 import org.killbill.billing.util.nodes.NodeCommand;
 import org.killbill.billing.util.nodes.NodeCommandMetadata;
 import org.killbill.billing.util.nodes.NodeCommandProperty;
@@ -223,8 +225,8 @@ public class TestWithFakeKPMPlugin extends TestIntegrationBase {
         private final List<BundleWithMetadata> bundles;
 
         @Inject
-        public FakeBundleRegistry(final PureOSGIBundleFinder osgiBundleFinder, final PluginFinder pluginFinder, final PluginConfigServiceApi pluginConfigServiceApi) {
-            super(osgiBundleFinder, pluginFinder, pluginConfigServiceApi);
+        public FakeBundleRegistry() {
+            super(null);
             bundles = new ArrayList<BundleWithMetadata>();
         }
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
index b2c1f6b..54de017 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithPriceOverride.java
@@ -32,6 +32,7 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
@@ -47,19 +48,18 @@ public class TestWithPriceOverride extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCreatWithFixedPriceOverride() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
         overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-trial", account.getCurrency(), BigDecimal.ONE, null, null));
 
-        final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(clock.getUTCToday(), null, InvoiceItemType.FIXED, new BigDecimal("1")));
@@ -67,19 +67,18 @@ public class TestWithPriceOverride extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testCreateWithRecurringPriceOverride() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
         overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, BigDecimal.TEN, null));
 
-        final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementWithPriceOverrideAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, overrides, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -99,16 +98,15 @@ public class TestWithPriceOverride extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testChangePlanWithRecurringPriceOverride() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -123,7 +121,7 @@ public class TestWithPriceOverride extends TestIntegrationBase {
         overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, new BigDecimal("279.95"), null));
 
         busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        bpSubscription.changePlanOverrideBillingPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, overrides, new LocalDate(2012, 5, 1), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
+        bpSubscription.changePlanOverrideBillingPolicy(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), overrides, new LocalDate(2012, 5, 1), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         invoiceChecker.checkInvoice(account.getId(), 3, callContext,
@@ -137,27 +135,26 @@ public class TestWithPriceOverride extends TestIntegrationBase {
         invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("279.95")));
     }
 
-
     // See issue #596
     @Test(groups = "slow")
     public void testChangePlanWithRecurringPriceOverrideAndSamePlan() throws Exception {
 
-        final AccountData accountData = getAccountData(1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
-        accountChecker.checkAccount(account.getId(), accountData, callContext);
-
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK,  NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
 
         // Create the add-on
         final DefaultEntitlement aoEntitlement = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
-                                                                                       NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+                                                                                       NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
 
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.PHASE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
         clock.addDays(30);
@@ -165,15 +162,13 @@ public class TestWithPriceOverride extends TestIntegrationBase {
 
         // Trigger change plan for AO with price override
         clock.addDays(4);
+        assertListenerStatus();
 
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
         overrides.add(new DefaultPlanPhasePriceOverride("telescopic-scope-monthly-evergreen", account.getCurrency(), null, new BigDecimal("1200.00"), null));
 
         busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        aoEntitlement.changePlanOverrideBillingPolicy("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, overrides, new LocalDate(2012, 5, 5), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
+        aoEntitlement.changePlanOverrideBillingPolicy(new PlanSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), overrides, new LocalDate(2012, 5, 5), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
     }
-
-
-
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
index 07726ee..c0e2582 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTaxItems.java
@@ -106,18 +106,17 @@ public class TestWithTaxItems extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testBasicTaxItems() throws Exception {
+        // We take april as it has 30 days (easier to play with BCD)
+        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+        clock.setDay(new LocalDate(2012, 4, 1));
 
         final AccountData accountData = getAccountData(1);
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        // We take april as it has 30 days (easier to play with BCD)
-        // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
-        clock.setDay(new LocalDate(2012, 4, 1));
-
         //
         // Create original subscription (Trial PHASE) -> $0 invoice.
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
 
@@ -128,7 +127,7 @@ public class TestWithTaxItems extends TestIntegrationBase {
         assertListenerStatus();
 
         // Add Cleaning ADD_ON => No Invoice
-        busHandler.pushExpectedEvent(NextEvent.CREATE);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
         addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY);
         assertListenerStatus();
 
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
index 07d2ecd..de723e3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.IllegalInstantException;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountData;
@@ -75,12 +76,12 @@ public class TestWithTimeZones extends TestIntegrationBase {
         final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
 
         final TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
-                                                                   SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+                                                                   SubscriptionEventType.START_BILLING, null, null, null, null);
         final Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
         expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
         invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
 
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
@@ -131,10 +132,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
-        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
-        Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
+        Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Cancel the next month specifying just a LocalDate
@@ -151,7 +151,7 @@ public class TestWithTimeZones extends TestIntegrationBase {
         assertListenerStatus();
 
         // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01")
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         Assert.assertEquals(invoices.size(), 1);
     }
 
@@ -177,10 +177,9 @@ public class TestWithTimeZones extends TestIntegrationBase {
         final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
-        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
-        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
-        Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
+        Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Cancel the next month specifying just a LocalDate
@@ -196,7 +195,63 @@ public class TestWithTimeZones extends TestIntegrationBase {
         assertListenerStatus();
 
         // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
         Assert.assertEquals(invoices.size(), 1);
     }
+
+    @Test(groups = "slow")
+    public void testReferenceTimeInDSTGap() throws Exception {
+        final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+        clock.setTime(new DateTime(2015, 3, 7, 2, 0, 0, tz));
+
+        final AccountData accountData = new MockAccountBuilder().currency(Currency.USD)
+                                                                .timeZone(tz)
+                                                                .build();
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+        Assert.assertEquals(account.getTimeZone(), tz);
+        Assert.assertEquals(account.getFixedOffsetTimeZone(), DateTimeZone.forOffsetHours(-8));
+
+        // Note the gap: 2015-03-07T02:00:00.000-08:00 to 2015-03-08T03:00:00.000-07:00
+        clock.addDays(1);
+
+        try {
+            // See TimeAwareContext#toUTCDateTime (which uses account.getFixedOffsetTimeZone() instead)
+            new DateTime(clock.getUTCToday().getYear(),
+                         clock.getUTCToday().getMonthOfYear(),
+                         clock.getUTCToday().getDayOfMonth(),
+                         account.getReferenceTime().toDateTime(tz).getHourOfDay(),
+                         account.getReferenceTime().toDateTime(tz).getMinuteOfHour(),
+                         account.getReferenceTime().toDateTime(tz).getSecondOfMinute(),
+                         account.getTimeZone());
+            Assert.fail();
+        } catch (final IllegalInstantException e) {
+            // Illegal instant due to time zone offset transition (daylight savings time 'gap'): 2015-03-08T10:00:00.000 (America/Los_Angeles)
+        }
+
+        busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
+        // Pass a date of today, to trigger TimeAwareContext#toUTCDateTime
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        Assert.assertEquals(entitlement.getEffectiveStartDate().compareTo(new LocalDate("2015-03-08")), 0);
+        Assert.assertEquals(((DefaultEntitlement) entitlement).getBasePlanSubscriptionBase().getStartDate().compareTo(new DateTime("2015-03-08T02:00:00.000-08:00")), 0);
+
+        invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-04-08"), callContext);
+
+        busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        clock.addDays(31);
+        assertListenerStatus();
+
+        invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-05-08"), callContext);
+
+        for (int i = 0; i < 25 ; i++) {
+            busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+            clock.addMonths(1);
+            assertListenerStatus();
+
+            invoiceChecker.checkChargedThroughDate(entitlement.getId(), new LocalDate("2015-03-08").plusMonths(3 + i), callContext);
+        }
+    }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 80ccbac..9aa8eb1 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -41,6 +41,7 @@ import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.usage.api.SubscriptionUsageRecord;
 import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageApiException;
 import org.killbill.billing.usage.api.UsageRecord;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.skife.jdbi.v2.Handle;
@@ -60,18 +61,18 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testWithNoUsageInPeriodAndOldUsage() throws Exception {
-        final AccountData accountData = getAccountData(1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
-        accountChecker.checkAccount(account.getId(), accountData, callContext);
-
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -79,7 +80,7 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
 
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
@@ -128,18 +129,18 @@ public class TestConsumableInArrear extends TestIntegrationBase {
 
     @Test(groups = "slow")
     public void testWithCancellation() throws Exception {
-        final AccountData accountData = getAccountData(1);
-        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
-        accountChecker.checkAccount(account.getId(), accountData, callContext);
-
         // We take april as it has 30 days (easier to play with BCD)
         // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
         clock.setDay(new LocalDate(2012, 4, 1));
 
+        final AccountData accountData = getAccountData(1);
+        final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+        accountChecker.checkAccount(account.getId(), accountData, callContext);
+
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -147,7 +148,7 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
 
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
         setUsage(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
@@ -199,9 +200,9 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         accountChecker.checkAccount(account.getId(), accountData, callContext);
 
         //
-        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE, NextEvent.BLOCK NextEvent.INVOICE
         //
-        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
         // Check bundle after BP got created otherwise we get an error from auditApi.
         subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
         invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
@@ -210,7 +211,7 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         //
         // ADD ADD_ON ON THE SAME DAY
         //
-        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.NULL_INVOICE);
+        final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
         assertListenerStatus();
 
         busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
@@ -245,12 +246,12 @@ public class TestConsumableInArrear extends TestIntegrationBase {
         Assert.assertEquals(countNotifications.intValue(), 4);
     }
 
-    private void setUsage(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) {
+    private void setUsage(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) throws UsageApiException {
         final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
         usageRecords.add(new UsageRecord(startDate, amount));
         final List<UnitUsageRecord> unitUsageRecords = new ArrayList<UnitUsageRecord>();
         unitUsageRecords.add(new UnitUsageRecord(unitType, usageRecords));
-        final SubscriptionUsageRecord record = new SubscriptionUsageRecord(subscriptionId, unitUsageRecords);
+        final SubscriptionUsageRecord record = new SubscriptionUsageRecord(subscriptionId, UUID.randomUUID().toString(), unitUsageRecords);
         usageUserApi.recordRolledUpUsage(record, context);
     }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
index 46c8e74..fbc182f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/AuditChecker.java
@@ -248,7 +248,7 @@ public class AuditChecker {
 
     private <T extends EntitySqlDao<M, E>, M extends EntityModelDao<E>, E extends Entity> M extractEntityModelFromEntityWithTargetRecordId(final UUID entityId, final UUID auditLogId, final Class<T> sqlDao, final CallContext context, final boolean useHistory) {
 
-        final M modelDaoThatGivesMeTableName = dbi.onDemand(sqlDao).getById(entityId.toString(), callContextFactory.createInternalCallContext(context));
+        final M modelDaoThatGivesMeTableName = dbi.onDemand(sqlDao).getById(entityId.toString(), callContextFactory.createInternalCallContextWithoutAccountRecordId(context));
 
         Integer targetRecordId = dbi.withHandle(new HandleCallback<Integer>() {
             @Override
@@ -263,7 +263,7 @@ public class AuditChecker {
             Long entityRecordId = nonEntityDao.retrieveHistoryTargetRecordId(Long.valueOf(targetRecordId), modelDaoThatGivesMeTableName.getHistoryTableName());
             targetRecordId = new Integer(entityRecordId.intValue());
         }
-        return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContext(context));
+        return dbi.onDemand(sqlDao).getByRecordId(Long.valueOf(targetRecordId), callContextFactory.createInternalCallContextWithoutAccountRecordId(context));
     }
 
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 7a10f54..8060745 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -66,7 +66,7 @@ public class InvoiceChecker {
     }
 
     public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
-        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, context);
+        final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, context);
         //Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
         final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
         checkInvoice(invoice.getId(), context, expected);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
index efaf1ea..03440c5 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/PaymentChecker.java
@@ -59,7 +59,7 @@ public class PaymentChecker {
     }
 
     public Payment checkPayment(final UUID accountId, final int paymentOrderingNumber, final CallContext context, final ExpectedPaymentCheck expected) throws PaymentApiException {
-        final List<Payment> payments = paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), context);
+        final List<Payment> payments = paymentApi.getAccountPayments(accountId, false, false, ImmutableList.<PluginProperty>of(), context);
         Assert.assertEquals(payments.size(), paymentOrderingNumber);
         final Payment payment = payments.get(paymentOrderingNumber - 1);
         final PaymentTransaction transaction = getPurchaseTransaction(payment);
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
index 6eb8afd..9472f6b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/RefundChecker.java
@@ -67,7 +67,7 @@ public class RefundChecker {
 
     public PaymentTransaction checkRefund(final UUID paymentId, final CallContext context, ExpectedRefundCheck expected) throws PaymentApiException {
 
-        final Payment payment = paymentApi.getPayment(paymentId, false, ImmutableList.<PluginProperty>of(), context);
+        final Payment payment = paymentApi.getPayment(paymentId, false, false, ImmutableList.<PluginProperty>of(), context);
         final PaymentTransaction refund = Iterables.tryFind(payment.getTransactions(), new Predicate<PaymentTransaction>() {
             @Override
             public boolean apply(final PaymentTransaction input) {
diff --git a/beatrix/src/test/resources/overdueWithNumberOfUnpaidInvoicesCondition.xml b/beatrix/src/test/resources/overdueWithNumberOfUnpaidInvoicesCondition.xml
new file mode 100644
index 0000000..798eea1
--- /dev/null
+++ b/beatrix/src/test/resources/overdueWithNumberOfUnpaidInvoicesCondition.xml
@@ -0,0 +1,53 @@
+<!--
+  ~ Copyright 2016 The Billing Project, LLC
+  ~
+  ~ The Billing Project 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.
+  -->
+
+<overdueConfig>
+   <accountOverdueStates>
+       <state name="OD3">
+           <condition>
+               <numberOfUnpaidInvoicesEqualsOrExceeds>5</numberOfUnpaidInvoicesEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD3</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD2">
+           <condition>
+               <numberOfUnpaidInvoicesEqualsOrExceeds>3</numberOfUnpaidInvoicesEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD2</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD1">
+           <condition>
+               <numberOfUnpaidInvoicesEqualsOrExceeds>2</numberOfUnpaidInvoicesEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD1</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+   </accountOverdueStates>
+</overdueConfig>
diff --git a/beatrix/src/test/resources/overdueWithTotalUnpaidInvoiceBalanceCondition.xml b/beatrix/src/test/resources/overdueWithTotalUnpaidInvoiceBalanceCondition.xml
new file mode 100644
index 0000000..b50d999
--- /dev/null
+++ b/beatrix/src/test/resources/overdueWithTotalUnpaidInvoiceBalanceCondition.xml
@@ -0,0 +1,53 @@
+<!--
+  ~ Copyright 2016 The Billing Project, LLC
+  ~
+  ~ The Billing Project 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.
+  -->
+
+<overdueConfig>
+   <accountOverdueStates>
+       <state name="OD3">
+           <condition>
+               <totalUnpaidInvoiceBalanceEqualsOrExceeds>1200.0</totalUnpaidInvoiceBalanceEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD3</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD2">
+           <condition>
+               <totalUnpaidInvoiceBalanceEqualsOrExceeds>600.0</totalUnpaidInvoiceBalanceEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD2</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD1">
+           <condition>
+               <totalUnpaidInvoiceBalanceEqualsOrExceeds>260.0</totalUnpaidInvoiceBalanceEqualsOrExceeds>
+           </condition>
+           <externalMessage>Reached OD1</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+   </accountOverdueStates>
+</overdueConfig>
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
index 11044e6..4701c94 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
@@ -96,7 +96,7 @@
 
     <plans>
         <plan name="pistol-monthly">
-            <effectiveDateForExistingSubscriptons>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptons>
+            <effectiveDateForExistingSubscriptions>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
             <product>Pistol</product>
             <initialPhases>
                 <phase type="TRIAL">
@@ -334,6 +334,8 @@
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
         <childPriceList name="SpecialDiscount">
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
index e1bed72..d75891d 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
@@ -295,6 +295,8 @@
             <plans>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
         <childPriceList name="SpecialDiscount">
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
index f5501e5..8ad8ba4 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
@@ -215,6 +215,9 @@
         <defaultPriceList name="DEFAULT">
             <plans>
                 <plan>shotgun-monthly</plan>
+                <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
     </priceLists>

bin/import-account 96(+79 -17)

diff --git a/bin/import-account b/bin/import-account
old mode 100644
new mode 100755
index 68060b8..aca6838
--- a/bin/import-account
+++ b/bin/import-account
@@ -31,11 +31,10 @@ USERNAME=${USERNAME-root}
 PASSWORD=${PASSWORD-root}
 
 # Temporary directory
-TMP_DIR=/var/tmp
+TMP_DIR=$(mktemp -d)
 
 WHO=`whoami`
 
-
 function fill_empty_columns() {
 
     local filename=$1
@@ -46,6 +45,14 @@ function fill_empty_columns() {
         mv $tmp $filename
         grep ',,' $filename > /dev/null
     done
+
+    grep ',$' $filename > /dev/null
+    while [[ $? = 0 ]]; do
+        cat $filename | sed s/,$/,\\\\N/ > $tmp
+        mv $tmp $filename
+        grep ',$' $filename > /dev/null
+    done
+
 }
 
 function replace_boolean() {
@@ -66,41 +73,96 @@ function replace_boolean() {
     mv $tmp $filename
 }
 
+function fix_dates() {
+
+    local filename=$1
+    local tmp=${filename}.tmp
+
+    cat $filename | sed s/+0000\",/\",/g > $tmp
+    mv $tmp $filename
+}
+
 function export_data() {
     local account_id=$1
-    curl $KILLBILL_URL/1.0/kb/export/$1 -H"X-Killbill-CreatedBy: $WHO" | split -p '--'  --
+    curl $KILLBILL_URL/1.0/kb/export/$1 -H"X-Killbill-CreatedBy: $WHO" > $TMP_DIR/kbdump
 }
 
-
 function import_data() {
     local filename=$1
-     mysqlimport --ignore-lines=1 --fields-terminated-by=, --fields-enclosed-by=\" --verbose -u$USERNAME -p$PASSWORD $DATABASE $TMP_DIR/$filename
+    local columns_names=$2
+    mysqlimport --local --ignore-lines=1 --fields-terminated-by=, --fields-enclosed-by=\" --columns=$columns_names --verbose -u$USERNAME -p$PASSWORD $DATABASE $TMP_DIR/$filename
 }
 
-function main() {
-
-    local account_id=$1
-    export_data $account_id
+function sanitize_and_import() {
+    cd $TMP_DIR
+    echo "Splitting dump $1 into tables. Working dir is $TMP_DIR"
+    cat $1 | split -p '--'  --
 
     for i in `ls x*`; do
-
         # Extract table name and move temp file with that name
         table_name=$(cat $i | head -1 | awk '{print $2}')
+        columns_names=$(cat $i | head -1 | awk '{print $3}')
         rm -f $table_name
         mv $i $table_name
+        echo "Importing $table_name"
 
         # Fill empty column with '\N'
         fill_empty_columns $table_name
 
         replace_boolean $table_name
 
-        import_data $table_name
-     done
-}
+        fix_dates $table_name
 
-# Setup
+        import_data $table_name $columns_names
+    done
+}
 
-cd $TMP_DIR
-rm -f xa*
-main $1
+# test if user is running gnu-getopt
+TEST=`getopt -o "a:" -l "action:" -- --action dump`
+if [ "$TEST" != " --action 'dump' --" ]; then
+    echo "You are not using gnu-getopt or latest getopt."
+    echo "For Mac OS X, please upgrade 'getopt' to 'gnu-getopt',"
+    echo "For Linux, please upgrade 'getopt'."
+    exit
+fi
+
+ARGS=`getopt -o "a:" -l "action:,help" -n "import-account" -- "$@"`
+eval set -- "${ARGS}"
+
+function usage() {
+    echo -n "./import-account"
+    echo -n " -a|--action <export|import>"
+    echo -n " --help this message"
+    echo
+    exit 1
+}
 
+while true; do
+  case "$1" in
+    -a|--action) ACTION=$2; shift 2;;
+    --help) usage; shift;;
+    --) shift; break;;
+  esac
+done
+
+if [ -z $ACTION ]; then
+    echo "Need to specify an action"
+    usage
+fi
+
+if [ $ACTION == "export" ]; then
+  if [ -z $1 ]; then
+      echo "Need to specify an account id"
+      usage
+  fi
+  export_data $1
+  sanitize_and_import $TMP_DIR/kbdump
+fi
+
+if [ $ACTION == "import" ]; then
+  if [ -z $1 ]; then
+      echo "Need to specify a file"
+      usage
+  fi
+  sanitize_and_import $1
+fi

catalog/pom.xml 41(+40 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 4ef6bf4..49aad72 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
@@ -27,6 +27,10 @@
     <name>killbill-catalog</name>
     <dependencies>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>jsr305</artifactId>
             <scope>provided</scope>
@@ -41,6 +45,21 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -50,6 +69,15 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-api</artifactId>
         </dependency>
@@ -107,10 +135,21 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-queue</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
index 63e58a4..2adf38c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
@@ -22,12 +22,18 @@ import java.net.URI;
 
 import javax.inject.Inject;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.CatalogUpdater;
 import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.CatalogUserApi;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.caching.CatalogCache;
 import org.killbill.billing.tenant.api.TenantApiException;
@@ -45,6 +51,7 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
     private final TenantUserApi tenantApi;
     private final CatalogCache catalogCache;
 
+
     @Inject
     public DefaultCatalogUserApi(final CatalogService catalogService,
                                  final TenantUserApi tenantApi,
@@ -58,14 +65,14 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
 
     @Override
     public Catalog getCatalog(final String catalogName, final TenantContext tenantContext) throws CatalogApiException {
-        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
-        return catalogService.getFullCatalog(internalTenantContext);
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
+        return catalogService.getFullCatalog(true, true, internalTenantContext);
     }
 
     @Override
     public StaticCatalog getCurrentCatalog(final String catalogName, final TenantContext tenantContext) throws CatalogApiException {
         final InternalTenantContext internalTenantContext = createInternalTenantContext(tenantContext);
-        return catalogService.getCurrentCatalog(internalTenantContext);
+        return catalogService.getCurrentCatalog(true, true, internalTenantContext);
     }
 
     @Override
@@ -85,9 +92,57 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
         }
     }
 
+
+    @Override
+    public void createDefaultEmptyCatalog(final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
+
+        try {
+            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
+            final StandaloneCatalog currentCatalog = getCurrentStandaloneCatalogForTenant(internalTenantContext);
+            final CatalogUpdater catalogUpdater = (currentCatalog != null) ?
+                                                  new CatalogUpdater(currentCatalog) :
+                                                  new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, effectiveDate, null);
+
+            catalogCache.clearCatalog(internalTenantContext);
+            tenantApi.updateTenantKeyValue(TenantKey.CATALOG.toString(), catalogUpdater.getCatalogXML(), callContext);
+        } catch (TenantApiException e) {
+            throw new CatalogApiException(e);
+        }
+    }
+
+    @Override
+    public void addSimplePlan(final SimplePlanDescriptor descriptor, final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
+
+        try {
+            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
+            final StandaloneCatalog currentCatalog = getCurrentStandaloneCatalogForTenant(internalTenantContext);
+            final CatalogUpdater catalogUpdater = (currentCatalog != null) ?
+                                                  new CatalogUpdater(currentCatalog) :
+                                                  new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, effectiveDate, descriptor.getCurrency());
+
+            catalogUpdater.addSimplePlanDescriptor(descriptor);
+
+            catalogCache.clearCatalog(internalTenantContext);
+            tenantApi.updateTenantKeyValue(TenantKey.CATALOG.toString(), catalogUpdater.getCatalogXML(), callContext);
+        } catch (TenantApiException e) {
+            throw new CatalogApiException(e);
+        }
+    }
+
+
+    private StandaloneCatalog getCurrentStandaloneCatalogForTenant(final InternalTenantContext internalTenantContext) throws CatalogApiException {
+        final VersionedCatalog versionedCatalog = (VersionedCatalog) catalogService.getCurrentCatalog(false, false, internalTenantContext);
+        if (versionedCatalog != null && !versionedCatalog.getVersions().isEmpty()) {
+            final StandaloneCatalog standaloneCatalogWithPriceOverride = versionedCatalog.getVersions().get(versionedCatalog.getVersions().size() - 1);
+            return standaloneCatalogWithPriceOverride;
+        } else {
+            return null;
+        }
+    }
+
     private InternalTenantContext createInternalTenantContext(final TenantContext tenantContext) {
-        // Only tenantRecordId will be populated-- this is important to always create the (ehcache) key the same way
-        return internalCallContextFactory.createInternalTenantContext(tenantContext);
+        // Only tenantRecordId will be populated -- this is important to always create the (ehcache) key the same way
+        return internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
     }
 
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java
new file mode 100644
index 0000000..9744387
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultSimplePlanDescriptor.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog.api.user;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+
+public class DefaultSimplePlanDescriptor implements SimplePlanDescriptor {
+
+    private final String planId;
+    private final String productName;
+    private final ProductCategory productCategory;
+    private final Currency currency;
+    private final BigDecimal amount;
+    private final BillingPeriod billingPeriod;
+    private final Integer trialLength;
+    private final TimeUnit trialTimeUnit;
+    private final List<String> availableBaseProducts;
+
+    public DefaultSimplePlanDescriptor(final String planId,
+                                       final String productName,
+                                       final ProductCategory productCategory,
+                                       final Currency currency,
+                                       final BigDecimal amount,
+                                       final BillingPeriod billingPeriod,
+                                       final Integer trialLength,
+                                       final TimeUnit trialTimeUnit,
+                                       final List<String> availableBaseProducts) {
+        this.planId = planId;
+        this.productName = productName;
+        this.productCategory = productCategory;
+        this.currency = currency;
+        this.amount = amount;
+        this.billingPeriod = billingPeriod;
+        this.trialLength = trialLength;
+        this.trialTimeUnit = trialTimeUnit;
+        this.availableBaseProducts = availableBaseProducts;
+    }
+
+    @Override
+    public String getPlanId() {
+        return planId;
+    }
+
+    @Override
+    public String getProductName() {
+        return productName;
+    }
+
+    @Override
+    public ProductCategory getProductCategory() {
+        return productCategory;
+    }
+
+    @Override
+    public List<String> getAvailableBaseProducts() {
+        return availableBaseProducts;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    @Override
+    public Integer getTrialLength() {
+        return trialLength;
+    }
+
+    @Override
+    public TimeUnit getTrialTimeUnit() {
+        return trialTimeUnit;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultSimplePlanDescriptor{" +
+               "planId='" + planId + '\'' +
+               ", productName='" + productName + '\'' +
+               ", productCategory=" + productCategory +
+               ", currency=" + currency +
+               ", amount=" + amount +
+               ", billingPeriod=" + billingPeriod +
+               ", trialLength=" + trialLength +
+               ", trialTimeUnit=" + trialTimeUnit +
+               ", availableBaseProducts=" + availableBaseProducts +
+               '}';
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
index 38984c0..9dc2ed8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCache.java
@@ -25,7 +25,7 @@ public interface CatalogCache {
 
     public void loadDefaultCatalog(final String url) throws CatalogApiException;
 
-    public VersionedCatalog getCatalog(InternalTenantContext tenantContext) throws CatalogApiException;
+    public VersionedCatalog getCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, InternalTenantContext tenantContext) throws CatalogApiException;
 
     public void clearCatalog(InternalTenantContext tenantContext);
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index 5713049..ee21422 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -24,9 +24,12 @@ import javax.inject.Inject;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.catalog.plugin.VersionedCatalogMapper;
 import org.killbill.billing.catalog.plugin.api.CatalogPluginApi;
 import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
@@ -51,9 +54,11 @@ public class EhCacheCatalogCache implements CatalogCache {
 
     private final CacheController cacheController;
     private final VersionedCatalogLoader loader;
+    private final CacheLoaderArgument cacheLoaderArgumentWithTemplateFiltering;
     private final CacheLoaderArgument cacheLoaderArgument;
     private final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
     private final VersionedCatalogMapper versionedCatalogMapper;
+    private final PriceOverride priceOverride;
     private final InternalCallContextFactory internalCallContextFactory;
 
     private VersionedCatalog defaultCatalog;
@@ -63,13 +68,16 @@ public class EhCacheCatalogCache implements CatalogCache {
                                final VersionedCatalogMapper versionedCatalogMapper,
                                final CacheControllerDispatcher cacheControllerDispatcher,
                                final VersionedCatalogLoader loader,
+                               final PriceOverride priceOverride,
                                final InternalCallContextFactory internalCallContextFactory) {
         this.pluginRegistry = pluginRegistry;
         this.versionedCatalogMapper = versionedCatalogMapper;
         this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CATALOG);
         this.loader = loader;
+        this.priceOverride = priceOverride;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.cacheLoaderArgument = initializeCacheLoaderArgument(this);
+        this.cacheLoaderArgumentWithTemplateFiltering = initializeCacheLoaderArgument(true);
+        this.cacheLoaderArgument = initializeCacheLoaderArgument(false);
         setDefaultCatalog();
     }
 
@@ -81,7 +89,7 @@ public class EhCacheCatalogCache implements CatalogCache {
     }
 
     @Override
-    public VersionedCatalog getCatalog(final InternalTenantContext tenantContext) throws CatalogApiException {
+    public VersionedCatalog getCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, final InternalTenantContext tenantContext) throws CatalogApiException {
 
         // STEPH TODO what are the possibilities for caching here ?
         final VersionedCatalog pluginVersionedCatalog = getCatalogFromPlugins(tenantContext);
@@ -90,16 +98,21 @@ public class EhCacheCatalogCache implements CatalogCache {
         }
 
         if (tenantContext.getTenantRecordId() == InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID) {
-            return defaultCatalog;
+            return useDefaultCatalog ? defaultCatalog : null;
         }
         // The cache loader might choke on some bad xml -- unlikely since we check its validity prior storing it,
         // but to be on the safe side;;
         try {
-            VersionedCatalog tenantCatalog = (VersionedCatalog) cacheController.get(tenantContext.getTenantRecordId(), cacheLoaderArgument);
+            VersionedCatalog tenantCatalog = (VersionedCatalog) cacheController.get(tenantContext.getTenantRecordId(),
+                                                                                    filterTemplateCatalog ? cacheLoaderArgumentWithTemplateFiltering : cacheLoaderArgument);
             // It means we are using a default catalog in a multi-tenant deployment, that does not really match a real use case, but we want to support it
             // for test purpose.
-            if (tenantCatalog == null) {
-                tenantCatalog = new VersionedCatalog(defaultCatalog.getClock(), defaultCatalog.getCatalogName(), defaultCatalog.getRecurringBillingMode(), defaultCatalog.getVersions(), tenantContext);
+            if (useDefaultCatalog && tenantCatalog == null) {
+                tenantCatalog = new VersionedCatalog(defaultCatalog.getClock());
+                for (final StandaloneCatalog cur : defaultCatalog.getVersions()) {
+                    final StandaloneCatalogWithPriceOverride curWithOverride = new StandaloneCatalogWithPriceOverride(cur, priceOverride, tenantContext.getTenantRecordId(), internalCallContextFactory);
+                    tenantCatalog.add(curWithOverride);
+                }
                 cacheController.add(tenantContext.getTenantRecordId(), tenantCatalog);
             }
             return tenantCatalog;
@@ -115,7 +128,7 @@ public class EhCacheCatalogCache implements CatalogCache {
         }
     }
 
-    private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) {
+    private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) throws CatalogApiException {
         final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
         for (final String service : pluginRegistry.getAllServices()) {
             final CatalogPluginApi plugin = pluginRegistry.getServiceForName(service);
@@ -134,11 +147,11 @@ public class EhCacheCatalogCache implements CatalogCache {
     // nothing about catalog.
     //
     // This is a contract between the TenantCatalogCacheLoader and the EhCacheCatalogCache
-    private CacheLoaderArgument initializeCacheLoaderArgument(final EhCacheCatalogCache parentCache) {
+    private CacheLoaderArgument initializeCacheLoaderArgument(final boolean filterTemplateCatalog) {
         final LoaderCallback loaderCallback = new LoaderCallback() {
             @Override
             public Object loadCatalog(final List<String> catalogXMLs, final Long tenantRecordId) throws CatalogApiException {
-                return loader.load(catalogXMLs, tenantRecordId);
+                return loader.load(catalogXMLs, filterTemplateCatalog, tenantRecordId);
             }
         };
         final Object[] args = new Object[1];
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
index 5779460..1c1f404 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
@@ -86,6 +86,11 @@ public class EhCacheOverriddenPlanCache implements OverriddenPlanCache {
         return (DefaultPlan) cacheController.get(planName, argument);
     }
 
+    @Override
+    public void addDryRunPlan(final String planName, final Plan plan) {
+        cacheController.putIfAbsent(planName, plan);
+    }
+
     private DefaultPlan loadOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException {
 
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
index f0f8178..f888dc8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
@@ -20,9 +20,12 @@ package org.killbill.billing.catalog.caching;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPlan;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.StaticCatalog;
 
 public interface OverriddenPlanCache {
 
     DefaultPlan getOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException;
+
+    void addDryRunPlan(final String planName, final Plan plan);
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogEntityCollection.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogEntityCollection.java
new file mode 100644
index 0000000..d2d2cbd
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogEntityCollection.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.killbill.billing.catalog.api.CatalogEntity;
+
+import com.google.common.collect.Ordering;
+
+public class CatalogEntityCollection<T extends CatalogEntity> implements Collection<T> {
+
+    private final Map<String, T> data;
+
+    public CatalogEntityCollection() {
+        this.data = new TreeMap<String, T>(Ordering.<String>natural());
+    }
+
+    public CatalogEntityCollection(final T[] entities) {
+        this.data = new TreeMap<String, T>(Ordering.<String>natural());
+        for (final T cur : entities) {
+            addEntry(cur);
+        }
+    }
+
+
+    public CatalogEntityCollection(final Iterable<T> entities) {
+        this.data = new TreeMap<String, T>(Ordering.<String>natural());
+        for (final T cur : entities) {
+            addEntry(cur);
+        }
+    }
+
+    //
+    // Returning such entries will be log(N)
+    //
+    public T findByName(final String entryName) {
+        return data.get(entryName);
+    }
+
+    public Collection<T> getEntries() {
+        return data.values();
+    }
+
+    @Override
+    public int size() {
+        return data.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return data.isEmpty();
+    }
+
+    @Override
+    public boolean contains(final Object o) {
+        return data.containsKey(((CatalogEntity) o).getName());
+    }
+
+    @Override
+    public Iterator iterator() {
+
+        // Build an iterator that will return ordered using natural ordering with regard to CatalogEntity#name
+        final Iterator<String> keyIterator = data.keySet().iterator();
+        final Iterator it = new Iterator() {
+            private String prevKey = null;
+            @Override
+            public boolean hasNext() {
+                return keyIterator.hasNext();
+            }
+            @Override
+            public Object next() {
+                prevKey = keyIterator.next();
+                return data.get(prevKey);
+            }
+            @Override
+            public void remove() {
+                if (prevKey != null) {
+                    keyIterator.remove();
+                    data.remove(prevKey);
+                }
+            }
+        };
+        return it;
+    }
+
+    @Override
+    public boolean add(final T t) {
+        addEntry(t);
+        return true;
+    }
+
+    @Override
+    public boolean remove(final Object o) {
+        return removeEntry((T) o);
+    }
+
+    @Override
+    public boolean addAll(final Collection c) {
+        final Iterator iterator = c.iterator();
+        while (iterator.hasNext()) {
+            final T cur = (T) iterator.next();
+            addEntry(cur);
+        }
+        return true;
+    }
+
+    @Override
+    public void clear() {
+        data.clear();
+    }
+
+    @Override
+    public boolean retainAll(final Collection c) {
+        throw new IllegalStateException("Not implemented");
+    }
+
+    @Override
+    public boolean removeAll(final Collection c) {
+        final Iterator iterator = c.iterator();
+        while (iterator.hasNext()) {
+            final CatalogEntity cur = (CatalogEntity) iterator.next();
+            data.remove(cur.getName());
+        }
+        return true;
+    }
+
+    @Override
+    public boolean containsAll(final Collection c) {
+        final Iterator iterator = c.iterator();
+        while (iterator.hasNext()) {
+            final CatalogEntity cur = (CatalogEntity) iterator.next();
+            if (!data.containsKey(cur.getName())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public Object[] toArray(final Object[] a) {
+        return data.values().toArray(a);
+    }
+
+    @Override
+    public Object[] toArray() {
+        return data.values().toArray(new Object[data.size()]);
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof CatalogEntityCollection)) {
+            return false;
+        }
+
+        final CatalogEntityCollection<?> that = (CatalogEntityCollection<?>) o;
+        return data != null ? data.equals(that.data) : that.data == null;
+    }
+
+    @Override
+    public int hashCode() {
+        return data != null ? data.hashCode() : 0;
+    }
+
+    private void addEntry(final T entry) {
+        data.put(entry.getName(), entry);
+    }
+
+    private boolean removeEntry(final T entry) {
+        return data.remove(entry.getName()) != null;
+    }
+
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
new file mode 100644
index 0000000..f8b61b6
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogUpdater.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingAlignment;
+import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.CatalogApiException;
+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.PlanAlignmentChange;
+import org.killbill.billing.catalog.api.PlanAlignmentCreate;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.rules.DefaultCaseBillingAlignment;
+import org.killbill.billing.catalog.rules.DefaultCaseCancelPolicy;
+import org.killbill.billing.catalog.rules.DefaultCaseChangePlanAlignment;
+import org.killbill.billing.catalog.rules.DefaultCaseChangePlanPolicy;
+import org.killbill.billing.catalog.rules.DefaultCaseCreateAlignment;
+import org.killbill.billing.catalog.rules.DefaultCasePriceList;
+import org.killbill.billing.catalog.rules.DefaultPlanRules;
+import org.killbill.xmlloader.XMLWriter;
+
+import com.google.common.collect.ImmutableList;
+
+public class CatalogUpdater {
+
+    private static URI DUMMY_URI;
+
+    static {
+        try {
+            DUMMY_URI = new URI("dummy");
+        } catch (URISyntaxException e) {
+        }
+    }
+
+    ;
+
+    private final DefaultMutableStaticCatalog catalog;
+
+    public CatalogUpdater(final StandaloneCatalog standaloneCatalog) {
+        this.catalog = new DefaultMutableStaticCatalog(standaloneCatalog);
+    }
+
+    public CatalogUpdater(final String catalogName, final BillingMode billingMode, final DateTime effectiveDate, final Currency... currencies) {
+
+        final DefaultPriceList defaultPriceList = new DefaultPriceList().setName(PriceListSet.DEFAULT_PRICELIST_NAME);
+        final StandaloneCatalog tmp = new StandaloneCatalog()
+                .setCatalogName(catalogName)
+                .setEffectiveDate(effectiveDate.toDate())
+                .setRecurringBillingMode(billingMode)
+                .setProducts(ImmutableList.<Product>of())
+                .setPlans(ImmutableList.<Plan>of())
+                .setPriceLists(new DefaultPriceListSet(defaultPriceList, new DefaultPriceList[0]))
+                .setPlanRules(getSaneDefaultPlanRules(defaultPriceList));
+        if (currencies != null && currencies.length > 0) {
+            tmp.setSupportedCurrencies(currencies);
+        }
+        tmp.initialize(tmp, DUMMY_URI);
+
+        this.catalog = new DefaultMutableStaticCatalog(tmp);
+    }
+
+    public StandaloneCatalog getCatalog() {
+        return catalog;
+    }
+
+    public String getCatalogXML() {
+        try {
+            return XMLWriter.writeXML(catalog, StandaloneCatalog.class);
+        } catch (final Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void addSimplePlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
+
+        // We need at least a planId
+        if (desc == null || desc.getPlanId() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
+
+        DefaultPlan plan = (DefaultPlan) getExistingPlan(desc.getPlanId());
+        if (plan == null && desc.getProductName() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
+
+        DefaultProduct product = plan != null ? (DefaultProduct) plan.getProduct() : (DefaultProduct)  getExistingProduct(desc.getProductName());
+        if (product == null) {
+            product = new DefaultProduct();
+            product.setName(desc.getProductName());
+            product.setCatagory(desc.getProductCategory());
+            product.initialize(catalog, DUMMY_URI);
+            catalog.addProduct(product);
+        }
+
+        if (plan == null) {
+
+            validateNewPlanDescriptor(desc);
+
+            plan = new DefaultPlan();
+            plan.setName(desc.getPlanId());
+            plan.setPriceListName(PriceListSet.DEFAULT_PRICELIST_NAME);
+            plan.setProduct(product);
+
+            if (desc.getTrialLength() > 0 && desc.getTrialTimeUnit() != TimeUnit.UNLIMITED) {
+                final DefaultPlanPhase trialPhase = new DefaultPlanPhase();
+                trialPhase.setPhaseType(PhaseType.TRIAL);
+                trialPhase.setDuration(new DefaultDuration().setUnit(desc.getTrialTimeUnit()).setNumber(desc.getTrialLength()));
+                trialPhase.setFixed(new DefaultFixed().setFixedPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(desc.getCurrency()).setValue(BigDecimal.ZERO)})));
+                plan.setInitialPhases(new DefaultPlanPhase[]{trialPhase});
+            }
+            catalog.addPlan(plan);
+        } else {
+            validateExistingPlan(plan, desc);
+        }
+
+        //
+        // At this point we have an old or newly created **simple** Plan and we need to either create the recurring section or add a new currency.
+        //
+        if (!isCurrencySupported(desc.getCurrency())) {
+            catalog.addCurrency(desc.getCurrency());
+            // Reset the fixed price to null so the isZero() logic goes through new currencies and set the zero price for all
+            if (plan.getInitialPhases().length == 1) {
+                ((DefaultInternationalPrice) plan.getInitialPhases()[0].getFixed().getPrice()).setPrices(null);
+            }
+        }
+
+        DefaultPlanPhase evergreenPhase = plan.getFinalPhase();
+        if (evergreenPhase == null) {
+            evergreenPhase = new DefaultPlanPhase();
+            evergreenPhase.setPhaseType(PhaseType.EVERGREEN);
+            evergreenPhase.setDuration(new DefaultDuration()
+                                               .setUnit(TimeUnit.UNLIMITED));
+            plan.setFinalPhase(evergreenPhase);
+        }
+
+        DefaultRecurring recurring = (DefaultRecurring) evergreenPhase.getRecurring();
+        if (recurring == null) {
+            recurring = new DefaultRecurring();
+            recurring.setBillingPeriod(desc.getBillingPeriod());
+            recurring.setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[0]));
+            evergreenPhase.setRecurring(recurring);
+        }
+
+        try {
+            recurring.getRecurringPrice().getPrice(desc.getCurrency());
+        } catch (CatalogApiException ignore) {
+            catalog.addRecurringPriceToPlan(recurring.getRecurringPrice(), new DefaultPrice().setCurrency(desc.getCurrency()).setValue(desc.getAmount()));
+        }
+
+        if (desc.getProductCategory() == ProductCategory.ADD_ON) {
+            for (final String bp : desc.getAvailableBaseProducts()) {
+                final Product targetBasePlan = getExistingProduct(bp);
+                boolean found = false;
+                for (Product cur : targetBasePlan.getAvailable()) {
+                    if (cur.getName().equals(product.getName())) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    catalog.addProductAvailableAO(getExistingProduct(bp), product);
+                }
+            }
+        }
+
+        // Reinit catalog
+        catalog.initialize(catalog, DUMMY_URI);
+    }
+
+    private void validateExistingPlan(final DefaultPlan plan, final SimplePlanDescriptor desc) throws CatalogApiException {
+
+        boolean failedValidation = false;
+
+        //
+        // TRIAL VALIDATION
+        //
+        // We only support adding new Plan with NO TRIAL or $0 TRIAL. Existing Plan not matching such criteria are incompatible
+        if (plan.getInitialPhases().length > 1 ||
+            (plan.getInitialPhases().length == 1 &&
+             (plan.getInitialPhases()[0].getPhaseType() != PhaseType.TRIAL || !plan.getInitialPhases()[0].getFixed().getPrice().isZero()))) {
+            failedValidation = true;
+
+        } else if (desc.getTrialLength() != null && desc.getTrialTimeUnit() != null) { // If desc includes trial info we verify this is valid
+            final boolean isDescConfiguredWithTrial = desc.getTrialLength() > 0 && desc.getTrialTimeUnit() != TimeUnit.UNLIMITED;
+            final boolean isPlanConfiguredWithTrial = plan.getInitialPhases().length == 1;
+            // Current plan has trial and desc does not or reverse
+            if ((isDescConfiguredWithTrial && !isPlanConfiguredWithTrial) ||
+                (!isDescConfiguredWithTrial && isPlanConfiguredWithTrial)) {
+                failedValidation = true;
+                // Both have trials , check they match
+            } else if (isDescConfiguredWithTrial && isPlanConfiguredWithTrial) {
+                if (plan.getInitialPhases()[0].getDuration().getUnit() != desc.getTrialTimeUnit() ||
+                    plan.getInitialPhases()[0].getDuration().getNumber() != desc.getTrialLength()) {
+                    failedValidation = true;
+                }
+            }
+        }
+
+        //
+        // RECURRING VALIDATION
+        //
+        if (!failedValidation) {
+            // Desc only supports EVERGREEN Phase
+            if (plan.getFinalPhase().getPhaseType() != PhaseType.EVERGREEN) {
+                failedValidation = true;
+            } else {
+
+                // Should be same recurring BillingPeriod
+                if (desc.getBillingPeriod() != null && plan.getFinalPhase().getRecurring().getBillingPeriod() != desc.getBillingPeriod()) {
+                    failedValidation = true;
+                } else if (desc.getCurrency() != null && desc.getAmount() != null) {
+                    try {
+                        final BigDecimal currentAmount = plan.getFinalPhase().getRecurring().getRecurringPrice().getPrice(desc.getCurrency());
+                        if (currentAmount.compareTo(desc.getAmount()) != 0) {
+                            failedValidation = true;
+                        }
+                    } catch (CatalogApiException ignoreIfCurrencyIsCurrentlyUndefined) {
+                    }
+                }
+            }
+        }
+
+        if (failedValidation) {
+            throw new CatalogApiException(ErrorCode.CAT_FAILED_SIMPLE_PLAN_VALIDATION, plan.toString(), desc.toString());
+        }
+    }
+
+    private boolean isCurrencySupported(final Currency targetCurrency) {
+        if (catalog.getCurrentSupportedCurrencies() != null) {
+            for (final Currency input : catalog.getCurrentSupportedCurrencies()) {
+                if (input.equals(targetCurrency)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private void validateNewPlanDescriptor(final SimplePlanDescriptor desc) throws CatalogApiException {
+        if (desc.getProductCategory() == null ||
+            desc.getBillingPeriod() == null ||
+            (desc.getAmount() == null || desc.getAmount().compareTo(BigDecimal.ZERO) <= 0) ||
+            desc.getCurrency() == null) {
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+        }
+
+        if (desc.getProductCategory() == ProductCategory.ADD_ON) {
+            if (desc.getAvailableBaseProducts() == null || desc.getAvailableBaseProducts().isEmpty()) {
+                throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+            }
+            for (final String cur : desc.getAvailableBaseProducts()) {
+                if (getExistingProduct(cur) == null) {
+                    throw new CatalogApiException(ErrorCode.CAT_INVALID_SIMPLE_PLAN_DESCRIPTOR, desc);
+                }
+            }
+        }
+    }
+
+    private Product getExistingProduct(final String productName) {
+        try {
+            return catalog.findCurrentProduct(productName);
+        } catch (final CatalogApiException e) {
+            return null;
+        }
+    }
+
+    private Plan getExistingPlan(final String planName) {
+        try {
+            return catalog.findCurrentPlan(planName);
+        } catch (CatalogApiException e) {
+            return null;
+        }
+    }
+
+    private DefaultPlanRules getSaneDefaultPlanRules(final DefaultPriceList defaultPriceList) {
+
+        final DefaultCaseChangePlanPolicy[] changePolicy = new DefaultCaseChangePlanPolicy[1];
+        changePolicy[0] = new DefaultCaseChangePlanPolicy();
+        changePolicy[0].setPolicy(BillingActionPolicy.IMMEDIATE);
+
+        final DefaultCaseChangePlanAlignment[] changeAlignment = new DefaultCaseChangePlanAlignment[1];
+        changeAlignment[0] = new DefaultCaseChangePlanAlignment();
+        changeAlignment[0].setAlignment(PlanAlignmentChange.START_OF_BUNDLE);
+
+        final DefaultCaseCancelPolicy[] cancelPolicy = new DefaultCaseCancelPolicy[1];
+        cancelPolicy[0] = new DefaultCaseCancelPolicy();
+        cancelPolicy[0].setPolicy(BillingActionPolicy.IMMEDIATE);
+
+        final DefaultCaseCreateAlignment[] createAlignment = new DefaultCaseCreateAlignment[1];
+        createAlignment[0] = new DefaultCaseCreateAlignment();
+        createAlignment[0].setAlignment(PlanAlignmentCreate.START_OF_BUNDLE);
+
+        final DefaultCaseBillingAlignment[] billingAlignmentCase = new DefaultCaseBillingAlignment[1];
+        billingAlignmentCase[0] = new DefaultCaseBillingAlignment();
+        billingAlignmentCase[0].setAlignment(BillingAlignment.ACCOUNT);
+
+        final DefaultCasePriceList[] priceList = new DefaultCasePriceList[1];
+        priceList[0] = new DefaultCasePriceList();
+        priceList[0].setToPriceList(defaultPriceList);
+
+        return new DefaultPlanRules()
+                .setChangeCase(changePolicy)
+                .setChangeAlignmentCase(changeAlignment)
+                .setCancelCase(cancelPolicy)
+                .setCreateAlignmentCase(createAlignment)
+                .setBillingAlignmentCase(billingAlignmentCase)
+                .setPriceListCase(priceList);
+    }
+
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
index cf17853..981b0e4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
@@ -26,7 +26,6 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.caching.CatalogCache;
-import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.glue.CatalogModule;
 import org.killbill.billing.platform.api.KillbillService;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
@@ -34,7 +33,7 @@ import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
-import org.killbill.billing.util.config.CatalogConfig;
+import org.killbill.billing.util.config.definition.CatalogConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -86,22 +85,22 @@ public class DefaultCatalogService implements KillbillService, CatalogService {
         tenantInternalApi.initializeCacheInvalidationCallback(TenantKey.CATALOG, cacheInvalidationCallback);
     }
 
-        @Override
+    @Override
     public String getName() {
         return CATALOG_SERVICE_NAME;
     }
 
     @Override
-    public Catalog getFullCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return getCatalog(context);
+    public Catalog getFullCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return getCatalog(useDefaultCatalog, filterTemplateCatalog, context);
     }
 
     @Override
-    public StaticCatalog getCurrentCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return getCatalog(context);
+    public StaticCatalog getCurrentCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return getCatalog(useDefaultCatalog, filterTemplateCatalog, context);
     }
 
-    private VersionedCatalog getCatalog(final InternalTenantContext context) throws CatalogApiException {
-        return catalogCache.getCatalog(context);
+    private VersionedCatalog getCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, final InternalTenantContext context) throws CatalogApiException {
+        return catalogCache.getCatalog(useDefaultCatalog, filterTemplateCatalog, context);
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
index 4c3ed6c..cc1e343 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
@@ -21,8 +21,11 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.joda.time.Period;
 
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Duration;
 import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -60,7 +63,7 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
     }
 
     @Override
-    public DateTime addToDateTime(final DateTime dateTime) {
+    public DateTime addToDateTime(final DateTime dateTime) throws CatalogApiException {
         if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
             return dateTime;
         }
@@ -73,9 +76,27 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
             case YEARS:
                 return dateTime.plusYears(number);
             case UNLIMITED:
-                return dateTime.plusYears(100);
             default:
-                return dateTime;
+                throw new CatalogApiException(ErrorCode.CAT_UNDEFINED_DURATION, unit);
+        }
+    }
+
+    @Override
+    public LocalDate addToLocalDate(final LocalDate localDate) throws CatalogApiException {
+        if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
+            return localDate;
+        }
+
+        switch (unit) {
+            case DAYS:
+                return localDate.plusDays(number);
+            case MONTHS:
+                return localDate.plusMonths(number);
+            case YEARS:
+                return localDate.plusYears(number);
+            case UNLIMITED:
+            default:
+                throw new CatalogApiException(ErrorCode.CAT_UNDEFINED_DURATION, unit);
         }
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
new file mode 100644
index 0000000..8e83016
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultMutableStaticCatalog.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogEntity;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.Price;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+
+public class DefaultMutableStaticCatalog extends StandaloneCatalog implements MutableStaticCatalog {
+
+    public DefaultMutableStaticCatalog() {
+    }
+
+    public DefaultMutableStaticCatalog(final Date effectiveDate) {
+        super(effectiveDate);
+    }
+
+    public DefaultMutableStaticCatalog(final StandaloneCatalog input) {
+        this.setCatalogName(input.getCatalogName())
+            .setEffectiveDate(input.getEffectiveDate())
+            .setSupportedCurrencies(input.getCurrentSupportedCurrencies())
+            .setUnits(input.getCurrentUnits())
+            .setProducts(input.getCurrentProducts())
+            .setPlans(input.getCurrentPlans())
+            .setRecurringBillingMode(input.getRecurringBillingMode())
+            .setPlanRules(input.getPlanRules())
+            .setPriceLists(input.getPriceLists());
+        initialize(this, null);
+    }
+
+    @Override
+    public void addCurrency(final Currency currency) throws CatalogApiException {
+        final Currency[] newEntries = allocateNewEntries(getCurrentSupportedCurrencies(), currency);
+        setSupportedCurrencies(newEntries);
+    }
+
+    @Override
+    public void addProduct(final Product product) throws CatalogApiException {
+        getCatalogEntityCollectionProduct().add(product);
+    }
+
+    @Override
+    public void addPlan(final Plan plan) throws CatalogApiException {
+
+        getCatalogEntityCollectionPlan().add(plan);
+
+        final DefaultPriceList priceList = getPriceLists().findPriceListFrom(plan.getPriceListName());
+        priceList.getCatalogEntityCollectionPlan().add(plan);
+    }
+
+    @Override
+    public void addPriceList(final PriceList priceList) throws CatalogApiException {
+        final PriceList[] newEntries = allocateNewEntries(getPriceLists().getChildPriceLists(), priceList);
+        final DefaultPriceListSet priceListSet = new DefaultPriceListSet((PriceListDefault) getPriceLists().getDefaultPricelist(), (DefaultPriceList[]) newEntries);
+        setPriceLists(priceListSet);
+    }
+
+    public void addRecurringPriceToPlan(final DefaultInternationalPrice currentPrices, final Price newPrice) throws CatalogApiException {
+        final Price[] newEntries = allocateNewEntries(currentPrices.getPrices(), newPrice);
+        currentPrices.setPrices((DefaultPrice[]) newEntries);
+    }
+
+    public void addProductAvailableAO(final Product targetBasePlan, final DefaultProduct aoProduct) throws CatalogApiException {
+        ((DefaultProduct) targetBasePlan).getCatalogEntityCollectionAvailable().add(aoProduct);
+    }
+
+    private <T> T[] allocateNewEntries(final T[] existingEntries, final T newEntry) throws CatalogApiException {
+
+        if (existingEntries != null) {
+            for (T input : existingEntries) {
+                boolean found;
+                if (input instanceof CatalogEntity) {
+                    found = ((CatalogEntity) input).getName().equals(((CatalogEntity) newEntry).getName());
+                } else if (input instanceof Enum) {
+                    found = ((Enum) input).name().equals(((Enum) newEntry).name());
+                } else if (input instanceof Price) {
+                    found = ((Price) input).getCurrency().equals(((Price) newEntry).getCurrency());
+                } else {
+                    throw new IllegalStateException("Unexpected type " + newEntry.getClass());
+                }
+                if (found) {
+                    //throw new CatalogApiException();
+                    throw new IllegalStateException("Already existing " + newEntry);
+                }
+            }
+        }
+
+        // Realloc and assign new entry
+        final int length = existingEntries != null ? existingEntries.length : 0;
+        final T[] newEntries = (T[]) Array.newInstance(newEntry.getClass(), length + 1);
+        for (int i = 0; i < newEntries.length + 1; i++) {
+            if (i < newEntries.length - 1) {
+                newEntries[i] = existingEntries[i];
+            } else {
+                newEntries[newEntries.length - 1] = newEntry;
+            }
+        }
+        return newEntries;
+    }
+
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index d0945db..84fe628 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -42,6 +42,7 @@ 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.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -55,16 +56,16 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @XmlID
     private String name;
 
-    //TODO MDW Validation - effectiveDateForExistingSubscriptons > catalog effectiveDate
+    //TODO MDW Validation - effectiveDateForExistingSubscriptions > catalog effectiveDate
     @XmlElement(required = false)
-    private Date effectiveDateForExistingSubscriptons;
+    private Date effectiveDateForExistingSubscriptions;
 
     @XmlElement(required = true)
     @XmlIDREF
     private DefaultProduct product;
 
     @XmlElementWrapper(name = "initialPhases", required = false)
-    @XmlElement(name = "phase", required = true)
+    @XmlElement(name = "phase", required = false)
     private DefaultPlanPhase[] initialPhases;
 
     @XmlElement(name = "finalPhase", required = true)
@@ -75,7 +76,9 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     //No other value is allowed for Tiered ADDONS
     //A value of -1 means unlimited
     @XmlElement(required = false)
-    private Integer plansAllowedInBundle = 1;
+    private Integer plansAllowedInBundle = -1;
+
+    private String priceListName;
 
     public DefaultPlan() {
         initialPhases = new DefaultPlanPhase[0];
@@ -83,7 +86,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
     public DefaultPlan(final String planName, final DefaultPlan in, final PlanPhasePriceOverride[] overrides) {
         this.name = planName;
-        this.effectiveDateForExistingSubscriptons = in.getEffectiveDateForExistingSubscriptons();
+        this.effectiveDateForExistingSubscriptions = in.getEffectiveDateForExistingSubscriptions();
         this.product = (DefaultProduct) in.getProduct();
         this.initialPhases = new DefaultPlanPhase[in.getInitialPhases().length];
         for (int i = 0; i < overrides.length - 1; i++) {
@@ -91,17 +94,12 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
             initialPhases[i] = newPhase;
         }
         this.finalPhase = new DefaultPlanPhase(this, in.getFinalPhase(), overrides[overrides.length - 1]);
-
+        this.priceListName = in.getPriceListName();
     }
 
     @Override
-    public Date getEffectiveDateForExistingSubscriptons() {
-        return effectiveDateForExistingSubscriptons;
-    }
-
-    public void setEffectiveDateForExistingSubscriptons(
-            final Date effectiveDateForExistingSubscriptons) {
-        this.effectiveDateForExistingSubscriptons = effectiveDateForExistingSubscriptons;
+    public Date getEffectiveDateForExistingSubscriptions() {
+        return effectiveDateForExistingSubscriptions;
     }
 
     @Override
@@ -109,19 +107,14 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return initialPhases;
     }
 
-    public DefaultPlan setInitialPhases(final DefaultPlanPhase[] phases) {
-        this.initialPhases = phases;
-        return this;
-    }
-
     @Override
     public Product getProduct() {
         return product;
     }
 
-    public DefaultPlan setProduct(final DefaultProduct product) {
-        this.product = product;
-        return this;
+    @Override
+    public String getPriceListName() {
+        return priceListName;
     }
 
     @Override
@@ -129,21 +122,11 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return name;
     }
 
-    public DefaultPlan setName(final String name) {
-        this.name = name;
-        return this;
-    }
-
     @Override
     public DefaultPlanPhase getFinalPhase() {
         return finalPhase;
     }
 
-    public DefaultPlan setFinalPhase(final DefaultPlanPhase finalPhase) {
-        this.finalPhase = finalPhase;
-        return this;
-    }
-
     @Override
     public PlanPhase[] getAllPhases() {
         final int length = (initialPhases == null || initialPhases.length == 0) ? 1 : (initialPhases.length + 1);
@@ -179,11 +162,6 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return plansAllowedInBundle;
     }
 
-    public DefaultPlan setPlansAllowedInBundle(final Integer plansAllowedInBundle) {
-        this.plansAllowedInBundle = plansAllowedInBundle;
-        return this;
-    }
-
     /* (non-Javadoc)
       * @see org.killbill.billing.catalog.IPlan#getPhaseIterator()
       */
@@ -207,14 +185,15 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
                 p.initialize(catalog, sourceURI);
             }
         }
+        this.priceListName = this.priceListName  != null ? this.priceListName : findPriceListForPlan(catalog);
     }
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        if (effectiveDateForExistingSubscriptons != null &&
-            catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
+        if (effectiveDateForExistingSubscriptions != null &&
+            catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptions.getTime()) {
             errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
-                                                         effectiveDateForExistingSubscriptons,
+                                                         effectiveDateForExistingSubscriptions,
                                                          catalog.getEffectiveDate().getTime()),
                                            catalog.getCatalogURI(), DefaultInternationalPrice.class, ""));
         }
@@ -224,6 +203,41 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
         return errors;
     }
 
+    public void setEffectiveDateForExistingSubscriptions(
+            final Date effectiveDateForExistingSubscriptions) {
+        this.effectiveDateForExistingSubscriptions = effectiveDateForExistingSubscriptions;
+    }
+
+    public DefaultPlan setName(final String name) {
+        this.name = name;
+        return this;
+    }
+
+    public DefaultPlan setFinalPhase(final DefaultPlanPhase finalPhase) {
+        this.finalPhase = finalPhase;
+        return this;
+    }
+
+    public DefaultPlan setProduct(final Product product) {
+        this.product = (DefaultProduct) product;
+        return this;
+    }
+
+    public DefaultPlan setPriceListName(final String priceListName) {
+        this.priceListName = priceListName;
+        return this;
+    }
+
+    public DefaultPlan setInitialPhases(final DefaultPlanPhase[] phases) {
+        this.initialPhases = phases;
+        return this;
+    }
+
+    public DefaultPlan setPlansAllowedInBundle(final Integer plansAllowedInBundle) {
+        this.plansAllowedInBundle = plansAllowedInBundle;
+        return this;
+    }
+
     @Override
     public DateTime dateOfFirstRecurringNonZeroCharge(final DateTime subscriptionStartDate, final PhaseType initialPhaseType) {
         DateTime result = subscriptionStartDate;
@@ -238,7 +252,10 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
             }
             final Recurring recurring = phase.getRecurring();
             if (recurring == null || recurring.getRecurringPrice() == null || recurring.getRecurringPrice().isZero()) {
-                result = phase.getDuration().addToDateTime(result);
+                try {
+                    result = phase.getDuration().addToDateTime(result);
+                } catch (final CatalogApiException ignored) {
+                }
             } else {
                 break;
             }
@@ -257,7 +274,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
         final DefaultPlan that = (DefaultPlan) o;
 
-        if (effectiveDateForExistingSubscriptons != null ? !effectiveDateForExistingSubscriptons.equals(that.effectiveDateForExistingSubscriptons) : that.effectiveDateForExistingSubscriptons != null) {
+        if (effectiveDateForExistingSubscriptions != null ? !effectiveDateForExistingSubscriptions.equals(that.effectiveDateForExistingSubscriptions) : that.effectiveDateForExistingSubscriptions != null) {
             return false;
         }
         if (finalPhase != null ? !finalPhase.equals(that.finalPhase) : that.finalPhase != null) {
@@ -281,7 +298,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (effectiveDateForExistingSubscriptons != null ? effectiveDateForExistingSubscriptons.hashCode() : 0);
+        result = 31 * result + (effectiveDateForExistingSubscriptions != null ? effectiveDateForExistingSubscriptions.hashCode() : 0);
         result = 31 * result + (initialPhases != null ? Arrays.hashCode(initialPhases) : 0);
         result = 31 * result + (finalPhase != null ? finalPhase.hashCode() : 0);
         result = 31 * result + (plansAllowedInBundle != null ? plansAllowedInBundle.hashCode() : 0);
@@ -290,9 +307,19 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
     @Override
     public String toString() {
-        return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptons="
-               + effectiveDateForExistingSubscriptons + ", product=" + product + ", initialPhases="
-               + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase + ", plansAllowedInBundle="
+        return "DefaultPlan [name=" + name + ", effectiveDateForExistingSubscriptions="
+               + effectiveDateForExistingSubscriptions + ", product=" + product.getName() + ", initialPhases="
+               + Arrays.toString(initialPhases) + ", finalPhase=" + finalPhase.getName() + ", plansAllowedInBundle="
                + plansAllowedInBundle + "]";
     }
+
+    private String findPriceListForPlan(final StandaloneCatalog catalog) {
+        for (final PriceList cur : catalog.getPriceLists().getAllPriceLists()) {
+            final DefaultPriceList curDefaultPriceList = (DefaultPriceList) cur;
+            if (curDefaultPriceList.findPlan(name) != null) {
+                return curDefaultPriceList.getName();
+            }
+        }
+        throw new IllegalStateException("Cannot extract pricelist for plan " + name);
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
index 41fc61a..55dbad6 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
@@ -255,4 +255,17 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
         //result = 31 * result + (usages != null ? Arrays.hashCode(usages) : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultPlanPhase{");
+        sb.append("type=").append(type);
+        sb.append(", duration=").append(duration);
+        sb.append(", fixed=").append(fixed);
+        sb.append(", recurring=").append(recurring);
+        sb.append(", usages=").append(Arrays.toString(usages));
+        sb.append(", plan=").append(plan.getName());
+        sb.append('}');
+        return sb.toString();
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
index 7d7903d..9bdf070 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
@@ -16,7 +16,9 @@
 
 package org.killbill.billing.catalog;
 
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -27,10 +29,10 @@ import javax.xml.bind.annotation.XmlID;
 import javax.xml.bind.annotation.XmlIDREF;
 
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.xmlloader.ValidatingConfig;
-import org.killbill.xmlloader.ValidationError;
 import org.killbill.xmlloader.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
@@ -40,24 +42,29 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
     @XmlID
     private String name;
 
-    @XmlElementWrapper(name = "plans", required = true)
+    @XmlElementWrapper(name = "plans", required = false)
     @XmlIDREF
-    @XmlElement(name = "plan", required = true)
-    private DefaultPlan[] plans;
+    @XmlElement(type=DefaultPlan.class, name = "plan", required = false)
+    private CatalogEntityCollection<Plan> plans;
 
     public DefaultPriceList() {
+        this.plans = new CatalogEntityCollection();
     }
 
     public DefaultPriceList(final DefaultPlan[] plans, final String name) {
-        this.plans = plans;
+        this.plans = new CatalogEntityCollection(plans);
         this.name = name;
     }
 
     @Override
-    public DefaultPlan[] getPlans() {
+    public Collection<Plan> getPlans() {
         return plans;
     }
 
+
+    public CatalogEntityCollection<Plan> getCatalogEntityCollectionPlan() {
+        return plans;
+    }
     /* (non-Javadoc)
       * @see org.killbill.billing.catalog.IPriceList#getName()
       */
@@ -70,48 +77,33 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
       * @see org.killbill.billing.catalog.IPriceList#findPlan(org.killbill.billing.catalog.api.IProduct, org.killbill.billing.catalog.api.BillingPeriod)
       */
     @Override
-    public DefaultPlan findPlan(final Product product, final BillingPeriod period) {
-        for (final DefaultPlan cur : getPlans()) {
+    public Collection<Plan> findPlans(final Product product, final BillingPeriod period) {
+        final List<Plan> result = new ArrayList<Plan>(plans.size());
+        for (final Plan cur : getPlans()) {
             if (cur.getProduct().equals(product) &&
-                    (cur.getRecurringBillingPeriod() == null || cur.getRecurringBillingPeriod().equals(period))) {
-                return cur;
+                (cur.getRecurringBillingPeriod() != null && cur.getRecurringBillingPeriod().equals(period))) {
+                result.add(cur);
             }
         }
-        return null;
+        return result;
+    }
+
+    public Plan findPlan(final String planName) {
+        return plans.findByName(planName);
     }
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        for (final DefaultPlan cur : getPlans()) {
-            final int numPlans = findNumberOfPlans(cur.getProduct(), cur.getRecurringBillingPeriod());
-            if (numPlans > 1) {
-                errors.add(new ValidationError(
-                        String.format("There are %d plans in pricelist %s and have the same product/billingPeriod (%s, %s)",
-                                      numPlans, getName(), cur.getProduct().getName(), cur.getRecurringBillingPeriod()), catalog.getCatalogURI(),
-                        DefaultPriceListSet.class, getName()));
-            }
-        }
         return errors;
     }
 
-    private int findNumberOfPlans(final Product product, final BillingPeriod period) {
-        int count = 0;
-        for (final DefaultPlan cur : getPlans()) {
-            if (cur.getProduct().equals(product) &&
-                    (cur.getRecurringBillingPeriod() == null || cur.getRecurringBillingPeriod().equals(period))) {
-                count++;
-            }
-        }
-        return count;
-    }
-
     public DefaultPriceList setName(final String name) {
         this.name = name;
         return this;
     }
 
-    public DefaultPriceList setPlans(final DefaultPlan[] plans) {
-        this.plans = plans;
+    public DefaultPriceList setPlans(final Iterable<Plan> plans) {
+        this.plans = new CatalogEntityCollection(plans);
         return this;
     }
 
@@ -129,17 +121,16 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         if (name != null ? !name.equals(that.name) : that.name != null) {
             return false;
         }
-        if (!Arrays.equals(plans, that.plans)) {
+        if (!plans.equals(that.plans)) {
             return false;
         }
-
         return true;
     }
 
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (plans != null ? Arrays.hashCode(plans) : 0);
+        result = 31 * result + (plans != null ? plans.hashCode() : 0);
         return result;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
index 8c01b12..c159833 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
@@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 
 import org.killbill.billing.ErrorCode;
@@ -37,7 +38,7 @@ import org.killbill.xmlloader.ValidationErrors;
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> implements PriceListSet {
     @XmlElement(required = true, name = "defaultPriceList")
-    private PriceListDefault defaultPricelist;
+    private DefaultPriceList defaultPricelist;
 
     @XmlElement(required = false, name = "childPriceList")
     private DefaultPriceList[] childPriceLists;
@@ -48,23 +49,30 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
         }
     }
 
-    public DefaultPriceListSet(final PriceListDefault defaultPricelist, final DefaultPriceList[] childPriceLists) {
+    public DefaultPriceListSet(final DefaultPriceList defaultPricelist, final DefaultPriceList[] childPriceLists) {
         this.defaultPricelist = defaultPricelist;
         this.childPriceLists = childPriceLists != null ? childPriceLists : new DefaultPriceList[0];
     }
 
-    public DefaultPlan getPlanFrom(final String priceListName, final Product product,
-                                   final BillingPeriod period) throws CatalogApiException {
-        DefaultPlan result = null;
+    public Plan getPlanFrom(final Product product, final BillingPeriod period, final String priceListName) throws CatalogApiException {
+
+        Collection<Plan> plans = null;
         final DefaultPriceList pl = findPriceListFrom(priceListName);
         if (pl != null) {
-            result = pl.findPlan(product, period);
+            plans = pl.findPlans(product, period);
         }
-        if (result != null) {
-            return result;
+        if (plans.size() == 0) {
+            plans = defaultPricelist.findPlans(product, period);
+        }
+        switch(plans.size()) {
+            case 0:
+                return null;
+            case 1:
+                return plans.iterator().next();
+            default:
+                throw new CatalogApiException(ErrorCode.CAT_MULTIPLE_MATCHING_PLANS_FOR_PRICELIST,
+                                              priceListName, product.getName(), period);
         }
-
-        return defaultPricelist.findPlan(product, period);
     }
 
     public DefaultPriceList findPriceListFrom(final String priceListName) throws CatalogApiException {
@@ -142,8 +150,4 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
         return result;
     }
 
-    @Override
-    public Plan getPlanListFrom(final String s, final Product product, final BillingPeriod billingPeriod) {
-        return null;
-    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
index 9c0c1fd..ffb8e96 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
@@ -18,6 +18,7 @@ package org.killbill.billing.catalog;
 
 import java.net.URI;
 import java.util.Arrays;
+import java.util.Collection;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -35,7 +36,6 @@ import org.killbill.xmlloader.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implements Product {
-    private static final DefaultProduct[] EMPTY_PRODUCT_LIST = new DefaultProduct[0];
 
     @XmlAttribute(required = true)
     @XmlID
@@ -46,16 +46,16 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 
     @XmlElementWrapper(name = "included", required = false)
     @XmlIDREF
-    @XmlElement(name = "addonProduct", required = true)
-    private DefaultProduct[] included;
+    @XmlElement(type=DefaultProduct.class, name = "addonProduct", required = false)
+    private CatalogEntityCollection<Product> included;
 
     @XmlElementWrapper(name = "available", required = false)
     @XmlIDREF
-    @XmlElement(name = "addonProduct", required = true)
-    private DefaultProduct[] available;
+    @XmlElement(type=DefaultProduct.class, name = "addonProduct", required = false)
+    private CatalogEntityCollection<Product> available;
 
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     //Not included in XML
@@ -72,22 +72,29 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     }
 
     @Override
-    public DefaultProduct[] getIncluded() {
-        return included;
+    public Collection<Product> getIncluded() {
+        return included.getEntries();
     }
 
     @Override
-    public DefaultProduct[] getAvailable() {
-        return available;
+    public Collection<Product> getAvailable() {
+        return available.getEntries();
     }
 
+
+    public CatalogEntityCollection<Product> getCatalogEntityCollectionAvailable() {
+        return available;
+    };
+
     public DefaultProduct() {
-        included = EMPTY_PRODUCT_LIST;
-        available = EMPTY_PRODUCT_LIST;
-        limits = new DefaultLimit[0];
+        this.included = new CatalogEntityCollection<Product>();
+        this.available = new CatalogEntityCollection<Product>();
+        this.limits = new DefaultLimit[0];
     }
 
     public DefaultProduct(final String name, final ProductCategory category) {
+        this.included = new CatalogEntityCollection<Product>();
+        this.available = new CatalogEntityCollection<Product>();
         this.category = category;
         this.name = name;
     }
@@ -98,7 +105,7 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     }
 
     public boolean isIncluded(final DefaultProduct addon) {
-        for (final DefaultProduct p : included) {
+        for (final Product p : included.getEntries()) {
             if (addon == p) {
                 return true;
             }
@@ -107,7 +114,7 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     }
 
     public boolean isAvailable(final DefaultProduct addon) {
-        for (final DefaultProduct p : included) {
+        for (final Product p : included.getEntries()) {
             if (addon == p) {
                 return true;
             }
@@ -168,13 +175,13 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
         return this;
     }
 
-    public DefaultProduct setIncluded(final DefaultProduct[] included) {
-        this.included = included;
+    public DefaultProduct setIncluded(final Collection<Product> included) {
+        this.included = new CatalogEntityCollection<Product>(included);
         return this;
     }
 
-    public DefaultProduct setAvailable(final DefaultProduct[] available) {
-        this.available = available;
+    public DefaultProduct setAvailable(final Collection<Product> available) {
+        this.available = new CatalogEntityCollection<Product>(available);
         return this;
     }
 
@@ -185,9 +192,14 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 
     @Override
     public String toString() {
-        return "DefaultProduct [name=" + name + ", category=" + category + ", included="
-                + Arrays.toString(included) + ", available=" + Arrays.toString(available) + ", catalogName="
-                + catalogName + "]";
+        return "DefaultProduct{" +
+               "name='" + name + '\'' +
+               ", category=" + category +
+               ", included=" + included +
+               ", available=" + available +
+               ", limits=" + Arrays.toString(limits) +
+               ", catalogName='" + catalogName + '\'' +
+               '}';
     }
 
     @Override
@@ -195,41 +207,40 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
+        if (!(o instanceof DefaultProduct)) {
             return false;
         }
 
-        final DefaultProduct that = (DefaultProduct) o;
+        final DefaultProduct product = (DefaultProduct) o;
 
-        if (!Arrays.equals(available, that.available)) {
-            return false;
-        }
-        if (catalogName != null ? !catalogName.equals(that.catalogName) : that.catalogName != null) {
+        if (name != null ? !name.equals(product.name) : product.name != null) {
             return false;
         }
-        if (category != that.category) {
+        if (category != product.category) {
             return false;
         }
-        if (!Arrays.equals(included, that.included)) {
+        if (included != null ? !included.equals(product.included) : product.included != null) {
             return false;
         }
-        if (!Arrays.equals(limits, that.limits)) {
+        if (available != null ? !available.equals(product.available) : product.available != null) {
             return false;
         }
-        if (name != null ? !name.equals(that.name) : that.name != null) {
+        // Probably incorrect - comparing Object[] arrays with Arrays.equals
+        if (!Arrays.equals(limits, product.limits)) {
             return false;
         }
+        return catalogName != null ? catalogName.equals(product.catalogName) : product.catalogName == null;
 
-        return true;
     }
 
     @Override
     public int hashCode() {
         int result = name != null ? name.hashCode() : 0;
         result = 31 * result + (category != null ? category.hashCode() : 0);
-        result = 31 * result + (included != null ? Arrays.hashCode(included) : 0);
-        result = 31 * result + (available != null ? Arrays.hashCode(available) : 0);
-        result = 31 * result + (limits != null ? Arrays.hashCode(limits) : 0);
+        result = 31 * result + (included != null ? included.hashCode() : 0);
+        result = 31 * result + (available != null ? available.hashCode() : 0);
+        result = 31 * result + Arrays.hashCode(limits);
+        result = 31 * result + (catalogName != null ? catalogName.hashCode() : 0);
         return result;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
index f3da569..0485363 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
@@ -42,11 +42,11 @@ import java.util.Arrays;
 public class DefaultTier extends ValidatingConfig<StandaloneCatalog> implements Tier {
 
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     @XmlElementWrapper(name = "blocks", required = false)
-    @XmlElement(name = "tieredBlock", required = true)
+    @XmlElement(name = "tieredBlock", required = false)
     private DefaultTieredBlock[] blocks;
 
     // Used to define a fixed price for the whole tier section
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
index 855acf9..fcc27a7 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
@@ -65,17 +65,17 @@ public class DefaultUsage extends ValidatingConfig<StandaloneCatalog> implements
 
     // Used for when billing usage IN_ADVANCE & CAPACITY
     @XmlElementWrapper(name = "limits", required = false)
-    @XmlElement(name = "limit", required = true)
+    @XmlElement(name = "limit", required = false)
     private DefaultLimit[] limits;
 
     // Used for when billing usage IN_ADVANCE & CONSUMABLE
     @XmlElementWrapper(name = "blocks", required = false)
-    @XmlElement(name = "block", required = true)
+    @XmlElement(name = "block", required = false)
     private DefaultBlock[] blocks;
 
     // Used for when billing usage IN_ARREAR
     @XmlElementWrapper(name = "tiers", required = false)
-    @XmlElement(name = "tier", required = true)
+    @XmlElement(name = "tier", required = false)
     private DefaultTier[] tiers;
 
     // Used to define a fixed price for the whole usage section -- bundle several limits/blocks of units.
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
index 7651aad..794abd5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
@@ -38,7 +38,7 @@ import org.killbill.billing.catalog.plugin.api.CatalogPluginApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
-import org.killbill.billing.util.config.CatalogConfig;
+import org.killbill.billing.util.config.definition.CatalogConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 0d85afc..80217c8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -20,6 +20,7 @@ package org.killbill.billing.catalog.io;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -37,6 +38,7 @@ import org.killbill.clock.Clock;
 import org.killbill.xmlloader.UriAccessor;
 import org.killbill.xmlloader.XMLLoader;
 
+import com.google.common.io.Resources;
 import com.google.inject.Inject;
 
 public class VersionedCatalogLoader implements CatalogLoader {
@@ -63,8 +65,9 @@ public class VersionedCatalogLoader implements CatalogLoader {
                 xmlURIs = new ArrayList<URI>();
                 xmlURIs.add(new URI(uriString));
             } else { // Assume its a directory
+                final URL url = getURLFromString(uriString);
                 final String directoryContents = UriAccessor.accessUriAsString(uriString);
-                xmlURIs = findXmlReferences(directoryContents, new URL(uriString));
+                xmlURIs = findXmlReferences(directoryContents, url);
             }
 
             final VersionedCatalog result = new VersionedCatalog(clock);
@@ -74,11 +77,21 @@ public class VersionedCatalogLoader implements CatalogLoader {
             }
             return result;
         } catch (Exception e) {
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog ", e);
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog: ", e.getMessage());
         }
     }
 
-    public VersionedCatalog load(final Iterable<String> catalogXMLs, final Long tenantRecordId) throws CatalogApiException {
+    private URL getURLFromString(final String urlString) {
+        try {
+            // If the string provided is already a URL (with correct scheme, ...) return the URL object
+            return new URL(urlString);
+        } catch (final MalformedURLException ignore) {
+        }
+        // If not, this must be something on the classpath
+        return Resources.getResource(urlString);
+    }
+
+    public VersionedCatalog load(final Iterable<String> catalogXMLs, final boolean filterTemplateCatalog, final Long tenantRecordId) throws CatalogApiException {
         final VersionedCatalog result = new VersionedCatalog(clock);
         final URI uri;
         try {
@@ -86,13 +99,15 @@ public class VersionedCatalogLoader implements CatalogLoader {
             for (final String cur : catalogXMLs) {
                 final InputStream curCatalogStream = new ByteArrayInputStream(cur.getBytes());
                 final StandaloneCatalog catalog = XMLLoader.getObjectFromStream(uri, curCatalogStream, StandaloneCatalog.class);
-                result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride, tenantRecordId, internalCallContextFactory));
+                if (!filterTemplateCatalog || !catalog.isTemplateCatalog()) {
+                    result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride, tenantRecordId, internalCallContextFactory));
+                }
             }
             return result;
         } catch (final CatalogApiException e) {
             throw e;
         } catch (final Exception e) {
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog ", e);
+            throw new CatalogApiException(ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
         }
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
index 56e21fb..1e4569d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
@@ -17,9 +17,17 @@
 
 package org.killbill.billing.catalog.override;
 
+
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -52,6 +60,8 @@ import java.util.regex.Pattern;
 
 public class DefaultPriceOverride implements PriceOverride {
 
+    private static final AtomicLong DRY_RUN_PLAN_IDX = new AtomicLong(0);
+
     public static final Pattern CUSTOM_PLAN_NAME_PATTERN = Pattern.compile("(.*)-(\\d+)$");
 
     private final CatalogOverrideDao overrideDao;
@@ -64,7 +74,7 @@ public class DefaultPriceOverride implements PriceOverride {
     }
 
     @Override
-    public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException {
+    public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, @Nullable final InternalCallContext context) throws CatalogApiException {
 
         final PlanPhasePriceOverride[] resolvedOverride = new PlanPhasePriceOverride[parentPlan.getAllPhases().length];
         int index = 0;
@@ -111,9 +121,18 @@ public class DefaultPriceOverride implements PriceOverride {
             }
         }
 
-        final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan, catalogEffectiveDate, resolvedOverride, context);
-        final String planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
+        final String planName;
+        if (context != null) {
+            final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan, catalogEffectiveDate, resolvedOverride, context);
+            planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
+        } else {
+            planName = new StringBuffer(parentPlan.getName()).append("-dryrun-").append(DRY_RUN_PLAN_IDX.incrementAndGet()).toString();
+        }
+
         final DefaultPlan result = new DefaultPlan(planName, (DefaultPlan) parentPlan, resolvedOverride);
+        if (context == null) {
+            overriddenPlanCache.addDryRunPlan(planName, result);
+        }
         return result;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
index b8335b0..6f52dc1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
@@ -19,7 +19,10 @@ package org.killbill.billing.catalog.plugin;
 
 import java.net.URI;
 import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.annotation.Nullable;
 
@@ -35,7 +38,6 @@ import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.DefaultRecurring;
 import org.killbill.billing.catalog.DefaultUnit;
 import org.killbill.billing.catalog.DefaultUsage;
-import org.killbill.billing.catalog.PriceListDefault;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.CurrencyValueNull;
@@ -81,18 +83,22 @@ public class StandaloneCatalogMapper {
     private final String catalogName;
     private final BillingMode recurringBillingMode;
 
-    private Iterable<DefaultProduct> tmpDefaultProducts;
-    private Iterable<DefaultPlan> tmpDefaultPlans;
+    private Iterable<Product> tmpDefaultProducts;
+    private Iterable<Plan> tmpDefaultPlans;
     private DefaultPriceListSet tmpDefaultPriceListSet;
+    private Map<String, DefaultPriceList> tmpDefaultPriceListMap;
 
     public StandaloneCatalogMapper(final String catalogName, final BillingMode recurringBillingMode) {
         this.catalogName = catalogName;
         this.recurringBillingMode = recurringBillingMode;
         this.tmpDefaultProducts = null;
         this.tmpDefaultPlans = null;
+        this.tmpDefaultPriceListMap = new HashMap<String, DefaultPriceList>();
     }
 
     public StandaloneCatalog toStandaloneCatalog(final StandalonePluginCatalog pluginCatalog, @Nullable URI catalogURI) {
+
+
         final StandaloneCatalog result = new StandaloneCatalog();
         result.setCatalogName(catalogName);
         result.setEffectiveDate(pluginCatalog.getEffectiveDate().toDate());
@@ -103,12 +109,11 @@ public class StandaloneCatalogMapper {
         result.setSupportedCurrencies(toArray(pluginCatalog.getCurrencies()));
         result.setUnits(toDefaultUnits(pluginCatalog.getUnits()));
         result.setPlanRules(toDefaultPlanRules(pluginCatalog.getPlanRules()));
-
         for (final Product cur : pluginCatalog.getProducts()) {
-            for (DefaultProduct target : result.getCurrentProducts()) {
+            for (Product target :  result.getCurrentProducts()) {
                 if (target.getName().equals(cur.getName())) {
-                    target.setAvailable(toFilteredDefaultProduct(ImmutableList.copyOf(cur.getAvailable())));
-                    target.setIncluded(toFilteredDefaultProduct(ImmutableList.copyOf(cur.getIncluded())));
+                    ((DefaultProduct) target).setAvailable(toFilteredDefaultProduct(cur.getAvailable()));
+                    ((DefaultProduct) target).setIncluded(toFilteredDefaultProduct(cur.getIncluded()));
                     break;
                 }
             }
@@ -250,81 +255,70 @@ public class StandaloneCatalogMapper {
     }
 
 
-    private DefaultProduct[] toDefaultProducts(final Iterable<Product> input) {
+    private Iterable<Product> toDefaultProducts(final Iterable<Product> input) {
         if (tmpDefaultProducts == null) {
-            final Function<Product, DefaultProduct> productTransformer = new Function<Product, DefaultProduct>() {
+            final Function<Product, Product> productTransformer = new Function<Product, Product>() {
                 @Override
-                public DefaultProduct apply(final Product input) {
+                public Product apply(final Product input) {
                     return toDefaultProduct(input);
                 }
             };
             tmpDefaultProducts = ImmutableList.copyOf(Iterables.transform(input, productTransformer));
         }
-        return toArray(tmpDefaultProducts);
+        return tmpDefaultProducts;
     }
 
-    private DefaultProduct[] toFilteredDefaultProduct(final Iterable<Product> input) {
+    private Collection<Product> toFilteredDefaultProduct(final Collection<Product> input) {
         if (!input.iterator().hasNext()) {
-            return new DefaultProduct[0];
+            return Collections.emptyList();
         }
-        final List<String> inputProductNames = ImmutableList.copyOf(Iterables.transform(input, new Function<Product, String>() {
+        final Iterable<String> inputProductNames = Iterables.transform(input, new Function<Product, String>() {
             @Override
             public String apply(final Product input) {
                 return input.getName();
             }
-        }));
-        final List<DefaultProduct> filteredAndOrdered = new ArrayList<DefaultProduct>(inputProductNames.size());
+        });
+        final Collection<Product> filteredAndOrdered = new ArrayList<Product>(input.size());
         for (final String cur : inputProductNames) {
-            final DefaultProduct found = findOrIllegalState(tmpDefaultProducts, new Predicate<DefaultProduct>() {
+            final Product found = findOrIllegalState(tmpDefaultProducts, new Predicate<Product>() {
                 @Override
-                public boolean apply(final DefaultProduct inputPredicate) {
+                public boolean apply(final Product inputPredicate) {
                     return inputPredicate.getName().equals(cur);
                 }
             }, "Failed to find product " + cur);
             filteredAndOrdered.add(found);
         }
-        return toArray(filteredAndOrdered);
+        return filteredAndOrdered;
     }
 
-    private DefaultPlan[] toDefaultPlans(final Iterable<Plan> input) {
+    private Iterable<Plan> toDefaultPlans(final Iterable<Plan> input) {
         if (tmpDefaultPlans == null) {
-            final Function<Plan, DefaultPlan> planTransformer = new Function<Plan, DefaultPlan>() {
+            final Function<Plan, Plan> planTransformer = new Function<Plan, Plan>() {
                 @Override
-                public DefaultPlan apply(final Plan input) {
+                public Plan apply(final Plan input) {
                     return toDefaultPlan(input);
                 }
             };
             tmpDefaultPlans = ImmutableList.copyOf(Iterables.transform(input, planTransformer));
         }
-        return toArray(tmpDefaultPlans);
+        return tmpDefaultPlans;
     }
 
-    private DefaultPlan[] toFilterDefaultPlans(final Iterable<Plan> input) {
+    private Iterable<Plan> toFilterDefaultPlans(final String priceListName) {
         if (tmpDefaultPlans == null) {
             throw new IllegalStateException("Cannot filter on uninitialized plans");
         }
-        final List<String> inputPlanNames = ImmutableList.copyOf(Iterables.transform(input, new Function<Plan, String>() {
+        return Iterables.filter(tmpDefaultPlans, new Predicate<Plan>() {
             @Override
-            public String apply(final Plan input) {
-                return input.getName();
+            public boolean apply(final Plan input) {
+                return input.getPriceListName().equals(priceListName);
             }
-        }));
-        final List<DefaultPlan> filteredAndOrdered = new ArrayList<DefaultPlan>(inputPlanNames.size());
-        for (final String cur : inputPlanNames) {
-            final DefaultPlan found = findOrIllegalState(tmpDefaultPlans, new Predicate<DefaultPlan>() {
-                @Override
-                public boolean apply(final DefaultPlan inputPredicate) {
-                    return inputPredicate.getName().equals(cur);
-                }
-            }, "Failed to find plan " + cur);
-            filteredAndOrdered.add(found);
-        }
-        return toArray(filteredAndOrdered);
+        });
     }
 
     private DefaultPriceListSet toDefaultPriceListSet(final PriceList defaultPriceList, final Iterable<PriceList> childrenPriceLists) {
         if (tmpDefaultPriceListSet == null) {
-            tmpDefaultPriceListSet = new DefaultPriceListSet(toPriceListDefault(defaultPriceList), toDefaultPriceLists(childrenPriceLists));
+            tmpDefaultPriceListSet = new DefaultPriceListSet(toDefaultPriceList(defaultPriceList), toDefaultPriceLists(childrenPriceLists));
         }
         return tmpDefaultPriceListSet;
     }
@@ -379,30 +373,26 @@ public class StandaloneCatalogMapper {
         if (input == null) {
             return null;
         }
-        final DefaultPriceList result = new DefaultPriceList();
-        result.setName(input.getName());
-        result.setPlans(toFilterDefaultPlans(ImmutableList.copyOf(input.getPlans())));
-        return result;
-    }
 
-    private PriceListDefault toPriceListDefault(@Nullable final PriceList input) {
-        if (input == null) {
-            return null;
+        DefaultPriceList result = tmpDefaultPriceListMap.get(input.getName());
+        if (result == null) {
+            result = new DefaultPriceList();
+            result.setName(input.getName());
+            result.setPlans(toFilterDefaultPlans(input.getName()));
+            tmpDefaultPriceListMap.put(input.getName(), result);
         }
-        final PriceListDefault result = new PriceListDefault();
-        result.setName(input.getName());
-        result.setPlans(toFilterDefaultPlans(ImmutableList.copyOf(input.getPlans())));
         return result;
     }
 
-    private DefaultProduct toDefaultProduct(@Nullable final Product input) {
+
+    private Product toDefaultProduct(@Nullable final Product input) {
         if (input == null) {
             return null;
         }
         if (tmpDefaultProducts != null) {
-            final DefaultProduct existingProduct = findOrIllegalState(tmpDefaultProducts, new Predicate<DefaultProduct>() {
+            final Product existingProduct = findOrIllegalState(tmpDefaultProducts, new Predicate<Product>() {
                 @Override
-                public boolean apply(final DefaultProduct predicateInput) {
+                public boolean apply(final Product predicateInput) {
                     return predicateInput.getName().equals(input.getName());
                 }
             }, "Unknown product " + input.getName());
@@ -415,11 +405,11 @@ public class StandaloneCatalogMapper {
         return result;
     }
 
-    private DefaultPlan toDefaultPlan(final Plan input) {
+    private Plan toDefaultPlan(final Plan input) {
         if (tmpDefaultPlans != null) {
-            final DefaultPlan existingPlan = findOrIllegalState(tmpDefaultPlans, new Predicate<DefaultPlan>() {
+            final Plan existingPlan = findOrIllegalState(tmpDefaultPlans, new Predicate<Plan>() {
                 @Override
-                public boolean apply(final DefaultPlan predicateInput) {
+                public boolean apply(final Plan predicateInput) {
                     return predicateInput.getName().equals(input.getName());
                 }
             }, "Unknown plan " + input.getName());
@@ -427,11 +417,12 @@ public class StandaloneCatalogMapper {
         }
         final DefaultPlan result = new DefaultPlan();
         result.setName(input.getName());
-        result.setEffectiveDateForExistingSubscriptons(input.getEffectiveDateForExistingSubscriptons());
+        result.setEffectiveDateForExistingSubscriptions(input.getEffectiveDateForExistingSubscriptions());
         result.setFinalPhase(toDefaultPlanPhase(input.getFinalPhase()));
         result.setInitialPhases(toDefaultPlanPhases(ImmutableList.copyOf(input.getInitialPhases())));
         result.setPlansAllowedInBundle(input.getPlansAllowedInBundle());
         result.setProduct(toDefaultProduct(input.getProduct()));
+        result.setPriceListName(input.getPriceListName());
         return result;
     }
 
@@ -513,4 +504,4 @@ public class StandaloneCatalogMapper {
         }
         return result;
     }
-}
+}
\ No newline at end of file
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
index 39ec3fa..1ee8470 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
@@ -17,16 +17,15 @@
 
 package org.killbill.billing.catalog.plugin;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
 import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
@@ -51,24 +50,18 @@ public class VersionedCatalogMapper {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    public VersionedCatalog toVersionedCatalog(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
-        final VersionedCatalog result = new VersionedCatalog(clock, pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode(), toStandaloneCatalogWithPriceOverrideList(pluginCatalog, internalTenantContext), internalTenantContext);
+    public VersionedCatalog toVersionedCatalog(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) throws CatalogApiException {
+        final VersionedCatalog result = new VersionedCatalog(clock);
+        for (final StandalonePluginCatalog cur : pluginCatalog.getStandalonePluginCatalogs()) {
+            result.add(toStandaloneCatalogWithPriceOverride(pluginCatalog, cur, internalTenantContext));
+        }
         return result;
     }
 
-    private List<StandaloneCatalogWithPriceOverride> toStandaloneCatalogWithPriceOverrideList(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
-        return ImmutableList.copyOf(Iterables.transform(pluginCatalog.getStandalonePluginCatalogs(), new Function<StandalonePluginCatalog, StandaloneCatalogWithPriceOverride>() {
-            @Override
-            public StandaloneCatalogWithPriceOverride apply(final StandalonePluginCatalog input) {
-                return toStandaloneCatalogWithPriceOverride(pluginCatalog, input, internalTenantContext);
-            }
-        }));
-    }
-
     private StandaloneCatalogWithPriceOverride toStandaloneCatalogWithPriceOverride(final VersionedPluginCatalog pluginCatalog, final StandalonePluginCatalog input, final InternalTenantContext internalTenantContext) {
         final StandaloneCatalogMapper mapper = new StandaloneCatalogMapper(pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode());
-        final StandaloneCatalog standaloneCatalog = mapper.toStandaloneCatalog(input, null);
-        final StandaloneCatalogWithPriceOverride result = new StandaloneCatalogWithPriceOverride(standaloneCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
+        final StandaloneCatalog catalog = mapper.toStandaloneCatalog(input, null);
+        final StandaloneCatalogWithPriceOverride result = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
         return result;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
index 629db2b..de0bef2 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
@@ -17,12 +17,16 @@
 package org.killbill.billing.catalog.rules;
 
 
+import org.killbill.billing.catalog.DefaultPrice;
 import org.killbill.billing.catalog.DefaultPriceList;
 import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.xmlloader.ValidatingConfig;
@@ -48,10 +52,26 @@ public abstract class DefaultCase<T> extends ValidatingConfig<StandaloneCatalog>
     }
 
     protected boolean satisfiesCase(final PlanSpecifier planPhase, final StaticCatalog c) throws CatalogApiException {
-        return (getProduct() == null || getProduct().equals(c.findCurrentProduct(planPhase.getProductName()))) &&
-                (getProductCategory() == null || getProductCategory().equals(planPhase.getProductCategory())) &&
-                (getBillingPeriod() == null || getBillingPeriod().equals(planPhase.getBillingPeriod())) &&
-                (getPriceList() == null || getPriceList().equals(c.findCurrentPricelist(planPhase.getPriceListName())));
+        final Product product;
+        final BillingPeriod billingPeriod;
+        final ProductCategory productCategory;
+        final PriceList priceList;
+        if (planPhase.getPlanName() != null) {
+            final Plan plan = c.findCurrentPlan(planPhase.getPlanName());
+            product = plan.getProduct();
+            billingPeriod = plan.getRecurringBillingPeriod();
+            productCategory = plan.getProduct().getCategory();
+            priceList = c.findCurrentPricelist(plan.getPriceListName());
+        } else {
+            product = c.findCurrentProduct(planPhase.getProductName());
+            billingPeriod = planPhase.getBillingPeriod();
+            productCategory = product.getCategory();
+            priceList = getPriceList() != null ? c.findCurrentPricelist(planPhase.getPriceListName()) : null;
+        }
+        return (getProduct() == null || getProduct().equals(product)) &&
+               (getProductCategory() == null || getProductCategory().equals(productCategory)) &&
+               (getBillingPeriod() == null || getBillingPeriod().equals(billingPeriod)) &&
+               (getPriceList() == null || getPriceList().equals(priceList));
     }
 
     public static <K> K getResult(final DefaultCase<K>[] cases, final PlanSpecifier planSpec, final StaticCatalog catalog) throws CatalogApiException {
@@ -72,7 +92,7 @@ public abstract class DefaultCase<T> extends ValidatingConfig<StandaloneCatalog>
         return errors;
     }
 
-    protected abstract DefaultCase<T> setProduct(DefaultProduct product);
+    protected abstract DefaultCase<T> setProduct(Product product);
 
     protected abstract DefaultCase<T> setProductCategory(ProductCategory productCategory);
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
index a9e25d9..ea441bd 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
@@ -73,20 +74,56 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
 
     protected abstract T getResult();
 
-
-
     public T getResult(final PlanPhaseSpecifier from,
                        final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
+
+
+
+        final Product inFromProduct;
+        final BillingPeriod inFromBillingPeriod;
+        final ProductCategory inFromProductCategory;
+        final PriceList inFromPriceList;
+        if (from.getPlanName() != null) {
+            final Plan plan = catalog.findCurrentPlan(from.getPlanName());
+            inFromProduct = plan.getProduct();
+            inFromBillingPeriod = plan.getRecurringBillingPeriod();
+            inFromProductCategory = plan.getProduct().getCategory();
+            inFromPriceList = catalog.findCurrentPricelist(plan.getPriceListName());
+        } else {
+            inFromProduct = catalog.findCurrentProduct(from.getProductName());
+            inFromBillingPeriod = from.getBillingPeriod();
+            inFromProductCategory = inFromProduct.getCategory();
+            inFromPriceList = from.getPriceListName() != null ? catalog.findCurrentPricelist(from.getPriceListName()) : null;
+        }
+
+        final Product inToProduct;
+        final BillingPeriod inToBillingPeriod;
+        final ProductCategory inToProductCategory;
+        final PriceList inToPriceList;
+        if (to.getPlanName() != null) {
+            final Plan plan = catalog.findCurrentPlan(to.getPlanName());
+            inToProduct = plan.getProduct();
+            inToBillingPeriod = plan.getRecurringBillingPeriod();
+            inToProductCategory = plan.getProduct().getCategory();
+            inToPriceList = catalog.findCurrentPricelist(plan.getPriceListName());
+        } else {
+            inToProduct = catalog.findCurrentProduct(to.getProductName());
+            inToBillingPeriod = to.getBillingPeriod();
+            inToProductCategory = inToProduct.getCategory();
+            inToPriceList = to.getPriceListName() != null ? catalog.findCurrentPricelist(to.getPriceListName()) : null;
+        }
+
+
         if (
                 (phaseType == null || from.getPhaseType() == phaseType) &&
-                        (fromProduct == null || fromProduct.equals(catalog.findCurrentProduct(from.getProductName()))) &&
-                        (fromProductCategory == null || fromProductCategory.equals(from.getProductCategory())) &&
-                        (fromBillingPeriod == null || fromBillingPeriod.equals(from.getBillingPeriod())) &&
-                        (toProduct == null || toProduct.equals(catalog.findCurrentProduct(to.getProductName()))) &&
-                        (toProductCategory == null || toProductCategory.equals(to.getProductCategory())) &&
-                        (toBillingPeriod == null || toBillingPeriod.equals(to.getBillingPeriod())) &&
-                        (fromPriceList == null || fromPriceList.equals(catalog.findCurrentPricelist(from.getPriceListName()))) &&
-                        (toPriceList == null || toPriceList.equals(catalog.findCurrentPricelist(to.getPriceListName())))
+                (fromProduct == null || fromProduct.equals(inFromProduct)) &&
+                (fromProductCategory == null || fromProductCategory.equals(inFromProductCategory)) &&
+                (fromBillingPeriod == null || fromBillingPeriod.equals(inFromBillingPeriod)) &&
+                (this.toProduct == null || this.toProduct.equals(inToProduct)) &&
+                (this.toProductCategory == null || this.toProductCategory.equals(inToProductCategory)) &&
+                (this.toBillingPeriod == null || this.toBillingPeriod.equals(inToBillingPeriod)) &&
+                (fromPriceList == null || fromPriceList.equals(inFromPriceList)) &&
+                (toPriceList == null || toPriceList.equals(inToPriceList))
                 ) {
             return getResult();
         }
@@ -117,8 +154,8 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
         return this;
     }
 
-    public DefaultCaseChange<T> setFromProduct(final DefaultProduct fromProduct) {
-        this.fromProduct = fromProduct;
+    public DefaultCaseChange<T> setFromProduct(final Product fromProduct) {
+        this.fromProduct = (DefaultProduct) fromProduct;
         return this;
     }
 
@@ -137,8 +174,8 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
         return this;
     }
 
-    public DefaultCaseChange<T> setToProduct(final DefaultProduct toProduct) {
-        this.toProduct = toProduct;
+    public DefaultCaseChange<T> setToProduct(final Product toProduct) {
+        this.toProduct = (DefaultProduct) toProduct;
         return this;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
index f0a0011..cf656ef 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
@@ -33,8 +33,7 @@ public abstract class DefaultCasePhase<T> extends DefaultCaseStandardNaming<T> {
 
     public T getResult(final PlanPhaseSpecifier specifier, final StaticCatalog c) throws CatalogApiException {
         if ((phaseType == null || specifier.getPhaseType() == phaseType)
-                && satisfiesCase(new PlanSpecifier(specifier), c)
-                ) {
+                && satisfiesCase(new PlanSpecifier(specifier), c)) {
             return getResult();
         }
         return null;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
index e922b0e..79c73aa 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
@@ -22,6 +22,7 @@ import javax.xml.bind.annotation.XmlIDREF;
 import org.killbill.billing.catalog.DefaultPriceList;
 import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.rules.Case;
 
@@ -59,8 +60,8 @@ public abstract class DefaultCaseStandardNaming<T> extends DefaultCase<T> implem
         return priceList;
     }
 
-    public DefaultCaseStandardNaming<T> setProduct(final DefaultProduct product) {
-        this.product = product;
+    public DefaultCaseStandardNaming<T> setProduct(final Product product) {
+        this.product = (DefaultProduct) product;
         return this;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
index b0e3222..99efa9c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
@@ -74,7 +74,6 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
     @XmlElement(name = "priceListCase", required = false)
     private DefaultCasePriceList[] priceListCase;
 
-
     @Override
     public Iterable<CaseChangePlanPolicy> getCaseChangePlanPolicy() {
         return ImmutableList.<CaseChangePlanPolicy>copyOf(changeCase);
@@ -105,65 +104,61 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
         return ImmutableList.<CasePriceList>copyOf(priceListCase);
     }
 
-
-
     public PlanAlignmentCreate getPlanCreateAlignment(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException {
-        final PlanAlignmentCreate result =  DefaultCase.getResult(createAlignmentCase, specifier, catalog);
+        final PlanAlignmentCreate result = DefaultCase.getResult(createAlignmentCase, specifier, catalog);
         return (result != null) ? result : PlanAlignmentCreate.START_OF_BUNDLE;
     }
 
     public BillingActionPolicy getPlanCancelPolicy(final PlanPhaseSpecifier planPhase, final StaticCatalog catalog) throws CatalogApiException {
-        final BillingActionPolicy result =  DefaultCasePhase.getResult(cancelCase, planPhase, catalog);
+        final BillingActionPolicy result = DefaultCasePhase.getResult(cancelCase, planPhase, catalog);
         return (result != null) ? result : BillingActionPolicy.END_OF_TERM;
     }
 
     public BillingAlignment getBillingAlignment(final PlanPhaseSpecifier planPhase, final StaticCatalog catalog) throws CatalogApiException {
         final BillingAlignment result = DefaultCasePhase.getResult(billingAlignmentCase, planPhase, catalog);
-        return (result != null) ?  result : BillingAlignment.ACCOUNT;
+        return (result != null) ? result : BillingAlignment.ACCOUNT;
     }
 
-    public PlanChangeResult planChange(final PlanPhaseSpecifier from, PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
-        final DefaultPriceList toPriceList;
-        if (to.getPriceListName() == null) { // Pricelist may be null because it is unspecified this is the principal use-case
-            toPriceList = findPriceList(from.toPlanSpecifier(), catalog);
-            to = new PlanSpecifier(to.getProductName(), to.getProductCategory(), to.getBillingPeriod(), toPriceList.getName());
-        } else {
-            toPriceList = (DefaultPriceList) catalog.findCurrentPricelist(to.getPriceListName());
-        }
+    public PlanChangeResult planChange(final PlanPhaseSpecifier from, final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
+
+        final DefaultPriceList toPriceList = to.getPriceListName() != null ?
+                                             (DefaultPriceList) catalog.findCurrentPricelist(to.getPriceListName()) :
+                                             findPriceList(from, catalog);
 
-        final BillingActionPolicy policy = getPlanChangePolicy(from, to, catalog);
+        // If we use old scheme {product, billingPeriod, pricelist}, ensure pricelist is correct
+        // (Pricelist may be null because if it is unspecified this is the principal use-case)
+        final PlanSpecifier toWithPriceList = to.getPlanName() == null ?
+                                              new PlanSpecifier(to.getProductName(), to.getBillingPeriod(), toPriceList.getName()) :
+                                              to;
+
+
+        final BillingActionPolicy policy = getPlanChangePolicy(from, toWithPriceList, catalog);
         if (policy == BillingActionPolicy.ILLEGAL) {
-            throw new IllegalPlanChange(from, to);
+            throw new IllegalPlanChange(from, toWithPriceList);
         }
 
-        final PlanAlignmentChange alignment = getPlanChangeAlignment(from, to, catalog);
+        final PlanAlignmentChange alignment = getPlanChangeAlignment(from, toWithPriceList, catalog);
 
         return new PlanChangeResult(toPriceList, policy, alignment);
     }
 
     public PlanAlignmentChange getPlanChangeAlignment(final PlanPhaseSpecifier from,
                                                       final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
-        final PlanAlignmentChange result =  DefaultCaseChange.getResult(changeAlignmentCase, from, to, catalog);
+        final PlanAlignmentChange result = DefaultCaseChange.getResult(changeAlignmentCase, from, to, catalog);
         return (result != null) ? result : PlanAlignmentChange.START_OF_BUNDLE;
     }
 
     public BillingActionPolicy getPlanChangePolicy(final PlanPhaseSpecifier from,
                                                    final PlanSpecifier to, final StaticCatalog catalog) throws CatalogApiException {
-        if (from.getProductName().equals(to.getProductName()) &&
-            from.getBillingPeriod() == to.getBillingPeriod() &&
-            from.getPriceListName().equals(to.getPriceListName())) {
-            return BillingActionPolicy.ILLEGAL;
-        }
-        //Plan toPlan = catalog.findPlan()
-
-        final BillingActionPolicy result =  DefaultCaseChange.getResult(changeCase, from, to, catalog);
+        final BillingActionPolicy result = DefaultCaseChange.getResult(changeCase, from, to, catalog);
         return (result != null) ? result : BillingActionPolicy.END_OF_TERM;
     }
 
     private DefaultPriceList findPriceList(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException {
         DefaultPriceList result = DefaultCase.getResult(priceListCase, specifier, catalog);
         if (result == null) {
-            result = (DefaultPriceList) catalog.findCurrentPricelist(specifier.getPriceListName());
+            final String priceListName = specifier.getPlanName() != null ? catalog.findCurrentPlan(specifier.getPlanName()).getPriceListName() : specifier.getPriceListName();
+            result = (DefaultPriceList) catalog.findCurrentPricelist(priceListName);
         }
         return result;
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index 0d02237..e6e6cf1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -19,6 +19,7 @@ package org.killbill.billing.catalog;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 
@@ -46,6 +47,7 @@ import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.StaticCatalog;
@@ -66,24 +68,24 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     @XmlElement(required = true)
     private BillingMode recurringBillingMode;
 
-    @XmlElementWrapper(name = "currencies", required = true)
-    @XmlElement(name = "currency", required = true)
+    @XmlElementWrapper(name = "currencies", required = false)
+    @XmlElement(name = "currency", required = false)
     private Currency[] supportedCurrencies;
 
     @XmlElementWrapper(name = "units", required = false)
-    @XmlElement(name = "unit", required = true)
+    @XmlElement(name = "unit", required = false)
     private DefaultUnit[] units;
 
-    @XmlElementWrapper(name = "products", required = true)
-    @XmlElement(name = "product", required = true)
-    private DefaultProduct[] products;
+    @XmlElementWrapper(name = "products", required = false)
+    @XmlElement(type=DefaultProduct.class, name = "product", required = false)
+    private CatalogEntityCollection<Product> products;
 
     @XmlElement(name = "rules", required = true)
     private DefaultPlanRules planRules;
 
-    @XmlElementWrapper(name = "plans", required = true)
-    @XmlElement(name = "plan", required = true)
-    private DefaultPlan[] plans;
+    @XmlElementWrapper(name = "plans", required = false)
+    @XmlElement(type=DefaultPlan.class, name = "plan", required = false)
+    private CatalogEntityCollection<Plan> plans;
 
     @XmlElement(name = "priceLists", required = true)
     private DefaultPriceListSet priceLists;
@@ -91,6 +93,8 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     private URI catalogURI;
 
     public StandaloneCatalog() {
+        this.plans = new CatalogEntityCollection<Plan>();
+        this.products = new CatalogEntityCollection<Product>();
     }
 
     protected StandaloneCatalog(final Date effectiveDate) {
@@ -119,7 +123,12 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
      * @see org.killbill.billing.catalog.ICatalog#getProducts()
      */
     @Override
-    public DefaultProduct[] getCurrentProducts() {
+    public Collection<Product> getCurrentProducts() {
+        return products.getEntries();
+    }
+
+
+    public CatalogEntityCollection<Product>  getCatalogEntityCollectionProduct() {
         return products;
     }
 
@@ -136,11 +145,24 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return supportedCurrencies;
     }
 
+
+
     @Override
-    public DefaultPlan[] getCurrentPlans() {
+    public Collection<Plan> getCurrentPlans() {
+        return plans.getEntries();
+    }
+
+
+    public CatalogEntityCollection<Plan> getCatalogEntityCollectionPlan() {
         return plans;
     }
 
+    public boolean isTemplateCatalog() {
+        return (products == null || products.size() == 0) &&
+               (plans == null || plans.size() == 0) &&
+               (supportedCurrencies == null || supportedCurrencies.length == 0);
+    }
+
     public URI getCatalogURI() {
         return catalogURI;
     }
@@ -161,18 +183,27 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
       * @see org.killbill.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
       */
     @Override
-    public DefaultPlan createOrFindCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
-        if (productName == null) {
-            throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
-        }
-        if (priceLists == null) {
-            throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND, priceListName);
+    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext unused) throws CatalogApiException {
+        final Plan result;
+        if (spec.getPlanName() != null) {
+            result = findCurrentPlan(spec.getPlanName());
+        } else {
+            if (spec.getProductName() == null) {
+                throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
+            }
+            if (spec.getBillingPeriod() == null) {
+                throw new CatalogApiException(ErrorCode.CAT_NULL_BILLING_PERIOD);
+            }
+            final String inputOrDefaultPricelist = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
+            final Product product = findCurrentProduct(spec.getProductName());
+            result = priceLists.getPlanFrom(product, spec.getBillingPeriod(), inputOrDefaultPricelist);
         }
-        final Product product = findCurrentProduct(productName);
-        final DefaultPlan result = priceLists.getPlanFrom(priceListName, product, period);
         if (result == null) {
-            final String periodString = (period == null) ? "NULL" : period.toString();
-            throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, periodString, priceListName);
+            throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND,
+                                          spec.getPlanName() !=  null ? spec.getPlanName() : "undefined",
+                                          spec.getProductName() != null ? spec.getProductName() : "undefined",
+                                          spec.getBillingPeriod() != null ? spec.getBillingPeriod() : "undefined",
+                                          spec.getPriceListName() != null ? spec.getPriceListName() : "undefined");
         }
         return result;
     }
@@ -182,10 +213,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (name == null || plans == null) {
             throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PLAN, name);
         }
-        for (final DefaultPlan p : plans) {
-            if (p.getName().equals(name)) {
-                return p;
-            }
+        final DefaultPlan result = (DefaultPlan) plans.findByName(name);
+        if (result != null) {
+            return result;
         }
         throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PLAN, name);
     }
@@ -195,10 +225,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (name == null || products == null) {
             throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PRODUCT, name);
         }
-        for (final DefaultProduct p : products) {
-            if (p.getName().equals(name)) {
-                return p;
-            }
+        final Product result = products.findByName(name);
+        if (result != null) {
+            return result;
         }
         throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PRODUCT, name);
     }
@@ -208,7 +237,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (name == null || plans == null) {
             throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PHASE, name);
         }
-
         final String planName = DefaultPlanPhase.planName(name);
         final Plan plan = findCurrentPlan(planName);
         return plan.findPhase(name);
@@ -220,7 +248,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (name == null || priceLists == null) {
             throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND, name);
         }
-
         return priceLists.findPriceListFrom(name);
     }
 
@@ -262,8 +289,8 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        validateCollection(catalog, errors, products);
-        validateCollection(catalog, errors, plans);
+        validateCollection(catalog, errors, (DefaultProduct[])  products.toArray(new DefaultProduct[products.size()]));
+        validateCollection(catalog, errors, (DefaultPlan[])  plans.toArray(new DefaultPlan[plans.size()]));
         priceLists.validate(catalog, errors);
         planRules.validate(catalog, errors);
         return errors;
@@ -275,15 +302,15 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         super.initialize(catalog, sourceURI);
         planRules.initialize(catalog, sourceURI);
         priceLists.initialize(catalog, sourceURI);
-        for (final DefaultProduct p : products) {
-            p.initialize(catalog, sourceURI);
+        for (final Product p : products.getEntries()) {
+            ((DefaultProduct)p).initialize(catalog, sourceURI);
         }
-        for (final DefaultPlan p : plans) {
-            p.initialize(catalog, sourceURI);
+        for (final Plan p : plans.getEntries()) {
+            ((DefaultPlan) p).initialize(catalog, sourceURI);
         }
-
     }
 
+
     //////////////////////////////////////////////////////////////////////////////
     //
     // UNIT LIMIT
@@ -296,8 +323,8 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return phase.compliesWithLimits(unit, value);
     }
 
-    public StandaloneCatalog setProducts(final DefaultProduct[] products) {
-        this.products = products;
+    public StandaloneCatalog setProducts(final Iterable<Product> products) {
+        this.products = new CatalogEntityCollection<Product>(products);
         return this;
     }
 
@@ -306,8 +333,8 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         return this;
     }
 
-    public StandaloneCatalog setPlans(final DefaultPlan[] plans) {
-        this.plans = plans;
+    public StandaloneCatalog setPlans(final Iterable<Plan> plans) {
+        this.plans = new CatalogEntityCollection<Plan>(plans);
         return this;
     }
 
@@ -344,7 +371,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     @Override
     public boolean canCreatePlan(final PlanSpecifier specifier) throws CatalogApiException {
         final Product product = findCurrentProduct(specifier.getProductName());
-        final Plan plan = createOrFindCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName(), null);
+        final Plan plan = createOrFindCurrentPlan(specifier, null);
         final DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
 
         return (product != null) &&
@@ -363,9 +390,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
                     for (BillingPeriod billingPeriod : BillingPeriod.values()) {
                         for (PriceList priceList : getPriceLists().getAllPriceLists()) {
                             if (priceListName == null || priceListName.equals(priceList.getName())) {
-                                Plan addonInList = priceList.findPlan(availAddon, billingPeriod);
-                                if ((addonInList != null)) {
-                                    availAddons.add(new DefaultListing(addonInList, priceList));
+                                Collection<Plan> addonInList = priceList.findPlans(availAddon, billingPeriod);
+                                for (Plan cur : addonInList) {
+                                    availAddons.add(new DefaultListing(cur, priceList));
                                 }
                             }
                         }
@@ -375,7 +402,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         } catch (CatalogApiException e) {
             // No such product - just return an empty list
         }
-
         return availAddons;
     }
 
@@ -421,13 +447,13 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (planRules != null ? !planRules.equals(that.planRules) : that.planRules != null) {
             return false;
         }
-        if (!Arrays.equals(plans, that.plans)) {
+        if (!plans.equals(that.plans)) {
             return false;
         }
         if (priceLists != null ? !priceLists.equals(that.priceLists) : that.priceLists != null) {
             return false;
         }
-        if (!Arrays.equals(products, that.products)) {
+        if (!products.equals(that.products)) {
             return false;
         }
         if (recurringBillingMode != that.recurringBillingMode) {
@@ -439,7 +465,6 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         if (!Arrays.equals(units, that.units)) {
             return false;
         }
-
         return true;
     }
 
@@ -450,9 +475,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
         result = 31 * result + (recurringBillingMode != null ? recurringBillingMode.hashCode() : 0);
         result = 31 * result + (supportedCurrencies != null ? Arrays.hashCode(supportedCurrencies) : 0);
         result = 31 * result + (units != null ? Arrays.hashCode(units) : 0);
-        result = 31 * result + (products != null ? Arrays.hashCode(products) : 0);
+        result = 31 * result + (products != null ? products.hashCode() : 0);
         result = 31 * result + (planRules != null ? planRules.hashCode() : 0);
-        result = 31 * result + (plans != null ? Arrays.hashCode(plans) : 0);
+        result = 31 * result + (plans != null ? plans.hashCode() : 0);
         result = 31 * result + (priceLists != null ? priceLists.hashCode() : 0);
         result = 31 * result + (catalogURI != null ? catalogURI.hashCode() : 0);
         return result;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index d80bbd4..8a42e4b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -17,45 +17,25 @@
 
 package org.killbill.billing.catalog;
 
-import java.net.URI;
-import java.util.Date;
-import java.util.List;
 import java.util.regex.Matcher;
 
-import javax.annotation.Nullable;
-
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingAlignment;
-import org.killbill.billing.catalog.api.BillingMode;
-import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.Listing;
 import org.killbill.billing.catalog.api.Plan;
-import org.killbill.billing.catalog.api.PlanAlignmentChange;
-import org.killbill.billing.catalog.api.PlanAlignmentCreate;
-import org.killbill.billing.catalog.api.PlanChangeResult;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
-import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.StaticCatalog;
-import org.killbill.billing.catalog.api.Unit;
 import org.killbill.billing.catalog.override.DefaultPriceOverride;
 import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.xmlloader.ValidatingConfig;
-import org.killbill.xmlloader.ValidationErrors;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
-public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements StaticCatalog {
+public class StandaloneCatalogWithPriceOverride extends StandaloneCatalog implements StaticCatalog {
 
-    private final StandaloneCatalog standaloneCatalog;
     private final Long tenantRecordId;
 
     /* Since we offer endpoints that attempt to serialize catalog objects, we need to explicitly tell Jackson to ignore those fields */
@@ -64,28 +44,22 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
     @JsonIgnore
     private final PriceOverride priceOverride;
 
-    public StandaloneCatalogWithPriceOverride(final StandaloneCatalog staticCatalog, final PriceOverride priceOverride, final Long tenantRecordId, final InternalCallContextFactory internalCallContextFactory) {
+    public StandaloneCatalogWithPriceOverride(final StandaloneCatalog catalog, final PriceOverride priceOverride, final Long tenantRecordId, final InternalCallContextFactory internalCallContextFactory) {
+        // Initialize from input catalog
+        setCatalogName(catalog.getCatalogName());
+        setEffectiveDate(catalog.getEffectiveDate());
+        setRecurringBillingMode(catalog.getRecurringBillingMode());
+        setProducts(catalog.getCurrentProducts());
+        setPlans(catalog.getCurrentPlans());
+        setPriceLists(catalog.getPriceLists());
+        setPlanRules(catalog.getPlanRules());
+        setSupportedCurrencies(catalog.getCurrentSupportedCurrencies());
+        setUnits(catalog.getCurrentUnits());
         this.tenantRecordId = tenantRecordId;
-        this.standaloneCatalog = staticCatalog;
         this.priceOverride = priceOverride;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    public StandaloneCatalogWithPriceOverride(final StandaloneCatalogWithPriceOverride cur, final InternalTenantContext tenantContext) {
-        this.tenantRecordId = tenantContext.getTenantRecordId();
-        this.priceOverride = cur.getPriceOverride();
-        this.standaloneCatalog = cur.getStandaloneCatalog();
-        this.internalCallContextFactory = cur.getInternalCallContextFactory();
-    }
-
-    public StandaloneCatalog getStandaloneCatalog() {
-        return standaloneCatalog;
-    }
-
-    public PriceOverride getPriceOverride() {
-        return priceOverride;
-    }
-
     public Long getTenantRecordId() {
         return tenantRecordId;
     }
@@ -94,68 +68,33 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         return internalCallContextFactory;
     }
 
-    @Override
-    public String getCatalogName() {
-        return standaloneCatalog.getCatalogName();
-    }
-
-    @Override
-    public BillingMode getRecurringBillingMode() {
-        return standaloneCatalog.getRecurringBillingMode();
-    }
-
-    @Override
-    public Date getEffectiveDate() {
-        return standaloneCatalog.getEffectiveDate();
-    }
-
-    @Override
-    public Currency[] getCurrentSupportedCurrencies() throws CatalogApiException {
-        return standaloneCatalog.getCurrentSupportedCurrencies();
-    }
 
     @Override
-    public DefaultProduct[] getCurrentProducts() throws CatalogApiException {
-        return standaloneCatalog.getCurrentProducts();
-    }
-
-    @Override
-    public Unit[] getCurrentUnits() throws CatalogApiException {
-        return standaloneCatalog.getCurrentUnits();
-    }
-
-    @Override
-    public DefaultPlan[] getCurrentPlans() throws CatalogApiException {
-        return standaloneCatalog.getCurrentPlans();
-    }
-
-    @Override
-    public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod period, final String priceListName, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
-        final Plan defaultPlan = standaloneCatalog.createOrFindCurrentPlan(productName, period, priceListName, null);
-
+    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+        final Plan defaultPlan = super.createOrFindCurrentPlan(spec, null);
         if (overrides == null ||
             overrides.getOverrides() == null ||
             overrides.getOverrides().isEmpty()) {
             return defaultPlan;
         }
 
-        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(overrides.getCallContext());
+        final InternalCallContext internalCallContext = overrides.getCallContext() != null ? internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(overrides.getCallContext()) : null;
         return priceOverride.getOrCreateOverriddenPlan(defaultPlan, CatalogDateHelper.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
     }
 
     @Override
-    public Plan findCurrentPlan(final String planName) throws CatalogApiException {
+    public DefaultPlan findCurrentPlan(final String planName) throws CatalogApiException {
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
             final InternalTenantContext internalTenantContext = createInternalTenantContext();
-            return priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+            return priceOverride.getOverriddenPlan(planName, this, internalTenantContext);
         }
-        return standaloneCatalog.findCurrentPlan(planName);
+        return super.findCurrentPlan(planName);
     }
 
     @Override
     public Product findCurrentProduct(final String productName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentProduct(productName);
+        return super.findCurrentProduct(productName);
     }
 
     @Override
@@ -164,79 +103,10 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
             final InternalTenantContext internalTenantContext = createInternalTenantContext();
-            final Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+            final Plan plan = priceOverride.getOverriddenPlan(planName, this, internalTenantContext);
             return plan.findPhase(phaseName);
         }
-        return standaloneCatalog.findCurrentPhase(phaseName);
-    }
-
-    @Override
-    public PriceList findCurrentPricelist(final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentPricelist(priceListName);
-    }
-
-    @Override
-    public BillingActionPolicy planChangePolicy(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChangePolicy(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public PlanChangeResult planChange(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChange(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planCancelPolicy(planPhaseSpecifier);
-    }
-
-    @Override
-    public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planCreateAlignment(planSpecifier);
-    }
-
-    @Override
-    public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
-        return standaloneCatalog.billingAlignment(planPhaseSpecifier);
-    }
-
-    @Override
-    public PlanAlignmentChange planChangeAlignment(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChangeAlignment(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public boolean canCreatePlan(final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.canCreatePlan(planSpecifier);
-    }
-
-    @Override
-    public List<Listing> getAvailableBasePlanListings() throws CatalogApiException {
-        return standaloneCatalog.getAvailableBasePlanListings();
-    }
-
-    @Override
-    public List<Listing> getAvailableAddOnListings(final String baseProductName, @Nullable final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.getAvailableAddOnListings(baseProductName, priceListName);
-    }
-
-    @Override
-    public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
-        return standaloneCatalog.compliesWithLimits(phaseName, unit, value);
-    }
-
-    @Override
-    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride root, final ValidationErrors errors) {
-        return standaloneCatalog.validate(root.standaloneCatalog, errors);
-    }
-
-    @Override
-    public void initialize(final StandaloneCatalogWithPriceOverride root, final URI sourceURI) {
-        standaloneCatalog.initialize(root.standaloneCatalog, sourceURI);
-    }
-
-    public DefaultPriceList findCurrentPriceList(final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentPriceList(priceListName);
+        return super.findCurrentPhase(phaseName);
     }
 
     private InternalTenantContext createInternalTenantContext() {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index b4a222a..83f07ac 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -20,6 +20,7 @@ package org.killbill.billing.catalog;
 
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
@@ -30,15 +31,14 @@ import javax.annotation.Nullable;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingAlignment;
 import org.killbill.billing.catalog.api.BillingMode;
-import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
@@ -60,47 +60,38 @@ import org.killbill.clock.Clock;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
 
-@XmlRootElement(name = "catalog")
+@XmlRootElement(name = "catalogs")
 @XmlAccessorType(XmlAccessType.NONE)
-public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
+public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> implements Catalog, StaticCatalog {
 
     private final Clock clock;
-    @XmlElement(name = "catalogVersion", required = true)
-    private final List<StandaloneCatalogWithPriceOverride> versions;
+
+    @XmlElementWrapper(name = "versions", required = true)
+    @XmlElement(name = "version", required = true)
+    private final List<StandaloneCatalog> versions;
     private String catalogName;
     private BillingMode recurringBillingMode;
 
     // Required for JAXB deserialization
     public VersionedCatalog() {
         this.clock = null;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.versions = new ArrayList<StandaloneCatalog>();
     }
 
     public VersionedCatalog(final Clock clock) {
         this.clock = clock;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
-    }
-
-    public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
-        this.clock = clock;
-        this.catalogName = catalogName;
-        this.recurringBillingMode = recurringBillingMode;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
-        for (final StandaloneCatalogWithPriceOverride cur : versions) {
-            final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
-            this.versions.add(catalogWithTenantInfo);
-        }
+        this.versions = new ArrayList<StandaloneCatalog>();
     }
 
     //
     // Private methods
     //
-    private StandaloneCatalogWithPriceOverride versionForDate(final DateTime date) throws CatalogApiException {
+    private StandaloneCatalog versionForDate(final DateTime date) throws CatalogApiException {
         return versions.get(indexOfVersionForDate(date.toDate()));
     }
 
-    private List<StandaloneCatalogWithPriceOverride> versionsBeforeDate(final Date date) throws CatalogApiException {
-        final List<StandaloneCatalogWithPriceOverride> result = new ArrayList<StandaloneCatalogWithPriceOverride>();
+    private List<StandaloneCatalog> versionsBeforeDate(final Date date) throws CatalogApiException {
+        final List<StandaloneCatalog> result = new ArrayList<StandaloneCatalog>();
         final int index = indexOfVersionForDate(date);
         for (int i = 0; i <= index; i++) {
             result.add(versions.get(i));
@@ -110,7 +101,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     private int indexOfVersionForDate(final Date date) throws CatalogApiException {
         for (int i = versions.size() - 1; i >= 0; i--) {
-            final StandaloneCatalogWithPriceOverride c = versions.get(i);
+            final StandaloneCatalog c = versions.get(i);
             if (c.getEffectiveDate().getTime() <= date.getTime()) {
                 return i;
             }
@@ -120,44 +111,41 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     private class PlanRequestWrapper {
 
-        String name;
-        String productName;
-        BillingPeriod bp;
-        String priceListName;
-        PlanPhasePriceOverridesWithCallContext overrides;
+        private final PlanSpecifier spec;
+        private final PlanPhasePriceOverridesWithCallContext overrides;
 
-        public PlanRequestWrapper(final String name) {
-            this.name = name;
+        public PlanRequestWrapper(final String planName) {
+            this.spec = new PlanSpecifier(planName);
+            this.overrides = null;
         }
 
-        public PlanRequestWrapper(final String productName, final BillingPeriod bp,
-                                  final String priceListName, final PlanPhasePriceOverridesWithCallContext overrides) {
-            this.productName = productName;
-            this.bp = bp;
-            this.priceListName = priceListName;
+        public PlanRequestWrapper(final PlanSpecifier spec,
+                                  final PlanPhasePriceOverridesWithCallContext overrides) {
+            this.spec = spec;
             this.overrides = overrides;
         }
 
-        public Plan findPlan(final StandaloneCatalogWithPriceOverride catalog) throws CatalogApiException {
-            if (name != null) {
-                return catalog.findCurrentPlan(name);
-            } else {
-                return catalog.createOrFindCurrentPlan(productName, bp, priceListName, overrides);
-            }
+
+        public Plan findPlan(final StandaloneCatalog catalog) throws CatalogApiException {
+            return catalog.createOrFindCurrentPlan(spec, overrides);
+        }
+
+        public PlanSpecifier getSpec() {
+            return spec;
         }
     }
 
-    private Plan findPlan(final PlanRequestWrapper wrapper,
-                          final DateTime requestedDate,
-                          final DateTime subscriptionStartDate)
+    private CatalogPlanEntry findCatalogPlanEntry(final PlanRequestWrapper wrapper,
+                                                  final DateTime requestedDate,
+                                                  final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
+        final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
         if (catalogs.isEmpty()) {
             throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
         }
 
         for (int i = catalogs.size() - 1; i >= 0; i--) { // Working backwards to find the latest applicable plan
-            final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
+            final StandaloneCatalog c = catalogs.get(i);
             final Plan plan;
             try {
                 plan = wrapper.findPlan(c);
@@ -172,32 +160,63 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
             final DateTime catalogEffectiveDate = CatalogDateHelper.toUTCDateTime(c.getEffectiveDate());
             if (!subscriptionStartDate.isBefore(catalogEffectiveDate)) { // Its a new subscription this plan always applies
-                return plan;
+                return new CatalogPlanEntry(c, plan);
             } else { //Its an existing subscription
-                if (plan.getEffectiveDateForExistingSubscriptons() != null) { //if it is null any change to this does not apply to existing subscriptions
-                    final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptons());
+                if (plan.getEffectiveDateForExistingSubscriptions() != null) { //if it is null any change to this does not apply to existing subscriptions
+                    final DateTime existingSubscriptionDate = CatalogDateHelper.toUTCDateTime(plan.getEffectiveDateForExistingSubscriptions());
                     if (requestedDate.isAfter(existingSubscriptionDate)) { // this plan is now applicable to existing subs
-                        return plan;
+                        return new CatalogPlanEntry(c, plan);
                     }
                 }
             }
         }
 
-        throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
+        final PlanSpecifier spec = wrapper.getSpec();
+        throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND,
+                                      spec.getPlanName() != null ? spec.getPlanName() : "undefined",
+                                      spec.getProductName() != null ? spec.getProductName() : "undefined",
+                                      spec.getBillingPeriod() != null ? spec.getBillingPeriod() : "undefined",
+                                      spec.getPriceListName() != null ? spec.getPriceListName() : "undefined");
     }
 
+    private static class CatalogPlanEntry {
+
+        private final StaticCatalog staticCatalog;
+        private final Plan plan;
+
+        public CatalogPlanEntry(final StaticCatalog staticCatalog, final Plan plan) {
+            this.staticCatalog = staticCatalog;
+            this.plan = plan;
+        }
+
+        public StaticCatalog getStaticCatalog() {
+            return staticCatalog;
+        }
+
+        public Plan getPlan() {
+            return plan;
+        }
+    }
+
+
     public Clock getClock() {
         return clock;
     }
 
-    public List<StandaloneCatalogWithPriceOverride> getVersions() {
+    public List<StandaloneCatalog> getVersions() {
         return versions;
     }
 
     //
     // Public methods not exposed in interface
     //
-    public void add(final StandaloneCatalogWithPriceOverride e) throws CatalogApiException {
+    public void addAll(final List<StandaloneCatalog> inputVersions) throws CatalogApiException {
+        for (final StandaloneCatalog cur : inputVersions) {
+            add(cur);
+        }
+    }
+
+    public void add(final StandaloneCatalog e) throws CatalogApiException {
         if (catalogName == null) {
             catalogName = e.getCatalogName();
         } else {
@@ -213,15 +232,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
             }
         }
         versions.add(e);
-        Collections.sort(versions, new Comparator<StandaloneCatalogWithPriceOverride>() {
+        Collections.sort(versions, new Comparator<StandaloneCatalog>() {
             @Override
-            public int compare(final StandaloneCatalogWithPriceOverride c1, final StandaloneCatalogWithPriceOverride c2) {
+            public int compare(final StandaloneCatalog c1, final StandaloneCatalog c2) {
                 return c1.getEffectiveDate().compareTo(c2.getEffectiveDate());
             }
         });
     }
 
-    public Iterator<StandaloneCatalogWithPriceOverride> iterator() {
+    public Iterator<StandaloneCatalog> iterator() {
         return versions.iterator();
     }
 
@@ -238,7 +257,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public DefaultProduct[] getProducts(final DateTime requestedDate) throws CatalogApiException {
+    public Collection<Product> getProducts(final DateTime requestedDate) throws CatalogApiException {
         return versionForDate(requestedDate).getCurrentProducts();
     }
 
@@ -248,13 +267,13 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public DefaultPlan[] getPlans(final DateTime requestedDate) throws CatalogApiException {
+    public Collection<Plan> getPlans(final DateTime requestedDate) throws CatalogApiException {
         return versionForDate(requestedDate).getCurrentPlans();
     }
 
     @Override
     public PriceListSet getPriceLists(final DateTime requestedDate) throws CatalogApiException {
-        return versionForDate(requestedDate).getStandaloneCatalog().getPriceLists();
+        return versionForDate(requestedDate).getPriceLists();
     }
 
     //
@@ -268,13 +287,11 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName,
-                                 final BillingPeriod term,
-                                 final String priceListName,
+    public Plan createOrFindPlan(final PlanSpecifier spec,
                                  final PlanPhasePriceOverridesWithCallContext overrides,
                                  final DateTime requestedDate)
             throws CatalogApiException {
-        return versionForDate(requestedDate).createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return versionForDate(requestedDate).createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
@@ -282,18 +299,18 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
                          final DateTime requestedDate,
                          final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        return findPlan(new PlanRequestWrapper(name), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(name), requestedDate, subscriptionStartDate);
+        return entry.getPlan();
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName,
-                                 final BillingPeriod term,
-                                 final String priceListName,
+    public Plan createOrFindPlan(final PlanSpecifier spec,
                                  final PlanPhasePriceOverridesWithCallContext overrides,
                                  final DateTime requestedDate,
                                  final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        return findPlan(new PlanRequestWrapper(productName, term, priceListName, overrides), requestedDate, subscriptionStartDate);
+        final CatalogPlanEntry entry =  findCatalogPlanEntry(new PlanRequestWrapper(spec, overrides), requestedDate, subscriptionStartDate);
+        return entry.getPlan();
     }
 
     //
@@ -318,14 +335,24 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     //
-    // Find a price list
+    // Find a price list associated to a given subscription
     //
     @Override
+    public PriceList findPriceListForPlan(final String planName,
+                                          final DateTime requestedDate,
+                                          final DateTime subscriptionStartDate)
+            throws CatalogApiException {
+        final CatalogPlanEntry entry = findCatalogPlanEntry(new PlanRequestWrapper(planName), requestedDate, subscriptionStartDate);
+        return entry.getStaticCatalog().findCurrentPricelist(entry.getPlan().getPriceListName());
+    }
+
+
     public PriceList findPriceList(final String name, final DateTime requestedDate)
             throws CatalogApiException {
         return versionForDate(requestedDate).findCurrentPriceList(name);
     }
 
+
     //
     // Rules
     //
@@ -372,15 +399,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     // VerifiableConfig API
     //
     @Override
-    public void initialize(final StandaloneCatalogWithPriceOverride catalog, final URI sourceURI) {
-        for (final StandaloneCatalogWithPriceOverride c : versions) {
-            c.initialize(catalog, sourceURI);
+    public void initialize(final VersionedCatalog catalog, final URI sourceURI) {
+        for (final StandaloneCatalog c : versions) {
+            c.initialize(c, sourceURI);
         }
     }
 
     @Override
-    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride catalog, final ValidationErrors errors) {
-        for (final StandaloneCatalogWithPriceOverride c : versions) {
+    public ValidationErrors validate(final VersionedCatalog catalog, final ValidationErrors errors) {
+        for (final StandaloneCatalog c : versions) {
             errors.addAll(c.validate(c, errors));
         }
         //TODO MDW validation - ensure all catalog versions have a single name
@@ -416,7 +443,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Product[] getCurrentProducts() throws CatalogApiException {
+    public Collection<Product> getCurrentProducts() throws CatalogApiException {
         return versionForDate(clock.getUTCNow()).getCurrentProducts();
     }
 
@@ -426,14 +453,13 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     }
 
     @Override
-    public Plan[] getCurrentPlans() throws CatalogApiException {
+    public Collection<Plan> getCurrentPlans() throws CatalogApiException {
         return versionForDate(clock.getUTCNow()).getCurrentPlans();
     }
 
     @Override
-    public Plan createOrFindCurrentPlan(final String productName, final BillingPeriod term,
-                                        final String priceList, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
-        return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(productName, term, priceList, overrides);
+    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+        return versionForDate(clock.getUTCNow()).createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
index 4e771ce..dca3946 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -29,9 +30,11 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
 import org.killbill.billing.catalog.DefaultProduct;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
-import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.xmlloader.UriAccessor;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
@@ -70,7 +73,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
     @Test(groups = "fast")
     public void testMissingDefaultCatalog() throws CatalogApiException {
         catalogCache.loadDefaultCatalog(null);
-        Assert.assertEquals(catalogCache.getCatalog(internalCallContext).getCatalogName(), "EmptyCatalog");
+        Assert.assertEquals(catalogCache.getCatalog(true, true, internalCallContext).getCatalogName(), "EmptyCatalog");
     }
 
     //
@@ -80,17 +83,21 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
     public void testDefaultCatalog() throws CatalogApiException {
         catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
 
-        final VersionedCatalog result = catalogCache.getCatalog(internalCallContext);
+        final VersionedCatalog result = catalogCache.getCatalog(true, true, internalCallContext);
         Assert.assertNotNull(result);
-        final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
-        Assert.assertEquals(products.length, 3);
+        final Collection<Product> products = result.getProducts(clock.getUTCNow());
+        Assert.assertEquals(products.size(), 3);
 
         // Verify the lookup with other contexts
-        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock(), result.getCatalogName(), result.getRecurringBillingMode(), result.getVersions(), multiTenantContext);
-        Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
-        Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
-        for (int i = 0; i < catalogCache.getCatalog(multiTenantContext).getVersions().size(); i++) {
-            Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().get(i).getTenantRecordId(), resultForMultiTenantContext.getVersions().get(i).getTenantRecordId());
+        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock());
+        for (final StandaloneCatalog cur : result.getVersions()) {
+            resultForMultiTenantContext.add(new StandaloneCatalogWithPriceOverride(cur, priceOverride, multiTenantContext.getTenantRecordId(), internalCallContextFactory));
+        }
+
+        Assert.assertEquals(catalogCache.getCatalog(true, true, multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
+        Assert.assertEquals(catalogCache.getCatalog(true, true, multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
+        for (int i = 0; i < catalogCache.getCatalog(true, true, multiTenantContext).getVersions().size(); i++) {
+           Assert.assertEquals(((StandaloneCatalogWithPriceOverride) catalogCache.getCatalog(true, true, multiTenantContext).getVersions().get(i)).getTenantRecordId(), ((StandaloneCatalogWithPriceOverride) resultForMultiTenantContext.getVersions().get(i)).getTenantRecordId());
         }
     }
 
@@ -131,14 +138,14 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         });
 
         // Verify the lookup for a non-cached tenant. No system config is set yet but EhCacheCatalogCache returns a default empty one
-        VersionedCatalog differentResult = catalogCache.getCatalog(differentMultiTenantContext);
+        VersionedCatalog differentResult = catalogCache.getCatalog(true, true, differentMultiTenantContext);
         Assert.assertNotNull(differentResult);
         Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
 
         // Make sure the cache loader isn't invoked, see https://github.com/killbill/killbill/issues/300
         shouldThrow.set(true);
 
-        differentResult = catalogCache.getCatalog(differentMultiTenantContext);
+        differentResult = catalogCache.getCatalog(true, true, differentMultiTenantContext);
         Assert.assertNotNull(differentResult);
         Assert.assertEquals(differentResult.getCatalogName(), "EmptyCatalog");
 
@@ -148,30 +155,30 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
 
         // Verify the lookup for this tenant
-        final VersionedCatalog result = catalogCache.getCatalog(multiTenantContext);
+        final VersionedCatalog result = catalogCache.getCatalog(true, true, multiTenantContext);
         Assert.assertNotNull(result);
-        final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
-        Assert.assertEquals(products.length, 6);
+        final Collection<Product> products = result.getProducts(clock.getUTCNow());
+        Assert.assertEquals(products.size(), 6);
 
         // Verify the lookup for another tenant
-        final VersionedCatalog otherResult = catalogCache.getCatalog(otherMultiTenantContext);
+        final VersionedCatalog otherResult = catalogCache.getCatalog(true, true, otherMultiTenantContext);
         Assert.assertNotNull(otherResult);
-        final DefaultProduct[] otherProducts = otherResult.getProducts(clock.getUTCNow());
-        Assert.assertEquals(otherProducts.length, 3);
+        final Collection<Product> otherProducts = otherResult.getProducts(clock.getUTCNow());
+        Assert.assertEquals(otherProducts.size(), 3);
 
         shouldThrow.set(true);
 
         // Verify the lookup for this tenant
-        final VersionedCatalog result2 = catalogCache.getCatalog(multiTenantContext);
+        final VersionedCatalog result2 = catalogCache.getCatalog(true, true, multiTenantContext);
         Assert.assertEquals(result2, result);
 
         // Verify the lookup with another context for the same tenant
         final InternalCallContext sameMultiTenantContext = Mockito.mock(InternalCallContext.class);
         Mockito.when(sameMultiTenantContext.getAccountRecordId()).thenReturn(9102L);
         Mockito.when(sameMultiTenantContext.getTenantRecordId()).thenReturn(multiTenantRecordId);
-        Assert.assertEquals(catalogCache.getCatalog(sameMultiTenantContext), result);
+        Assert.assertEquals(catalogCache.getCatalog(true, true, sameMultiTenantContext), result);
 
         // Verify the lookup with the other tenant
-        Assert.assertEquals(catalogCache.getCatalog(otherMultiTenantContext), otherResult);
+        Assert.assertEquals(catalogCache.getCatalog(true, true, otherMultiTenantContext), otherResult);
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
index 9b84483..3109de4 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
@@ -21,6 +21,7 @@ import org.killbill.billing.catalog.caching.CatalogCache;
 import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.glue.TestCatalogModuleNoDB;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.testng.annotations.BeforeClass;
@@ -46,6 +47,9 @@ public abstract class CatalogTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected CatalogCacheInvalidationCallback cacheInvalidationCallback;
 
+    @Inject
+    protected PriceOverride priceOverride;
+
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
         final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB(configSource));
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
index e3bef69..182bd7f 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/glue/TestCatalogModule.java
@@ -24,6 +24,7 @@ import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
 import org.killbill.billing.mock.glue.MockTenantModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.CacheModule;
+import org.killbill.billing.util.glue.ConfigModule;
 
 public class TestCatalogModule extends CatalogModule {
 
@@ -36,6 +37,7 @@ public class TestCatalogModule extends CatalogModule {
         super.configure();
         install(new MockNonEntityDaoModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new MockTenantModule(configSource));
         install(new MockAccountModule(configSource));
     }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
index ef0d645..f5513d6 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
@@ -32,6 +32,7 @@ import javax.xml.transform.TransformerException;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -120,7 +121,7 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
     public void testLoad() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, CatalogApiException {
         final VersionedCatalog c = loader.loadDefaultCatalog(Resources.getResource("versionedCatalog").toString());
         Assert.assertEquals(c.size(), 3);
-        final Iterator<StandaloneCatalogWithPriceOverride> it = c.iterator();
+        final Iterator<StandaloneCatalog> it = c.iterator();
         DateTime dt = new DateTime("2011-01-01T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
         dt = new DateTime("2011-02-02T00:00:00+00:00");
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
new file mode 100644
index 0000000..b431e9a
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog.io;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
+import org.killbill.billing.catalog.DefaultDuration;
+import org.killbill.billing.catalog.DefaultFixed;
+import org.killbill.billing.catalog.DefaultInternationalPrice;
+import org.killbill.billing.catalog.DefaultMutableStaticCatalog;
+import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.DefaultPlanPhase;
+import org.killbill.billing.catalog.DefaultPrice;
+import org.killbill.billing.catalog.DefaultPriceListSet;
+import org.killbill.billing.catalog.DefaultProduct;
+import org.killbill.billing.catalog.DefaultRecurring;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.xmlloader.XMLLoader;
+import org.killbill.xmlloader.XMLWriter;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestXMLWriter extends CatalogTestSuiteNoDB {
+
+
+    // Verifies we can generate the XML associated with a VersionedCatalog
+    @Test(groups = "fast")
+    public void testVersionedCatalog() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final VersionedCatalog versionedCatalog = new VersionedCatalog(clock);
+        versionedCatalog.add(catalog);
+        final String newCatalogStr = XMLWriter.writeXML(versionedCatalog, VersionedCatalog.class);
+        //System.err.println(newCatalogStr);
+    }
+
+
+    // Verify we can marshall/unmarshall a (fairly complex catalog) catalog and get back the same result (Required to support catalog update)
+    @Test(groups = "fast")
+    public void testMarshallUnmarshall() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final String oldCatalogStr = XMLWriter.writeXML(catalog, StandaloneCatalog.class);
+        //System.err.println(oldCatalogStr);
+
+        final StandaloneCatalog oldCatalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(oldCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+        final String oldCatalogStr2 = XMLWriter.writeXML(oldCatalog, StandaloneCatalog.class);
+        assertEquals(oldCatalogStr2, oldCatalogStr);
+    }
+
+
+
+
+    @Test(groups = "fast")
+    public void testAddPlan() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+
+        final MutableStaticCatalog mutableCatalog = new DefaultMutableStaticCatalog(catalog);
+
+        final DefaultProduct newProduct = new DefaultProduct();
+        newProduct.setName("Dynamic");
+        newProduct.setCatagory(ProductCategory.BASE);
+        newProduct.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+        mutableCatalog.addProduct(newProduct);
+
+        final DefaultPlanPhase trialPhase = new DefaultPlanPhase();
+        trialPhase.setPhaseType(PhaseType.TRIAL);
+        trialPhase.setDuration(new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(14));
+        trialPhase.setFixed(new DefaultFixed().setFixedPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.ZERO)})));
+
+        final DefaultPlanPhase evergreenPhase = new DefaultPlanPhase();
+        evergreenPhase.setPhaseType(PhaseType.EVERGREEN);
+        evergreenPhase.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(1));
+        evergreenPhase.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        final DefaultPlan newPlan = new DefaultPlan();
+        newPlan.setName("dynamic-monthly");
+        newPlan.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan.setProduct(newProduct);
+        newPlan.setInitialPhases(new DefaultPlanPhase[]{trialPhase});
+        newPlan.setFinalPhase(evergreenPhase);
+        // TODO Ordering breaks
+        mutableCatalog.addPlan(newPlan);
+        newPlan.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+        final String newCatalogStr = XMLWriter.writeXML((StandaloneCatalog) mutableCatalog, StandaloneCatalog.class);
+        final StandaloneCatalog newCatalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(newCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+        assertEquals(newCatalog.getCurrentPlans().size(), catalog.getCurrentPlans().size() + 1);
+
+        final Plan plan = newCatalog.findCurrentPlan("dynamic-monthly");
+        assertEquals(plan.getName(), "dynamic-monthly");
+        assertEquals(plan.getPriceListName(), DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        assertEquals(plan.getProduct().getName(), "Dynamic");
+        assertEquals(plan.getProduct().getCategory(), ProductCategory.BASE);
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getName(), "dynamic-monthly-trial");
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getCurrency(), Currency.USD);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getValue(), BigDecimal.ZERO);
+
+        assertEquals(plan.getFinalPhase().getName(), "dynamic-monthly-evergreen");
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.USD);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+    }
+
+}
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
index f4a20e3..5c788a0 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalog.java
@@ -16,15 +16,19 @@
 
 package org.killbill.billing.catalog;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingAlignment;
-import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogEntity;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanAlignmentChange;
@@ -43,6 +47,8 @@ import org.killbill.billing.catalog.rules.DefaultCaseChangePlanPolicy;
 import org.killbill.billing.catalog.rules.DefaultCaseCreateAlignment;
 import org.killbill.billing.catalog.rules.DefaultPlanRules;
 
+import com.google.common.collect.ImmutableList;
+
 public class MockCatalog extends StandaloneCatalog implements Catalog {
 
     private static final String[] PRODUCT_NAMES = new String[]{"TestProduct1", "TestProduct2", "TestProduct3"};
@@ -54,7 +60,7 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     public MockCatalog() {
         setEffectiveDate(new Date());
         setProducts(MockProduct.createAll());
-        setPlans((DefaultPlan[]) MockPlan.createAll());
+        setPlans(MockPlan.createAll());
         populateRules();
         populatePriceLists();
     }
@@ -73,14 +79,19 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     public void populatePriceLists() {
-        final DefaultPlan[] plans = getCurrentPlans();
-
-        final DefaultPriceList[] priceList = new DefaultPriceList[plans.length - 1];
-        for (int i = 1; i < plans.length; i++) {
-            priceList[i - 1] = new DefaultPriceList(new DefaultPlan[]{plans[i]}, plans[i].getName() + "-pl");
+        final Collection<Plan> plans = getCurrentPlans();
+
+        final DefaultPriceList[] priceList = new DefaultPriceList[plans.size() - 1];
+        int i = 1;
+        final Iterator<Plan> it = plans.iterator();
+        final Plan initialPlan = it.next();
+        while (it.hasNext()) {
+            final Plan plan = it.next();
+            priceList[i - 1] = new DefaultPriceList(new DefaultPlan[] { (DefaultPlan) plan}, plan.getName() + "-pl");
+            i++;
         }
 
-        final DefaultPriceListSet set = new DefaultPriceListSet(new PriceListDefault(new DefaultPlan[]{plans[0]}), priceList);
+        final DefaultPriceListSet set = new DefaultPriceListSet(new PriceListDefault(new DefaultPlan[]{(DefaultPlan) initialPlan}), priceList);
         setPriceLists(set);
     }
 
@@ -99,12 +110,12 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Product[] getProducts(final DateTime requestedDate) throws CatalogApiException {
+    public Collection<Product> getProducts(final DateTime requestedDate) throws CatalogApiException {
         return getCurrentProducts();
     }
 
     @Override
-    public Plan[] getPlans(final DateTime requestedDate) throws CatalogApiException {
+    public Collection<Plan> getPlans(final DateTime requestedDate) throws CatalogApiException {
         return getCurrentPlans();
     }
 
@@ -119,9 +130,9 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName, final BillingPeriod term, final String priceListName, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate)
+    public Plan createOrFindPlan(final PlanSpecifier spec, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate)
             throws CatalogApiException {
-        return createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
@@ -131,9 +142,9 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
-    public Plan createOrFindPlan(final String productName, final BillingPeriod term, final String priceListName, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate,
+    public Plan createOrFindPlan(final PlanSpecifier spec, PlanPhasePriceOverridesWithCallContext overrides, final DateTime requestedDate,
                          final DateTime subscriptionStartDate) throws CatalogApiException {
-        return createOrFindCurrentPlan(productName, term, priceListName, overrides);
+        return createOrFindCurrentPlan(spec, overrides);
     }
 
     @Override
@@ -153,6 +164,11 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
     }
 
     @Override
+    public PriceList findPriceListForPlan(final String name, final DateTime requestedDate, final DateTime subscriptionStartDate) throws CatalogApiException {
+        return findCurrentPricelist(name);
+    }
+
+    @Override
     public BillingActionPolicy planChangePolicy(final PlanPhaseSpecifier from, final PlanSpecifier to, final DateTime requestedDate)
             throws CatalogApiException {
         return planChangePolicy(from, to);
@@ -229,6 +245,23 @@ public class MockCatalog extends StandaloneCatalog implements Catalog {
         return canCreatePlan;
     }
 
+
+    public DefaultProduct getCurrentProduct(int idx) {
+        return (DefaultProduct) getCurrentProducts().toArray()[idx];
+    }
+
+    private <T extends CatalogEntity> void convertCurrentEntries(final Collection<T> unordered, final T [] result) {
+        // Tests are not so well written and make assumption on how such entries are ordered
+        final LinkedList<T> list = new LinkedList<T>(unordered);
+        Collections.sort(list, new Comparator<T>() {
+            @Override
+            public int compare(final T o1, final T o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        list.toArray(result);
+    }
+
     public void setCanCreatePlan(final boolean canCreatePlan) {
         this.canCreatePlan = canCreatePlan;
     }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
index f74e9de..d793793 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogModule.java
@@ -38,8 +38,8 @@ public class MockCatalogModule extends KillBillModule {
 
         final CatalogService catalogService = Mockito.mock(CatalogService.class);
         try {
-            Mockito.when(catalogService.getCurrentCatalog(Mockito.any(InternalCallContext.class))).thenReturn(new MockCatalog());
-            Mockito.when(catalogService.getFullCatalog(Mockito.any(InternalCallContext.class))).thenReturn(catalog);
+            Mockito.when(catalogService.getCurrentCatalog(Mockito.any(Boolean.class), Mockito.any(Boolean.class), Mockito.any(InternalCallContext.class))).thenReturn(new MockCatalog());
+            Mockito.when(catalogService.getFullCatalog(Mockito.any(Boolean.class), Mockito.any(Boolean.class), Mockito.any(InternalCallContext.class))).thenReturn(catalog);
             bind(CatalogService.class).toInstance(catalogService);
         } catch (CatalogApiException e) {
             throw new RuntimeException(e);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
index 41b66fa..64ae687 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
@@ -40,12 +40,12 @@ public class MockCatalogService extends DefaultCatalogService {
     }
 
     @Override
-    public Catalog getFullCatalog(InternalTenantContext context) {
+    public Catalog getFullCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, InternalTenantContext context) {
         return catalog;
     }
 
     @Override
-    public StaticCatalog getCurrentCatalog(InternalTenantContext context) {
+    public StaticCatalog getCurrentCatalog(final boolean useDefaultCatalog, final boolean filterTemplateCatalog, InternalTenantContext context) {
         return catalog;
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockPlan.java b/catalog/src/test/java/org/killbill/billing/catalog/MockPlan.java
index e77c761..54a95ba 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockPlan.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockPlan.java
@@ -16,10 +16,16 @@
 
 package org.killbill.billing.catalog;
 
+import java.util.Collection;
+
+import org.killbill.billing.catalog.api.Plan;
+
+import com.google.common.collect.ImmutableList;
+
 public class MockPlan extends DefaultPlan {
 
     public static MockPlan createBicycleTrialEvergreen1USD(final int trialDurationInDays) {
-        return new MockPlan("BicycleTrialEvergreen1USD",
+        return new MockPlan("1-BicycleTrialEvergreen1USD",
                             MockProduct.createBicycle(),
                             new DefaultPlanPhase[]{MockPlanPhase.createTrial(trialDurationInDays)},
                             MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -27,7 +33,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createBicycleTrialEvergreen1USD() {
-        return new MockPlan("BicycleTrialEvergreen1USD",
+        return new MockPlan("1-BicycleTrialEvergreen1USD",
                             MockProduct.createBicycle(),
                             new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial()},
                             MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -35,7 +41,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createSportsCarTrialEvergreen100USD() {
-        return new MockPlan("SportsCarTrialEvergreen100USD",
+        return new MockPlan("4-SportsCarTrialEvergreen100USD",
                             MockProduct.createSportsCar(),
                             new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial()},
                             MockPlanPhase.createUSDMonthlyEvergreen("100.00", null),
@@ -43,7 +49,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createPickupTrialEvergreen10USD() {
-        return new MockPlan("PickupTrialEvergreen10USD",
+        return new MockPlan("3-PickupTrialEvergreen10USD",
                             MockProduct.createPickup(),
                             new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial()},
                             MockPlanPhase.createUSDMonthlyEvergreen("10.00", null),
@@ -51,7 +57,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createJetTrialEvergreen1000USD() {
-        return new MockPlan("JetTrialEvergreen1000USD",
+        return new MockPlan("5-JetTrialEvergreen1000USD",
                             MockProduct.createJet(),
                             new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial()},
                             MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -59,7 +65,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createJetTrialFixedTermEvergreen1000USD() {
-        return new MockPlan("JetTrialEvergreen1000USD",
+        return new MockPlan("6-JetTrialEvergreen1000USD",
                             MockProduct.createJet(),
                             new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial(), MockPlanPhase.createUSDMonthlyFixedTerm("500.00", null, 6)},
                             MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -67,7 +73,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createHornMonthlyNoTrial1USD() {
-        return new MockPlan("Horn1USD",
+        return new MockPlan("7-Horn1USD",
                             MockProduct.createHorn(),
                             new DefaultPlanPhase[]{},
                             MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -75,7 +81,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public MockPlan() {
-        this("BicycleTrialEvergreen1USD",
+        this("1-BicycleTrialEvergreen1USD",
              MockProduct.createBicycle(),
              new DefaultPlanPhase[]{MockPlanPhase.create30DayTrial()},
              MockPlanPhase.create1USDMonthlyEvergreen(),
@@ -96,7 +102,7 @@ public class MockPlan extends DefaultPlan {
     }
 
     public static MockPlan createBicycleNoTrialEvergreen1USD() {
-        return new MockPlan("BicycleNoTrialEvergreen1USD",
+        return new MockPlan("2-BicycleNoTrialEvergreen1USD",
                             MockProduct.createBicycle(),
                             new DefaultPlanPhase[]{},
                             MockPlanPhase.createUSDMonthlyEvergreen("1.0", null),
@@ -119,16 +125,14 @@ public class MockPlan extends DefaultPlan {
         setPlansAllowedInBundle(1);
     }
 
-    public static DefaultPlan[] createAll() {
-        return new DefaultPlan[]{
-                createBicycleTrialEvergreen1USD(),
-                createBicycleNoTrialEvergreen1USD(),
-                createPickupTrialEvergreen10USD(),
-                createSportsCarTrialEvergreen100USD(),
-                createJetTrialEvergreen1000USD(),
-                createJetTrialFixedTermEvergreen1000USD(),
-                createHornMonthlyNoTrial1USD()
-        };
+    public static Collection<Plan> createAll() {
+        return ImmutableList.<Plan>of(createBicycleTrialEvergreen1USD(),
+                               createBicycleNoTrialEvergreen1USD(),
+                               createPickupTrialEvergreen10USD(),
+                               createSportsCarTrialEvergreen100USD(),
+                               createJetTrialEvergreen1000USD(),
+                               createJetTrialFixedTermEvergreen1000USD(),
+                               createHornMonthlyNoTrial1USD());
     }
 
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockProduct.java b/catalog/src/test/java/org/killbill/billing/catalog/MockProduct.java
index f0a718e..5693004 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockProduct.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockProduct.java
@@ -16,8 +16,13 @@
 
 package org.killbill.billing.catalog;
 
+import java.util.Collection;
+
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 
+import com.google.common.collect.ImmutableList;
+
 public class MockProduct extends DefaultProduct {
 
     public MockProduct() {
@@ -33,23 +38,23 @@ public class MockProduct extends DefaultProduct {
     }
 
     public static MockProduct createBicycle() {
-        return new MockProduct("Bicycle", ProductCategory.BASE, "Vehcles");
+        return new MockProduct("1-Bicycle", ProductCategory.BASE, "Vehcles");
     }
 
     public static MockProduct createPickup() {
-        return new MockProduct("Pickup", ProductCategory.BASE, "Vehcles");
+        return new MockProduct("2-Pickup", ProductCategory.BASE, "Vehcles");
     }
 
     public static MockProduct createSportsCar() {
-        return new MockProduct("SportsCar", ProductCategory.BASE, "Vehcles");
+        return new MockProduct("3-SportsCar", ProductCategory.BASE, "Vehcles");
     }
 
     public static MockProduct createJet() {
-        return new MockProduct("Jet", ProductCategory.BASE, "Vehcles");
+        return new MockProduct("4-Jet", ProductCategory.BASE, "Vehcles");
     }
 
     public static MockProduct createHorn() {
-        return new MockProduct("Horn", ProductCategory.ADD_ON, "Vehcles");
+        return new MockProduct("5-Horn", ProductCategory.ADD_ON, "Vehcles");
     }
 
     public static MockProduct createSpotlight() {
@@ -57,18 +62,17 @@ public class MockProduct extends DefaultProduct {
     }
 
     public static MockProduct createRedPaintJob() {
-        return new MockProduct("RedPaintJob", ProductCategory.ADD_ON, "Vehcles");
+        return new MockProduct("6-RedPaintJob", ProductCategory.ADD_ON, "Vehcles");
     }
 
-    public static DefaultProduct[] createAll() {
-        return new MockProduct[]{
+    public static Collection<Product> createAll() {
+        return ImmutableList.<Product>of(
                 createBicycle(),
                 createPickup(),
                 createSportsCar(),
                 createJet(),
                 createHorn(),
-                createRedPaintJob()
-        };
+                createRedPaintJob());
     }
 
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
index b3f16dd..6f55bcb 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCase.java
@@ -19,6 +19,7 @@ package org.killbill.billing.catalog.rules;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlIDREF;
 
+import org.killbill.billing.catalog.api.Product;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -84,8 +85,8 @@ public class TestCase extends CatalogTestSuiteNoDB {
             return priceList;
         }
 
-        protected DefaultCaseResult setProduct(final DefaultProduct product) {
-            this.product = product;
+        protected DefaultCaseResult setProduct(final Product product) {
+            this.product = (DefaultProduct) product;
             return this;
         }
 
@@ -109,7 +110,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testBasic() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -120,8 +121,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -130,7 +130,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testWildCardProduct() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -142,8 +142,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertion(Result.FOO, cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -152,7 +151,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testWildCardProductCategory() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -164,7 +163,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
@@ -174,7 +173,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testWildCardBillingPeriod() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -186,8 +185,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -196,7 +194,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testWildCardPriceList() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -208,8 +206,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", cat);
     }
@@ -218,7 +215,7 @@ public class TestCase extends CatalogTestSuiteNoDB {
     public void testCaseOrder() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
         final DefaultCaseResult cr0 = new DefaultCaseResult(
@@ -250,21 +247,21 @@ public class TestCase extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3},
-                                                new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName()), cat);
+                                                new PlanSpecifier(product.getName(), BillingPeriod.MONTHLY, priceList.getName()), cat);
         Assert.assertEquals(r1, Result.FOO);
 
         final Result r2 = DefaultCase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2},
-                                                new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName()), cat);
+                                                new PlanSpecifier(product.getName(), BillingPeriod.ANNUAL, priceList.getName()), cat);
         Assert.assertEquals(r2, Result.DIPSY);
     }
 
     protected void assertionNull(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) throws CatalogApiException {
-        Assert.assertNull(cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
+        Assert.assertNull(cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat));
     }
 
     protected void assertionException(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) {
         try {
-            cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat);
+            cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat);
             Assert.fail("Expecting an exception");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -272,6 +269,6 @@ public class TestCase extends CatalogTestSuiteNoDB {
     }
 
     protected void assertion(final Result result, final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final StandaloneCatalog cat) throws CatalogApiException {
-        Assert.assertEquals(result, cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
+        Assert.assertEquals(result, cr.getResult(new PlanSpecifier(productName, bp, priceListName), cat));
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
index 6de6027..6528a3f 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCaseChange.java
@@ -71,10 +71,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testBasic() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -87,20 +87,19 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -108,20 +107,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionNull(cr,
                       product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -136,16 +121,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -160,10 +143,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardFromProduct() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -176,34 +159,19 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
-                  cat.getCurrentProducts()[1].getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
+                  cat.getCurrentProduct(1).getName(), product2.getName(),
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -218,16 +186,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -242,10 +208,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardToProduct() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -258,35 +224,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
-                  product1.getName(), cat.getCurrentProducts()[1].getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
+                  product1.getName(), cat.getCurrentProduct(1).getName(),
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -307,16 +258,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -331,10 +280,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardFromProductCategory() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -347,20 +296,19 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -368,20 +316,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.ADD_ON, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
                       product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -396,16 +336,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -420,10 +358,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardToProductCategory() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -436,35 +374,27 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.ADD_ON,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -485,16 +415,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -509,10 +437,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardFromBillingPeriod() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -525,34 +453,19 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -560,7 +473,6 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.ANNUAL, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
@@ -574,16 +486,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -598,10 +508,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildCardToBillingPeriod() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -614,34 +524,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -656,23 +552,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.ANNUAL,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -687,10 +580,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildCardFromPriceList() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -703,34 +596,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -752,16 +631,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                  cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                  priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -776,10 +653,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardToPriceList() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -792,34 +669,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -841,16 +704,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                  priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                  priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
@@ -865,10 +726,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testWildcardPlanPhase() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr = new DefaultCaseChangeResult(
@@ -881,34 +742,20 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      cat.getCurrentProducts()[1].getName(), product2.getName(),
+                      cat.getCurrentProduct(1).getName(), product2.getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
                       PhaseType.EVERGREEN, cat);
 
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.ADD_ON, ProductCategory.BASE,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
-
-        assertionNull(cr,
-                      product1.getName(), product2.getName(),
-                      ProductCategory.BASE, ProductCategory.ADD_ON,
-                      BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                      priceList1.getName(), priceList2.getName(),
-                      PhaseType.EVERGREEN, cat);
 
         assertionNull(cr,
-                      product1.getName(), cat.getCurrentProducts()[1].getName(),
+                      product1.getName(), cat.getCurrentProduct(1).getName(),
                       ProductCategory.BASE, ProductCategory.BASE,
                       BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                       priceList1.getName(), priceList2.getName(),
@@ -930,21 +777,18 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           cat.getCurrentProducts()[1].getName(), priceList2.getName(),
+                           cat.getCurrentProduct(1).getName(), priceList2.getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertionException(cr,
                            product1.getName(), product2.getName(),
-                           ProductCategory.BASE, ProductCategory.BASE,
                            BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
-                           priceList1.getName(), cat.getCurrentProducts()[1].getName(),
+                           priceList1.getName(), cat.getCurrentProduct(1).getName(),
                            PhaseType.EVERGREEN, cat);
 
         assertion(Result.FOO, cr,
                   product1.getName(), product2.getName(),
-                  ProductCategory.BASE, ProductCategory.BASE,
                   BillingPeriod.MONTHLY, BillingPeriod.MONTHLY,
                   priceList1.getName(), priceList2.getName(),
                   PhaseType.TRIAL, cat);
@@ -954,10 +798,10 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
     public void testOrder() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final DefaultProduct product2 = cat.getCurrentProducts()[2];
+        final DefaultProduct product2 = cat.getCurrentProduct(2);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
         final DefaultCaseChangeResult cr0 = new DefaultCaseChangeResult(
@@ -1001,14 +845,14 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCaseChange.getResult(new DefaultCaseChangeResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                      new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
-                                                      new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, priceList2.getName()), cat);
+                                                      new PlanPhaseSpecifier(product1.getName(),  BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
+                                                      new PlanSpecifier(product2.getName(),  BillingPeriod.MONTHLY, priceList2.getName()), cat);
 
         Assert.assertEquals(r1, Result.FOO);
 
         final Result r2 = DefaultCaseChange.getResult(new DefaultCaseChangeResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                      new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
-                                                      new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.ANNUAL, priceList2.getName()), cat);
+                                                      new PlanPhaseSpecifier(product1.getName(),  BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN),
+                                                      new PlanSpecifier(product2.getName(),  BillingPeriod.ANNUAL, priceList2.getName()), cat);
 
         Assert.assertEquals(r2, Result.DIPSY);
     }
@@ -1020,8 +864,8 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
                                  final String fromPriceListName, final String toPriceListName,
                                  final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                                           new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                                           new PlanSpecifier(toProductName, toBp, toPriceListName), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
@@ -1029,13 +873,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
     protected void assertionException(final DefaultCaseChangeResult cr,
                                       final String fromProductName, final String toProductName,
-                                      final ProductCategory fromProductCategory, final ProductCategory toProductCategory,
                                       final BillingPeriod fromBp, final BillingPeriod toBp,
                                       final String fromPriceListName, final String toPriceListName,
                                       final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                         new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat);
+            cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                         new PlanSpecifier(toProductName, toBp, toPriceListName), cat);
             Assert.fail("Expecting an exception");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -1044,13 +887,12 @@ public class TestCaseChange extends CatalogTestSuiteNoDB {
 
     protected void assertion(final Result result, final DefaultCaseChangeResult cr,
                              final String fromProductName, final String toProductName,
-                             final ProductCategory fromProductCategory, final ProductCategory toProductCategory,
                              final BillingPeriod fromBp, final BillingPeriod toBp,
                              final String fromPriceListName, final String toPriceListName,
                              final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType),
-                                                     new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName), cat));
+            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(fromProductName, fromBp, fromPriceListName, phaseType),
+                                                     new PlanSpecifier(toProductName, toBp, toPriceListName), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
index 3fa6052..2db00d3 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestCasePhase.java
@@ -61,7 +61,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testBasic() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -73,8 +73,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -84,7 +83,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testWildCardProduct() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -96,8 +95,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertion(Result.FOO, cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -107,7 +105,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testWildCardProductCategory() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -119,7 +117,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
@@ -130,7 +128,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testWildCardBillingPeriod() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -142,8 +140,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -153,7 +150,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testWildCardPriceList() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -165,8 +162,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -176,7 +172,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testWildCardPhaseType() {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr = new DefaultCaseResult(
@@ -188,8 +184,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.FOO);
 
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-        assertionNull(cr, product.getName(), ProductCategory.ADD_ON, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+        assertionNull(cr, cat.getCurrentProduct(1).getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
         assertionException(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
         assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
@@ -199,7 +194,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
     public void testOrder() throws CatalogApiException {
         final MockCatalog cat = new MockCatalog();
 
-        final DefaultProduct product = cat.getCurrentProducts()[0];
+        final DefaultProduct product = cat.getCurrentProduct(0);
         final DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
         final DefaultCaseResult cr0 = new DefaultCaseResult(
@@ -243,12 +238,12 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
                 Result.LALA);
 
         final Result r1 = DefaultCasePhase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                     new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN), cat);
+                                                     new PlanPhaseSpecifier(product.getName(), BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN), cat);
 
         Assert.assertEquals(Result.FOO, r1);
 
         final Result r2 = DefaultCasePhase.getResult(new DefaultCaseResult[]{cr0, cr1, cr2, cr3, cr4},
-                                                     new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN), cat);
+                                                     new PlanPhaseSpecifier(product.getName(), BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN), cat);
 
         Assert.assertEquals(Result.DIPSY, r2);
 
@@ -256,7 +251,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertionNull(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
@@ -264,7 +259,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertionException(final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
             Assert.fail("Exception expected");
         } catch (CatalogApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
@@ -273,7 +268,7 @@ public class TestCasePhase extends CatalogTestSuiteNoDB {
 
     protected void assertion(final Result result, final DefaultCaseResult cr, final String productName, final ProductCategory productCategory, final BillingPeriod bp, final String priceListName, final PhaseType phaseType, final StandaloneCatalog cat) {
         try {
-            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+            Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(productName, bp, priceListName, phaseType), cat));
         } catch (CatalogApiException e) {
             Assert.fail("", e);
         }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
index 47ca215..fc7b061 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestLoadRules.java
@@ -40,13 +40,13 @@ public class TestLoadRules extends CatalogTestSuiteNoDB {
         Assert.assertNotNull(catalog);
         final DefaultPlanRules rules = catalog.getPlanRules();
 
-        final PlanSpecifier specifier = new PlanSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
+        final PlanSpecifier specifier = new PlanSpecifier("Laser-Scope", BillingPeriod.MONTHLY,
                                                           "DEFAULT");
 
         final PlanAlignmentCreate alignment = rules.getPlanCreateAlignment(specifier, catalog);
         Assert.assertEquals(alignment, PlanAlignmentCreate.START_OF_SUBSCRIPTION);
 
-        final PlanSpecifier specifier2 = new PlanSpecifier("Extra-Ammo", ProductCategory.ADD_ON, BillingPeriod.MONTHLY,
+        final PlanSpecifier specifier2 = new PlanSpecifier("Extra-Ammo", BillingPeriod.MONTHLY,
                                                            "DEFAULT");
 
         final PlanAlignmentCreate alignment2 = rules.getPlanCreateAlignment(specifier2, catalog);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
index 152ad5f..950ea45 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/rules/TestPlanRules.java
@@ -56,30 +56,12 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testCannotChangeToSamePlan() throws CatalogApiException {
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
-        final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName());
-
-        try {
-            cat.getPlanRules().planChange(from, to, cat);
-            Assert.fail("We did not see an exception when  trying to change plan to the same plan");
-        } catch (IllegalPlanChange e) {
-            // Correct - cannot change to the same plan
-        } catch (CatalogApiException e) {
-            Assert.fail("", e);
-        }
-    }
-
-    @Test(groups = "fast")
     public void testExistingPriceListIsKept() throws CatalogApiException {
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.ANNUAL, priceList1.getName());
+        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+        final PlanSpecifier to = new PlanSpecifier(product1.getName(), BillingPeriod.ANNUAL, priceList1.getName());
 
         PlanChangeResult result = null;
         try {
@@ -97,13 +79,13 @@ public class TestPlanRules extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testBaseCase() throws CatalogApiException {
-        final DefaultProduct product1 = cat.getCurrentProducts()[0];
-        final DefaultProduct product2 = cat.getCurrentProducts()[1];
+        final DefaultProduct product1 = cat.getCurrentProduct(0);
+        final DefaultProduct product2 = cat.getCurrentProduct(1);
         final DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
         final DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
 
-        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
-        final PlanSpecifier to = new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, null);
+        final PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+        final PlanSpecifier to = new PlanSpecifier(product2.getName(), BillingPeriod.MONTHLY, null);
 
         PlanChangeResult result = null;
         try {
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
index 08a434b..abcbdf9 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
@@ -19,10 +19,8 @@
 package org.killbill.billing.catalog;
 
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.io.VersionedCatalogLoader;
 import org.killbill.billing.platform.api.KillbillService.ServiceException;
-import org.killbill.billing.util.config.CatalogConfig;
-import org.killbill.clock.DefaultClock;
+import org.killbill.billing.util.config.definition.CatalogConfig;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -33,13 +31,13 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
         final DefaultCatalogService service = new DefaultCatalogService(new CatalogConfig() {
             @Override
             public String getCatalogURI() {
-                return "file:src/test/resources/versionedCatalog";
+                return "versionedCatalog";
             }
 
         }, tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
-        Assert.assertNotNull(service.getFullCatalog(internalCallContext));
-        Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "WeaponsHireSmall");
+        Assert.assertNotNull(service.getFullCatalog(true, true, internalCallContext));
+        Assert.assertEquals(service.getFullCatalog(true, true, internalCallContext).getCatalogName(), "WeaponsHireSmall");
     }
 
     @Test(groups = "fast")
@@ -47,12 +45,12 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
         final DefaultCatalogService service = new DefaultCatalogService(new CatalogConfig() {
             @Override
             public String getCatalogURI() {
-                return "file:src/test/resources/WeaponsHire.xml";
+                return "WeaponsHire.xml";
             }
 
         },  tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
-        Assert.assertNotNull(service.getFullCatalog(internalCallContext));
-        Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "Firearms");
+        Assert.assertNotNull(service.getFullCatalog(true, true, internalCallContext));
+        Assert.assertEquals(service.getFullCatalog(true, true, internalCallContext).getCatalogName(), "Firearms");
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
new file mode 100644
index 0000000..a973ad1
--- /dev/null
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogUpdater.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.catalog.api.BillingMode;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.MutableStaticCatalog;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PriceList;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
+import org.killbill.xmlloader.XMLLoader;
+import org.killbill.xmlloader.XMLWriter;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Resources;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.fail;
+
+public class TestCatalogUpdater extends CatalogTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testEmptyDefaultCatalog() throws Exception {
+
+        final DateTime now = clock.getUTCNow();
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, null);
+        final String catalogXML = catalogUpdater.getCatalogXML();
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(catalogXML.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+        assertEquals(catalog.getCurrentPlans().size(), 0);
+    }
+
+    @Test(groups = "fast")
+    public void testAddNoTrialPlanOnFirstCatalog() throws CatalogApiException {
+
+        final DateTime now = clock.getUTCNow();
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
+
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        assertEquals(catalog.getCurrentProducts().size(), 1);
+
+        final Product product = catalog.getCurrentProducts().iterator().next();
+        assertEquals(product.getName(), "Foo");
+        assertEquals(product.getCategory(), ProductCategory.BASE);
+
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final Plan plan = catalog.findCurrentPlan("foo-monthly");
+        assertEquals(plan.getName(), "foo-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 0);
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "foo-monthly-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.EUR);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().size(), 1);
+        assertEquals(priceList.getPlans().iterator().next().getName(), "foo-monthly");
+    }
+
+
+    @Test(groups = "fast")
+    public void testAddTrialPlanOnFirstCatalog() throws CatalogApiException {
+
+        final DateTime now = clock.getUTCNow();
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("foo-monthly", "Foo", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater("dummy", BillingMode.IN_ADVANCE, now, desc.getCurrency());
+
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        assertEquals(catalog.getCurrentProducts().size(), 1);
+
+        final Product product = catalog.getCurrentProducts().iterator().next();
+        assertEquals(product.getName(), "Foo");
+        assertEquals(product.getCategory(), ProductCategory.BASE);
+
+        assertEquals(catalog.getCurrentPlans().size(), 1);
+
+        final Plan plan = catalog.findCurrentPlan("foo-monthly");
+        assertEquals(plan.getName(), "foo-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getCurrency(), Currency.EUR);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices()[0].getValue(), BigDecimal.ZERO);
+        assertEquals(plan.getInitialPhases()[0].getName(), "foo-monthly-trial");
+
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "foo-monthly-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.EUR);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().size(), 1);
+        assertEquals(priceList.getPlans().iterator().next().getName(), "foo-monthly");
+    }
+
+
+
+    @Test(groups = "fast")
+    public void testAddPlanOnExistingCatalog() throws Exception {
+
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().size(), 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-annual", "Standard", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        final Plan plan = catalog.findCurrentPlan("standard-annual");
+        assertEquals(plan.getName(), "standard-annual");
+
+        assertEquals(plan.getInitialPhases().length, 0);
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "standard-annual-evergreen");
+
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 1);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getValue(), BigDecimal.TEN);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices()[0].getCurrency(), Currency.USD);
+
+        assertEquals(catalog.getPriceLists().getAllPriceLists().size(), 1);
+        final PriceList priceList = catalog.getPriceLists().getAllPriceLists().get(0);
+        assertEquals(priceList.getName(), new PriceListDefault().getName());
+        assertEquals(priceList.getPlans().size(), 4);
+    }
+
+
+
+    @Test(groups = "fast")
+    public void testAddExistingPlanWithNewCurrency() throws Exception {
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().size(), 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final StandaloneCatalog catalog = catalogUpdater.getCatalog();
+
+        final Plan plan = catalog.findCurrentPlan("standard-monthly");
+        assertEquals(plan.getName(), "standard-monthly");
+
+        assertEquals(plan.getInitialPhases().length, 1);
+        assertEquals(plan.getInitialPhases()[0].getPhaseType(), PhaseType.TRIAL);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrices().length, 3);
+        assertEquals(plan.getInitialPhases()[0].getFixed().getPrice().getPrice(Currency.EUR), BigDecimal.ZERO);
+        assertEquals(plan.getInitialPhases()[0].getName(), "standard-monthly-trial");
+
+        assertEquals(plan.getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        assertNull(plan.getFinalPhase().getFixed());
+        assertEquals(plan.getFinalPhase().getName(), "standard-monthly-evergreen");
+        assertEquals(plan.getFinalPhase().getRecurring().getBillingPeriod(), BillingPeriod.MONTHLY);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrices().length, 3);
+        assertEquals(plan.getFinalPhase().getRecurring().getRecurringPrice().getPrice(Currency.EUR), BigDecimal.TEN);
+    }
+
+    @Test(groups = "fast")
+    public void testInvalidPlanDescriptors() throws Exception {
+        final StandaloneCatalog originalCatalog = enhanceOriginalCatalogForInvalidTestCases("SpyCarBasic.xml");
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().size(), 5);
+
+        CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        // Existing Plan has a 30 days trial => try with no TRIAL
+        SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.DAYS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+
+        // Existing Plan has a 30 days trial => try different trial length
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a 30 days trial => try different trial unit
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a MONTHLY recurring => try with ANNUAL BillingPeriod
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.ANNUAL, 30, TimeUnit.DAYS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has a discount phase
+        desc = new DefaultSimplePlanDescriptor("dynamic-monthly", "Dynamic", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.MONTHS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan has final fixedterm phase
+        desc = new DefaultSimplePlanDescriptor("superdynamic-fixedterm", "SuperDynamic", ProductCategory.BASE, Currency.EUR, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+
+        // Existing Plan a different recurring price ($100)
+        desc = new DefaultSimplePlanDescriptor("standard-monthly", "Standard", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
+        addBadSimplePlanDescriptor(catalogUpdater, desc);
+    }
+
+
+    @Test(groups = "fast")
+    public void testVerifyXML() throws Exception {
+
+        final StandaloneCatalog originalCatalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().size(), 1);
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getName(), new PriceListDefault().getName());
+        assertEquals(originalCatalog.getPriceLists().getAllPriceLists().get(0).getPlans().size(), 3);
+
+        final CatalogUpdater catalogUpdater = new CatalogUpdater(originalCatalog);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor("dynamic-annual", "Dynamic", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
+        catalogUpdater.addSimplePlanDescriptor(desc);
+
+        final String expectedXML = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
+                                   "<catalog>\n" +
+                                   "    <effectiveDate>2013-02-08T00:00:00Z</effectiveDate>\n" +
+                                   "    <catalogName>SpyCarBasic</catalogName>\n" +
+                                   "    <recurringBillingMode>IN_ADVANCE</recurringBillingMode>\n" +
+                                   "    <currencies>\n" +
+                                   "        <currency>USD</currency>\n" +
+                                   "        <currency>GBP</currency>\n" +
+                                   "    </currencies>\n" +
+                                   "    <products>\n" +
+                                   "        <product name=\"Dynamic\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Sports\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Standard\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "        <product name=\"Super\">\n" +
+                                   "            <category>BASE</category>\n" +
+                                   "            <included/>\n" +
+                                   "            <available/>\n" +
+                                   "            <limits/>\n" +
+                                   "        </product>\n" +
+                                   "    </products>\n" +
+                                   "    <rules>\n" +
+                                   "        <changePolicy>\n" +
+                                   "            <changePolicyCase>\n" +
+                                   "                <policy>IMMEDIATE</policy>\n" +
+                                   "            </changePolicyCase>\n" +
+                                   "        </changePolicy>\n" +
+                                   "        <changeAlignment>\n" +
+                                   "            <changeAlignmentCase>\n" +
+                                   "                <alignment>START_OF_BUNDLE</alignment>\n" +
+                                   "            </changeAlignmentCase>\n" +
+                                   "        </changeAlignment>\n" +
+                                   "        <cancelPolicy>\n" +
+                                   "            <cancelPolicyCase>\n" +
+                                   "                <policy>IMMEDIATE</policy>\n" +
+                                   "            </cancelPolicyCase>\n" +
+                                   "        </cancelPolicy>\n" +
+                                   "        <createAlignment>\n" +
+                                   "            <createAlignmentCase>\n" +
+                                   "                <alignment>START_OF_BUNDLE</alignment>\n" +
+                                   "            </createAlignmentCase>\n" +
+                                   "        </createAlignment>\n" +
+                                   "        <billingAlignment>\n" +
+                                   "            <billingAlignmentCase>\n" +
+                                   "                <alignment>ACCOUNT</alignment>\n" +
+                                   "            </billingAlignmentCase>\n" +
+                                   "        </billingAlignment>\n" +
+                                   "        <priceList>\n" +
+                                   "            <priceListCase>\n" +
+                                   "                <toPriceList>DEFAULT</toPriceList>\n" +
+                                   "            </priceListCase>\n" +
+                                   "        </priceList>\n" +
+                                   "    </rules>\n" +
+                                   "    <plans>\n" +
+                                   "        <plan name=\"dynamic-annual\">\n" +
+                                   "            <product>Dynamic</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>14</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>10</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"sports-monthly\">\n" +
+                                   "            <product>Sports</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>375.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>500.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"standard-monthly\">\n" +
+                                   "            <product>Standard</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>75.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>100.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "        <plan name=\"super-monthly\">\n" +
+                                   "            <product>Super</product>\n" +
+                                   "            <initialPhases>\n" +
+                                   "                <phase type=\"TRIAL\">\n" +
+                                   "                    <duration>\n" +
+                                   "                        <unit>DAYS</unit>\n" +
+                                   "                        <number>30</number>\n" +
+                                   "                    </duration>\n" +
+                                   "                    <fixed type=\"ONE_TIME\">\n" +
+                                   "                        <fixedPrice>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>USD</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                            <price>\n" +
+                                   "<currency>GBP</currency>\n" +
+                                   "<value>0</value>\n" +
+                                   "                            </price>\n" +
+                                   "                        </fixedPrice>\n" +
+                                   "                    </fixed>\n" +
+                                   "                    <usages/>\n" +
+                                   "                </phase>\n" +
+                                   "            </initialPhases>\n" +
+                                   "            <finalPhase type=\"EVERGREEN\">\n" +
+                                   "                <duration>\n" +
+                                   "                    <unit>UNLIMITED</unit>\n" +
+                                   "                    <number>-1</number>\n" +
+                                   "                </duration>\n" +
+                                   "                <recurring>\n" +
+                                   "                    <billingPeriod>MONTHLY</billingPeriod>\n" +
+                                   "                    <recurringPrice>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>GBP</currency>\n" +
+                                   "                            <value>750.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                        <price>\n" +
+                                   "                            <currency>USD</currency>\n" +
+                                   "                            <value>1000.00</value>\n" +
+                                   "                        </price>\n" +
+                                   "                    </recurringPrice>\n" +
+                                   "                </recurring>\n" +
+                                   "                <usages/>\n" +
+                                   "            </finalPhase>\n" +
+                                   "            <plansAllowedInBundle>-1</plansAllowedInBundle>\n" +
+                                   "        </plan>\n" +
+                                   "    </plans>\n" +
+                                   "    <priceLists>\n" +
+                                   "        <defaultPriceList name=\"DEFAULT\">\n" +
+                                   "            <plans>\n" +
+                                   "                <plan>dynamic-annual</plan>\n" +
+                                   "                <plan>sports-monthly</plan>\n" +
+                                   "                <plan>standard-monthly</plan>\n" +
+                                   "                <plan>super-monthly</plan>\n" +
+                                   "            </plans>\n" +
+                                   "        </defaultPriceList>\n" +
+                                   "    </priceLists>\n" +
+                                   "</catalog>\n";
+
+        assertEquals(catalogUpdater.getCatalogXML(), expectedXML);
+        System.err.println(catalogUpdater.getCatalogXML());
+    }
+
+
+    private StandaloneCatalog enhanceOriginalCatalogForInvalidTestCases(final String catalogName) throws Exception {
+
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource(catalogName).toExternalForm(), StandaloneCatalog.class);
+
+        final MutableStaticCatalog mutableCatalog = new DefaultMutableStaticCatalog(catalog);
+
+        final DefaultProduct newProduct1 = new DefaultProduct();
+        newProduct1.setName("Dynamic");
+        newProduct1.setCatagory(ProductCategory.BASE);
+        newProduct1.initialize((StandaloneCatalog) mutableCatalog, null);
+        mutableCatalog.addProduct(newProduct1);
+
+        final DefaultPlanPhase discountPhase1 = new DefaultPlanPhase();
+        discountPhase1.setPhaseType(PhaseType.DISCOUNT);
+        discountPhase1.setDuration(new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(14));
+        discountPhase1.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        final DefaultPlanPhase evergreenPhase1 = new DefaultPlanPhase();
+        evergreenPhase1.setPhaseType(PhaseType.EVERGREEN);
+        evergreenPhase1.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(1));
+        evergreenPhase1.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+        // Add a Plan with a DISCOUNT phase
+        final DefaultPlan newPlan1 = new DefaultPlan();
+        newPlan1.setName("dynamic-monthly");
+        newPlan1.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan1.setProduct(newProduct1);
+        newPlan1.setInitialPhases(new DefaultPlanPhase[]{discountPhase1});
+        newPlan1.setFinalPhase(evergreenPhase1);
+        mutableCatalog.addPlan(newPlan1);
+        newPlan1.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+
+        final DefaultProduct newProduct2 = new DefaultProduct();
+        newProduct2.setName("SuperDynamic");
+        newProduct2.setCatagory(ProductCategory.BASE);
+        newProduct2.initialize((StandaloneCatalog) mutableCatalog, null);
+        mutableCatalog.addProduct(newProduct2);
+
+        // Add a Plan with a FIXEDTERM phase
+        final DefaultPlanPhase fixedterm2 = new DefaultPlanPhase();
+        fixedterm2.setPhaseType(PhaseType.FIXEDTERM);
+        fixedterm2.setDuration(new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(3));
+        fixedterm2.setRecurring(new DefaultRecurring().setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice().setPrices(new DefaultPrice[]{new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.TEN)})));
+
+
+        final DefaultPlan newPlan2 = new DefaultPlan();
+        newPlan2.setName("superdynamic-fixedterm");
+        newPlan2.setPriceListName(DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        newPlan2.setProduct(newProduct2);
+        newPlan2.setFinalPhase(fixedterm2);
+        mutableCatalog.addPlan(newPlan2);
+        newPlan2.initialize((StandaloneCatalog) mutableCatalog, new URI("dummy"));
+
+
+        final String newCatalogStr = XMLWriter.writeXML((StandaloneCatalog) mutableCatalog, StandaloneCatalog.class);
+        return XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(newCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
+    }
+
+
+    private void addBadSimplePlanDescriptor(final CatalogUpdater catalogUpdater, final SimplePlanDescriptor desc) {
+        try {
+            catalogUpdater.addSimplePlanDescriptor(desc);
+            fail("Should have failed to add invalid desc " + desc);
+        } catch (final CatalogApiException e) {
+            assertEquals(e.getCode(), ErrorCode.CAT_FAILED_SIMPLE_PLAN_VALIDATION.getCode());
+        }
+    }
+}
\ No newline at end of file
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
index 2813984..325b7a2 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
@@ -64,8 +64,8 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
 
         assertEquals(overriddenPlan.getProduct().getName(), plan.getProduct().getName());
         assertEquals(overriddenPlan.getRecurringBillingPeriod(), plan.getRecurringBillingPeriod());
-        if (plan.getEffectiveDateForExistingSubscriptons() != null) {
-            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptons().compareTo(plan.getEffectiveDateForExistingSubscriptons()), 0);
+        if (plan.getEffectiveDateForExistingSubscriptions() != null) {
+            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptions().compareTo(plan.getEffectiveDateForExistingSubscriptions()), 0);
         }
         assertNotEquals(overriddenPlan.getFinalPhase().getName(), plan.getFinalPhase().getName());
         assertEquals(overriddenPlan.getPlansAllowedInBundle(), plan.getPlansAllowedInBundle());
@@ -131,8 +131,8 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
 
         assertEquals(overriddenPlan.getProduct().getName(), plan.getProduct().getName());
         assertEquals(overriddenPlan.getRecurringBillingPeriod(), plan.getRecurringBillingPeriod());
-        if (plan.getEffectiveDateForExistingSubscriptons() != null) {
-            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptons().compareTo(plan.getEffectiveDateForExistingSubscriptons()), 0);
+        if (plan.getEffectiveDateForExistingSubscriptions() != null) {
+            assertEquals(overriddenPlan.getEffectiveDateForExistingSubscriptions().compareTo(plan.getEffectiveDateForExistingSubscriptions()), 0);
         }
         assertNotEquals(overriddenPlan.getFinalPhase().getName(), plan.getFinalPhase().getName());
         assertEquals(overriddenPlan.getPlansAllowedInBundle(), plan.getPlansAllowedInBundle());
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestInternationalPrice.java b/catalog/src/test/java/org/killbill/billing/catalog/TestInternationalPrice.java
index a3803e2..12cc090 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestInternationalPrice.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestInternationalPrice.java
@@ -63,9 +63,9 @@ public class TestInternationalPrice extends CatalogTestSuiteNoDB {
     public void testPriceInitialization() throws URISyntaxException, CatalogApiException {
         final StandaloneCatalog c = new MockCatalog();
         c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
-        ((DefaultInternationalPrice) c.getCurrentPlans()[0].getFinalPhase().getRecurring().getRecurringPrice()).setPrices(null);
+        ((DefaultInternationalPrice) c.getCurrentPlans().iterator().next().getFinalPhase().getRecurring().getRecurringPrice()).setPrices(null);
         c.initialize(c, new URI("foo://bar"));
-        Assert.assertEquals(c.getCurrentPlans()[0].getFinalPhase().getRecurring().getRecurringPrice().getPrice(Currency.GBP), new BigDecimal(0));
+        Assert.assertEquals(c.getCurrentPlans().iterator().next().getFinalPhase().getRecurring().getRecurringPrice().getPrice(Currency.GBP), new BigDecimal(0));
     }
 
     @Test(groups = "fast")
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java b/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
index 466b1c4..86f5033 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
@@ -32,7 +32,7 @@ public class TestPlan extends CatalogTestSuiteNoDB {
         final StandaloneCatalog c = new MockCatalog();
         c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
         final DefaultPlan p1 = MockPlan.createBicycleTrialEvergreen1USD();
-        p1.setEffectiveDateForExistingSubscriptons(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
+        p1.setEffectiveDateForExistingSubscriptions(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
         final ValidationErrors errors = p1.validate(c, new ValidationErrors());
         Assert.assertEquals(errors.size(), 1);
         errors.log(log);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java b/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
index d38438a..8b3346a 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestPriceListSet.java
@@ -52,10 +52,10 @@ public class TestPriceListSet extends CatalogTestSuiteNoDB {
         };
         final DefaultPriceListSet set = new DefaultPriceListSet(defaultPriceList, childPriceLists);
 
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, "child").getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, "child").getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
     }
 
     @Test(groups = "fast")
@@ -65,8 +65,8 @@ public class TestPriceListSet extends CatalogTestSuiteNoDB {
         final DefaultPlan[] defaultPlans = new DefaultPlan[]{
                 new MockPlan().setName("plan-foo-monthly").setProduct(foo).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(MONTHLY, null)).setPhaseType(EVERGREEN)),
                 new MockPlan().setName("plan-bar-monthly").setProduct(bar).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(MONTHLY, null)).setPhaseType(EVERGREEN)),
-                new MockPlan().setName("plan-foo-annual").setProduct(foo).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(null, null)).setPhaseType(EVERGREEN)),
-                new MockPlan().setName("plan-bar-annual").setProduct(bar).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(null, null)).setPhaseType(EVERGREEN))
+                new MockPlan().setName("plan-foo-annual").setProduct(foo).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(ANNUAL, null)).setPhaseType(EVERGREEN)),
+                new MockPlan().setName("plan-bar-annual").setProduct(bar).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(ANNUAL, null)).setPhaseType(EVERGREEN))
         };
         final DefaultPlan[] childPlans = new DefaultPlan[]{
                 new MockPlan().setName("plan-foo").setProduct(foo).setFinalPhase(new MockPlanPhase().setRecurring(new MockRecurring(ANNUAL, null)).setPhaseType(DISCOUNT)),
@@ -79,9 +79,9 @@ public class TestPriceListSet extends CatalogTestSuiteNoDB {
         };
         final DefaultPriceListSet set = new DefaultPriceListSet(defaultPriceList, childPriceLists);
 
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
-        Assert.assertEquals(set.getPlanFrom("child", foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.ANNUAL).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
-        Assert.assertEquals(set.getPlanFrom(PriceListSet.DEFAULT_PRICELIST_NAME, foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, "child").getFinalPhase().getPhaseType(), PhaseType.DISCOUNT);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, "child").getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
+        Assert.assertEquals(set.getPlanFrom(foo, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
index 93b491e..78b4554 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
@@ -16,12 +16,15 @@
 
 package org.killbill.billing.catalog;
 
+import org.killbill.billing.catalog.api.Plan;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.PhaseType;
 
+import com.google.common.collect.ImmutableList;
+
 public class TestStandaloneCatalog extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
@@ -38,7 +41,7 @@ public class TestStandaloneCatalog extends CatalogTestSuiteNoDB {
         phaseDiscount1.setPlan(plan1);
         phaseDiscount2.setPlan(plan2);
 
-        final StandaloneCatalog cat = new MockCatalog().setPlans(new DefaultPlan[]{plan1, plan2});
+        final StandaloneCatalog cat = new MockCatalog().setPlans(ImmutableList.<Plan>of(plan1, plan2));
 
         Assert.assertEquals(cat.findCurrentPhase("TestPlan1-discount"), phaseDiscount1);
         Assert.assertEquals(cat.findCurrentPhase("TestPlan2-discount"), phaseDiscount2);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
index 12d60f5..c4bd0b9 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -21,7 +21,6 @@ package org.killbill.billing.catalog;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URISyntaxException;
-import java.util.Date;
 
 import javax.xml.bind.JAXBException;
 import javax.xml.transform.TransformerException;
@@ -38,8 +37,6 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.xml.sax.SAXException;
 
-import com.google.common.io.Resources;
-
 public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
 
     private VersionedCatalog vc;
@@ -47,14 +44,9 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {
         super.beforeClass();
-        vc = loader.loadDefaultCatalog(Resources.getResource("versionedCatalog").toString());
+        vc = loader.loadDefaultCatalog("versionedCatalog");
     }
 
-    @Test(groups = "fast")
-    public void testAddCatalog() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException, CatalogApiException {
-        vc.add(new StandaloneCatalogWithPriceOverride(new StandaloneCatalog(new Date()).setCatalogName(vc.getCatalogName()).setRecurringBillingMode(vc.getRecurringBillingMode()), null, 0L, null));
-        Assert.assertEquals(vc.size(), 4);
-    }
 
     @Test(groups = "fast")
     public void testFindPlanWithDates() throws Exception {
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index 4b1ee25..670abb5 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -275,7 +275,80 @@
                 </recurring>
             </finalPhase>
         </plan>
-
+        <plan name="pistol-weekly">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>WEEKLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
+        <plan name="pistol-thirty-days">
+            <product>Pistol</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice>
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>THIRTY_DAYS</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>29.95</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>29.95</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
         <plan name="pistol-monthly">
             <product>Pistol</product>
             <initialPhases>
@@ -765,6 +838,7 @@
                     </recurringPrice>
                 </recurring>
             </finalPhase>
+            <plansAllowedInBundle>2</plansAllowedInBundle>
         </plan>
         <plan name="cleaning-monthly">
             <product>Cleaning</product>
@@ -1101,6 +1175,8 @@
     <priceLists>
         <defaultPriceList name="DEFAULT">
             <plans>
+                <plan>pistol-weekly</plan>
+                <plan>pistol-thirty-days</plan>
                 <plan>blowdart-monthly</plan>
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
diff --git a/catalog/src/test/resources/SpyCarAdvanced.xml b/catalog/src/test/resources/SpyCarAdvanced.xml
index f02a9cd..a08c425 100644
--- a/catalog/src/test/resources/SpyCarAdvanced.xml
+++ b/catalog/src/test/resources/SpyCarAdvanced.xml
@@ -458,6 +458,82 @@
                 </recurring>
             </finalPhase>
         </plan>
+        <plan name="cia-standard-monthly">
+            <product>Standard</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>3</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>25.00</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>30.00</value>
+                            </price>
+                            <price>
+                                <currency>USD</currency>
+                                <value>33.00</value>
+                            </price>
+                            <price>
+                                <currency>JPY</currency>
+                                <value>3.30</value>
+                            </price>
+                            <price>
+                                <currency>BTC</currency>
+                                <value>0.04</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>50.00</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>75.00</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>90.00</value>
+                        </price>
+                        <price>
+                            <currency>JPY</currency>
+                            <value>8.00</value>
+                        </price>
+                        <price>
+                            <currency>BTC</currency>
+                            <value>0.08</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
         <plan name="discount-sports-monthly">
             <product>Sports</product>
             <initialPhases>
@@ -534,6 +610,82 @@
                 </recurring>
             </finalPhase>
         </plan>
+        <plan name="cia-sports-monthly">
+            <product>Sports</product>
+            <initialPhases>
+                <phase type="TRIAL">
+                    <duration>
+                        <unit>DAYS</unit>
+                        <number>30</number>
+                    </duration>
+                    <fixed>
+                        <fixedPrice> <!-- empty price implies $0 -->
+                        </fixedPrice>
+                    </fixed>
+                </phase>
+                <phase type="DISCOUNT">
+                    <duration>
+                        <unit>MONTHS</unit>
+                        <number>3</number>
+                    </duration>
+                    <recurring>
+                        <billingPeriod>MONTHLY</billingPeriod>
+                        <recurringPrice>
+                            <price>
+                                <currency>GBP</currency>
+                                <value>150.00</value>
+                            </price>
+                            <price>
+                                <currency>EUR</currency>
+                                <value>150.00</value>
+                            </price>
+                            <price>
+                                <currency>USD</currency>
+                                <value>250.00</value>
+                            </price>
+                            <price>
+                                <currency>JPY</currency>
+                                <value>20.30</value>
+                            </price>
+                            <price>
+                                <currency>BTC</currency>
+                                <value>0.2</value>
+                            </price>
+                        </recurringPrice>
+                    </recurring>
+                </phase>
+            </initialPhases>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <recurring>
+                    <billingPeriod>MONTHLY</billingPeriod>
+                    <recurringPrice>
+                        <price>
+                            <currency>GBP</currency>
+                            <value>300.00</value>
+                        </price>
+                        <price>
+                            <currency>EUR</currency>
+                            <value>375.00</value>
+                        </price>
+                        <price>
+                            <currency>USD</currency>
+                            <value>450.00</value>
+                        </price>
+                        <price>
+                            <currency>JPY</currency>
+                            <value>40.00</value>
+                        </price>
+                        <price>
+                            <currency>BTC</currency>
+                            <value>0.4</value>
+                        </price>
+                    </recurringPrice>
+                </recurring>
+            </finalPhase>
+        </plan>
         <plan name="discount-super-monthly">
             <product>Super</product>
             <initialPhases>
@@ -807,17 +959,12 @@
                 <plan>discount-standard-monthly</plan>
                 <plan>discount-sports-monthly</plan>
                 <plan>discount-super-monthly</plan>
-                <plan>remotecontrol-monthly</plan>
-                <plan>oilslick-monthly</plan>
             </plans>
         </childPriceList>
         <childPriceList name="CIA">
             <plans>
-                <plan>discount-standard-monthly</plan>
-                <plan>discount-sports-monthly</plan>
-                <plan>discount-super-monthly</plan>
-                <plan>remotecontrol-monthly</plan>
-                <plan>oilslick-monthly</plan>
+                <plan>cia-standard-monthly</plan>
+                <plan>cia-sports-monthly</plan>
             </plans>
         </childPriceList>
     </priceLists>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index 978b238..b2b9276 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -276,6 +276,8 @@
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
     </priceLists>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 9801e42..f04ff0e 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -87,7 +87,7 @@
 
     <plans>
         <plan name="pistol-monthly">
-            <effectiveDateForExistingSubscriptons>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptons>
+            <effectiveDateForExistingSubscriptions>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
 
             <product>Pistol</product>
             <initialPhases>
@@ -278,6 +278,8 @@
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
     </priceLists>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index 453a143..dc9c0b1 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -87,7 +87,7 @@
 
     <plans>
         <plan name="pistol-monthly">
-            <effectiveDateForExistingSubscriptons>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptons>
+            <effectiveDateForExistingSubscriptions>2011-03-14T00:00:00+00:00</effectiveDateForExistingSubscriptions>
             <product>Pistol</product>
             <initialPhases>
                 <phase type="TRIAL">
@@ -277,6 +277,8 @@
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
     </priceLists>
diff --git a/catalog/src/test/resources/WeaponsHireSmall.xml b/catalog/src/test/resources/WeaponsHireSmall.xml
index c0501d3..5adfb99 100644
--- a/catalog/src/test/resources/WeaponsHireSmall.xml
+++ b/catalog/src/test/resources/WeaponsHireSmall.xml
@@ -310,6 +310,8 @@
                 <plan>pistol-monthly</plan>
                 <plan>shotgun-monthly</plan>
                 <plan>shotgun-annual</plan>
+                <plan>laser-scope-monthly</plan>
+                <plan>extra-ammo-monthly</plan>
             </plans>
         </defaultPriceList>
     </priceLists>

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index 069ba1e..3278f26 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/currency/src/main/java/org/killbill/billing/currency/api/DefaultCurrencyConversionApi.java b/currency/src/main/java/org/killbill/billing/currency/api/DefaultCurrencyConversionApi.java
index e9e4559..4d8ecff 100644
--- a/currency/src/main/java/org/killbill/billing/currency/api/DefaultCurrencyConversionApi.java
+++ b/currency/src/main/java/org/killbill/billing/currency/api/DefaultCurrencyConversionApi.java
@@ -26,7 +26,7 @@ import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.util.config.CurrencyConfig;
+import org.killbill.billing.util.config.definition.CurrencyConfig;
 
 public class DefaultCurrencyConversionApi implements CurrencyConversionApi {
 
diff --git a/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java b/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
index 310eb05..2962722 100644
--- a/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
+++ b/currency/src/main/java/org/killbill/billing/currency/DefaultCurrencyProviderPluginRegistry.java
@@ -26,7 +26,6 @@ import org.slf4j.LoggerFactory;
 import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
-import org.killbill.billing.util.config.CurrencyConfig;
 
 import com.google.inject.Inject;
 
diff --git a/currency/src/main/java/org/killbill/billing/currency/glue/CurrencyModule.java b/currency/src/main/java/org/killbill/billing/currency/glue/CurrencyModule.java
index 3865eef..4252f6e 100644
--- a/currency/src/main/java/org/killbill/billing/currency/glue/CurrencyModule.java
+++ b/currency/src/main/java/org/killbill/billing/currency/glue/CurrencyModule.java
@@ -25,7 +25,7 @@ import org.killbill.billing.currency.api.DefaultCurrencyConversionApi;
 import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.util.config.CurrencyConfig;
+import org.killbill.billing.util.config.definition.CurrencyConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 

entitlement/pom.xml 37(+32 -5)

diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 9c0e4ca..20c9c9b 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
@@ -50,6 +50,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -59,11 +74,20 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi</artifactId>
         </dependency>
@@ -150,6 +174,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -168,10 +199,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.skife.config</groupId>
-            <artifactId>config-magic</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index ec04528..429eafe 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
@@ -24,12 +24,14 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
@@ -41,8 +43,13 @@ import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator
 import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
 import org.killbill.billing.junction.DefaultBlockingState;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 
 // Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
 public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -51,31 +58,35 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
 
     private BlockingStateOrdering() {}
 
-    public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
-        INSTANCE.computeEvents(entitlements, internalTenantContext, result);
+    public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+        INSTANCE.computeEvents(entitlements, internalTenantContext, inputAndOutputResult);
+
     }
 
-    private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> result) {
+    private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+
+
         final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
         final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
-        final Map<UUID, DateTime> referenceTimes = new HashMap<UUID, DateTime>();
         for (final Entitlement entitlement : entitlements) {
             allEntitlementUUIDs.add(entitlement.getId());
             Preconditions.checkState(entitlement instanceof DefaultEntitlement, "Entitlement %s is not a DefaultEntitlement", entitlement);
             blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
-            referenceTimes.put(entitlement.getId(), ((DefaultEntitlement) entitlement).getSubscriptionBase().getStartDate());
         }
 
+        final SupportForOlderVersionThan_0_17_X backwardCompatibleContext = new SupportForOlderVersionThan_0_17_X(inputAndOutputResult, blockingStates);
+
         // Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
-        for (final BlockingState bs : blockingStates) {
-            final List<SubscriptionEvent> newEvents = new ArrayList<SubscriptionEvent>();
-            final int index = insertFromBlockingEvent(referenceTimes, internalTenantContext, allEntitlementUUIDs, result, bs, bs.getEffectiveDate(), newEvents);
-            insertAfterIndex(result, newEvents, index);
+        for (final BlockingState currentBlockingState : blockingStates) {
+            final List<SubscriptionEvent> outputNewEvents = new ArrayList<SubscriptionEvent>();
+            final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, backwardCompatibleContext, internalTenantContext, outputNewEvents);
+            insertAfterIndex(inputAndOutputResult, outputNewEvents, index);
         }
+        backwardCompatibleContext.addMissing_START_ENTITLEMENT(inputAndOutputResult, internalTenantContext);
     }
 
     // Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
-    private int insertFromBlockingEvent(final Map<UUID, DateTime> referenceTimes, final InternalTenantContext internalTenantContext, final Collection<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final Collection<SubscriptionEvent> newEvents) {
+    private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final SupportForOlderVersionThan_0_17_X backwardCompatibleContext, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
         // Keep the current state per entitlement
         final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
         for (final UUID cur : allEntitlementUUIDs) {
@@ -86,12 +97,12 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
         // Find out where to insert next event, and calculate current state for each entitlement at the position where we stop.
         //
         int index = -1;
-        final Iterator<SubscriptionEvent> it = result.iterator();
+        final Iterator<SubscriptionEvent> it = inputExistingEvents.iterator();
         // Where we need to insert in that stream
         DefaultSubscriptionEvent curInsertion = null;
         while (it.hasNext()) {
             final DefaultSubscriptionEvent cur = (DefaultSubscriptionEvent) it.next();
-            final int compEffectiveDate = bsEffectiveDate.compareTo(cur.getEffectiveDateTime());
+            final int compEffectiveDate = currentBlockingState.getEffectiveDate().compareTo(cur.getEffectiveDateTime());
             final boolean shouldContinue = (compEffectiveDate >= 0);
             if (!shouldContinue) {
                 break;
@@ -107,6 +118,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                     curTargetState.setEntitlementStopped();
                     break;
                 case START_BILLING:
+                    // For older subscriptions we miss the START_ENTITLEMENT (the START_BILLING marks both start of billing and entitlement)
+                    if (backwardCompatibleContext.isOlderEntitlement(cur.getEntitlementId())) {
+                        curTargetState.setEntitlementStarted();
+                    }
                     curTargetState.setBillingStarted();
                     break;
                 case PAUSE_BILLING:
@@ -124,17 +139,17 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
         }
 
         // Extract the list of targets based on the type of blocking state
-        final List<UUID> targetEntitlementIds = bs.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(bs.getBlockedId()) :
+        final List<UUID> targetEntitlementIds = currentBlockingState.getType() == BlockingStateType.SUBSCRIPTION ? ImmutableList.<UUID>of(currentBlockingState.getBlockedId()) :
                                                 ImmutableList.<UUID>copyOf(allEntitlementUUIDs);
 
         // For each target compute the new events that should be inserted in the stream
         for (final UUID targetEntitlementId : targetEntitlementIds) {
-            final SubscriptionEvent[] prevNext = findPrevNext(result, targetEntitlementId, curInsertion);
+            final SubscriptionEvent[] prevNext = findPrevNext(inputExistingEvents, targetEntitlementId, curInsertion);
             final TargetState curTargetState = targetStates.get(targetEntitlementId);
 
-            final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(bs);
+            final List<SubscriptionEventType> eventTypes = curTargetState.addStateAndReturnEventTypes(currentBlockingState);
             for (final SubscriptionEventType t : eventTypes) {
-                newEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, bs, t, referenceTimes.get(targetEntitlementId), internalTenantContext));
+                outputNewEvents.add(toSubscriptionEvent(prevNext[0], prevNext[1], targetEntitlementId, currentBlockingState, t, internalTenantContext));
             }
         }
         return index;
@@ -177,7 +192,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
 
     private SubscriptionEvent toSubscriptionEvent(@Nullable final SubscriptionEvent prev, @Nullable final SubscriptionEvent next,
                                                   final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType,
-                                                  final DateTime referenceTime, final InternalTenantContext internalTenantContext) {
+                                                  final InternalTenantContext internalTenantContext) {
         final Product prevProduct;
         final Plan prevPlan;
         final PlanPhase prevPlanPhase;
@@ -204,8 +219,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
         final PlanPhase nextPlanPhase;
         final PriceList nextPriceList;
         final BillingPeriod nextBillingPeriod;
-        if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) || SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
-            SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) || SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
+        if (SubscriptionEventType.PAUSE_ENTITLEMENT.equals(eventType) ||
+            SubscriptionEventType.PAUSE_BILLING.equals(eventType) ||
+            SubscriptionEventType.RESUME_ENTITLEMENT.equals(eventType) ||
+            SubscriptionEventType.RESUME_BILLING.equals(eventType) ||
             (SubscriptionEventType.SERVICE_STATE_CHANGE.equals(eventType) && (prev == null || (!SubscriptionEventType.STOP_ENTITLEMENT.equals(prev.getSubscriptionEventType()) && !SubscriptionEventType.STOP_BILLING.equals(prev.getSubscriptionEventType()))))) {
             // Enforce next = prev for pause/resume events as well as service changes
             nextProduct = prevProduct;
@@ -258,7 +275,6 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                                             nextPriceList,
                                             nextBillingPeriod,
                                             in.getCreatedDate(),
-                                            referenceTime,
                                             internalTenantContext);
     }
 
@@ -345,17 +361,22 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                                                                               bs.getStateName(),
                                                                               bs.getService(),
                                                                               bs.isBlockChange(),
-                                                                              (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+                                                                              (bs.isBlockEntitlement() && isEntitlementStarted &&  !isEntitlementStopped),
                                                                               (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
                                                                               bs.getEffectiveDate());
 
             final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
-            if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+            if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START)) {
+                isEntitlementStarted = true;
+                result.add(SubscriptionEventType.START_ENTITLEMENT);
+                return result;
+            } else if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
                 isEntitlementStopped = true;
                 result.add(SubscriptionEventType.STOP_ENTITLEMENT);
                 return result;
             }
 
+
             //
             // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
             // across all services
@@ -372,7 +393,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
             }
             final BlockingAggregator stateAfter = getState();
 
-            final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+            final boolean shouldResumeEntitlement = isEntitlementStarted &&  !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
             if (shouldResumeEntitlement) {
                 result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
             }
@@ -381,7 +402,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                 result.add(SubscriptionEventType.RESUME_BILLING);
             }
 
-            final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+            final boolean shouldBlockEntitlement = isEntitlementStarted &&  !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
             if (shouldBlockEntitlement) {
                 result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
             }
@@ -404,4 +425,85 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
             return aggrBefore;
         }
     }
+
+    //
+    // The logic to add the missing START_ENTITLEMENT for older subscriptions is contained in this class. When we want/need to drop backward compatibility we can
+    // simply drop this class and where it is called.
+    //
+    private static class SupportForOlderVersionThan_0_17_X {
+
+        private final Set<UUID> olderEntitlementSet;
+
+        public SupportForOlderVersionThan_0_17_X(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+            this.olderEntitlementSet = computeOlderEntitlementSet(initialEntitlementEvents, blockingStates);
+        }
+
+        public boolean isOlderEntitlement(final UUID entitlementId) {
+            return olderEntitlementSet.contains(entitlementId);
+        }
+
+        public void addMissing_START_ENTITLEMENT(final LinkedList<SubscriptionEvent> inputAndOutputResult, final InternalTenantContext internalTenantContext) {
+
+            // Insert missing START_ENTITLEMENT right before START_BILLING (same event as START_BILLING but with different type=START_ENTITLEMENT to be compatible with old code)
+            final ListIterator<SubscriptionEvent> it = inputAndOutputResult.listIterator();
+            while (it.hasNext()) {
+                final SubscriptionEvent cur = it.next();
+                if (cur.getSubscriptionEventType() == SubscriptionEventType.START_BILLING && olderEntitlementSet.contains(cur.getEntitlementId())) {
+                    final SubscriptionEvent newEntitlementStartEvent = new DefaultSubscriptionEvent(cur.getId(),
+                                                                                                    cur.getEntitlementId(),
+                                                                                                    internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+                                                                                                    SubscriptionEventType.START_ENTITLEMENT,
+                                                                                                    false,
+                                                                                                    false,
+                                                                                                    DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                                                    SubscriptionEventType.START_ENTITLEMENT.toString(),
+                                                                                                    cur.getPrevProduct(),
+                                                                                                    cur.getPrevPlan(),
+                                                                                                    cur.getPrevPhase(),
+                                                                                                    cur.getPrevPriceList(),
+                                                                                                    cur.getPrevBillingPeriod(),
+                                                                                                    cur.getNextProduct(),
+                                                                                                    cur.getNextPlan(),
+                                                                                                    cur.getNextPhase(),
+                                                                                                    cur.getNextPriceList(),
+                                                                                                    cur.getNextBillingPeriod(),
+                                                                                                    internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+                                                                                                    internalTenantContext);
+                    it.previous();
+                    it.add(newEntitlementStartEvent);
+                    it.next();
+                }
+            }
+        }
+
+        private Set<UUID> computeOlderEntitlementSet(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+
+            final Set<UUID> START_BILLING_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(initialEntitlementEvents, new Predicate<SubscriptionEvent>() {
+                @Override
+                public boolean apply(final SubscriptionEvent input) {
+                    return input.getSubscriptionEventType() == SubscriptionEventType.START_BILLING;
+                }
+            }), new Function<SubscriptionEvent, UUID>() {
+                @Override
+                public UUID apply(final SubscriptionEvent input) {
+                    return input.getEntitlementId();
+                }
+            }));
+
+            final Set<UUID> ENT_STATE_START_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(blockingStates, new Predicate<BlockingState>() {
+                @Override
+                public boolean apply(final BlockingState input) {
+                    return input.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME) && input.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START);
+                }
+            }), new Function<BlockingState, UUID>() {
+                @Override
+                public UUID apply(final BlockingState input) {
+                    return input.getBlockedId();
+                }
+            }));
+
+            return Sets.<UUID>difference(START_BILLING_entitlementIdSet, ENT_STATE_START_entitlementIdSet);
+        }
+    }
+
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
index f32a19c..67b0ae2 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultBlockingTransitionInternalEvent.java
@@ -18,6 +18,7 @@ package org.killbill.billing.entitlement.api;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.BusEventBase;
 
@@ -28,6 +29,9 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 public class DefaultBlockingTransitionInternalEvent extends BusEventBase implements BlockingTransitionInternalEvent {
 
     private final UUID blockableId;
+    private final String stateName;
+    private final String service;
+    private final DateTime effectiveDate;
     private final BlockingStateType blockingType;
     private final Boolean isTransitionedToBlockedBilling;
     private final Boolean isTransitionedToUnblockedBilling;
@@ -36,6 +40,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
 
     @JsonCreator
     public DefaultBlockingTransitionInternalEvent(@JsonProperty("blockableId") final UUID blockableId,
+                                                  @JsonProperty("stateName") final String stateName,
+                                                  @JsonProperty("service") final String service,
+                                                  @JsonProperty("effectiveDate") final DateTime effectiveDate,
                                                   @JsonProperty("blockingType") final BlockingStateType blockingType,
                                                   @JsonProperty("isTransitionedToBlockedBilling") final Boolean transitionedToBlockedBilling,
                                                   @JsonProperty("isTransitionedToUnblockedBilling") final Boolean transitionedToUnblockedBilling,
@@ -47,6 +54,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
         super(searchKey1, searchKey2, userToken);
         this.blockableId = blockableId;
         this.blockingType = blockingType;
+        this.service = service;
+        this.stateName = stateName;
+        this.effectiveDate = effectiveDate;
         isTransitionedToBlockedBilling = transitionedToBlockedBilling;
         isTransitionedToUnblockedBilling = transitionedToUnblockedBilling;
         isTransitionedToBlockedEntitlement = transitionedToBlockedEntitlement;
@@ -63,6 +73,21 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
         return blockingType;
     }
 
+    @Override
+    public String getStateName() {
+        return stateName;
+    }
+
+    @Override
+    public String getService() {
+        return service;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
     @JsonProperty("isTransitionedToBlockedBilling")
     @Override
     public Boolean isTransitionedToBlockedBilling() {
@@ -110,6 +135,15 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
         if (blockingType != that.blockingType) {
             return false;
         }
+        if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+            return false;
+        }
+        if (service != null ? !service.equals(that.service) : that.service != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
         if (isTransitionedToBlockedBilling != null ? !isTransitionedToBlockedBilling.equals(that.isTransitionedToBlockedBilling) : that.isTransitionedToBlockedBilling != null) {
             return false;
         }
@@ -130,6 +164,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
     public int hashCode() {
         int result = blockableId != null ? blockableId.hashCode() : 0;
         result = 31 * result + (blockingType != null ? blockingType.hashCode() : 0);
+        result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+        result = 31 * result + (service != null ? service.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (isTransitionedToBlockedBilling != null ? isTransitionedToBlockedBilling.hashCode() : 0);
         result = 31 * result + (isTransitionedToUnblockedBilling != null ? isTransitionedToUnblockedBilling.hashCode() : 0);
         result = 31 * result + (isTransitionedToBlockedEntitlement != null ? isTransitionedToBlockedEntitlement.hashCode() : 0);
@@ -142,6 +179,9 @@ public class DefaultBlockingTransitionInternalEvent extends BusEventBase impleme
         final StringBuilder sb = new StringBuilder("DefaultBlockingTransitionInternalEvent{");
         sb.append("blockableId=").append(blockableId);
         sb.append(", blockingType=").append(blockingType);
+        sb.append(", stateName=").append(stateName);
+        sb.append(", service=").append(service);
+        sb.append(", effectiveDate=").append(effectiveDate);
         sb.append(", isTransitionedToBlockedBilling=").append(isTransitionedToBlockedBilling);
         sb.append(", isTransitionedToUnblockedBilling=").append(isTransitionedToUnblockedBilling);
         sb.append(", isTransitionedToBlockedEntitlement=").append(isTransitionedToBlockedEntitlement);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index a93cdbb..1f84737 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -35,9 +35,11 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -71,11 +73,20 @@ import org.killbill.notificationq.api.NotificationEvent;
 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.collect.ImmutableList;
 
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCancelEntitlement;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logChangePlan;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logUncancelEntitlement;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logUpdateBCD;
+
 public class DefaultEntitlement extends EntityBase implements Entitlement {
 
+    private Logger log = LoggerFactory.getLogger(DefaultEntitlement.class);
+
     private final SecurityApi securityApi;
     protected final EventsStreamBuilder eventsStreamBuilder;
     protected final EntitlementDateHelper dateHelper;
@@ -93,7 +104,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     // Refresh-able
     protected EventsStream eventsStream;
 
-
     public DefaultEntitlement(final UUID accountId, final UUID entitlementId, final EventsStreamBuilder eventsStreamBuilder,
                               final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution, final BlockingStateDao blockingStateDao,
                               final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
@@ -246,7 +256,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public LocalDate getEffectiveStartDate() {
-        return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), getSubscriptionBase().getStartDate());
+        return eventsStream.getEntitlementEffectiveStartDate();
     }
 
     @Override
@@ -279,10 +289,17 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         return getSubscriptionBase().getLastActiveCategory();
     }
 
+    @Override
+    public Integer getBillCycleDayLocal() {
+        final Integer perSubscriptionBillCycleDayLocal = getSubscriptionBase().getBillCycleDayLocal();
+        return perSubscriptionBillCycleDayLocal != null ? perSubscriptionBillCycleDayLocal : eventsStream.getDefaultBillCycleDayLocal();
+    }
 
     @Override
     public Entitlement cancelEntitlementWithPolicy(final EntitlementActionPolicy entitlementPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
 
+        logCancelEntitlement(log, this, null, null, entitlementPolicy, null);
+
         // Get the latest state from disk - required to have the latest CTD
         refresh(callContext);
 
@@ -291,24 +308,32 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     }
 
     @Override
-    public Entitlement cancelEntitlementWithDate(@Nullable final LocalDate localCancelDate, final boolean overrideBillingEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement cancelEntitlementWithDate(@Nullable final LocalDate entitlementEffectiveDate, final boolean overrideBillingEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logCancelEntitlement(log, this, entitlementEffectiveDate, overrideBillingEffectiveDate, null, null);
 
         checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
 
         // Get the latest state from disk
         refresh(callContext);
 
+        if (entitlementEffectiveDate != null && entitlementEffectiveDate.compareTo(getEffectiveStartDate()) < 0) {
+            throw new EntitlementApiException(ErrorCode.SUB_INVALID_REQUESTED_DATE, entitlementEffectiveDate, getEffectiveStartDate());
+        }
+
+        final LocalDate billingEffectiveDate = overrideBillingEffectiveDate ? entitlementEffectiveDate : null;
         final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CANCEL_SUBSCRIPTION,
                                                                                getAccountId(),
                                                                                null,
                                                                                getBundleId(),
                                                                                getExternalKey(),
                                                                                null,
-                                                                               localCancelDate,
+                                                                               entitlementEffectiveDate,
+                                                                               billingEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
-
         final WithEntitlementPlugin<Entitlement> cancelEntitlementWithPlugin = new WithEntitlementPlugin<Entitlement>() {
 
             @Override
@@ -318,7 +343,8 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTime(localCancelDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
+
+                final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), contextWithValidAccountRecordId);
                 try {
                     if (overrideBillingEffectiveDate) {
                         getSubscriptionBase().cancelWithDate(effectiveCancelDate, callContext);
@@ -346,51 +372,76 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         return pluginExecution.executeWithPlugin(cancelEntitlementWithPlugin, pluginContext);
     }
 
-
     @Override
     public void uncancelEntitlement(final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
 
+        logUncancelEntitlement(log, this);
+
         checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
 
         // Get the latest state from disk
         refresh(callContext);
 
-        if (eventsStream.isSubscriptionCancelled()) {
-            throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
-        }
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.UNCANCEL_SUBSCRIPTION,
+                                                                               getAccountId(),
+                                                                               null,
+                                                                               getBundleId(),
+                                                                               getExternalKey(),
+                                                                               null,
+                                                                               null,
+                                                                               null,
+                                                                               null,
+                                                                               properties,
+                                                                               callContext);
 
-        final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
-        if (eventsStream.isEntitlementCancelled()) {
-            final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
-            blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
-        } else if (pendingEntitlementCancellationEvents.size() > 0) {
-            // Reactivate entitlements
-            // See also https://github.com/killbill/killbill/issues/111
-            //
-            // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
-            // one future event in the list
-            //
-            for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
-                blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
-            }
-        } else {
-            // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
-            throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
-        }
+        final WithEntitlementPlugin<Void> uncancelEntitlementWithPlugin = new WithEntitlementPlugin<Void>() {
 
-        // If billing was previously cancelled, reactivate
-        if (getSubscriptionBase().getFutureEndDate() != null) {
-            try {
-                getSubscriptionBase().uncancel(callContext);
-            } catch (final SubscriptionBaseApiException e) {
-                throw new EntitlementApiException(e);
+            @Override
+            public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+                if (eventsStream.isSubscriptionCancelled()) {
+                    throw new EntitlementApiException(ErrorCode.SUB_UNCANCEL_BAD_STATE, getId());
+                }
+
+                final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
+                final Collection<BlockingState> pendingEntitlementCancellationEvents = eventsStream.getPendingEntitlementCancellationEvents();
+                if (eventsStream.isEntitlementCancelled()) {
+                    final BlockingState cancellationEvent = eventsStream.getEntitlementCancellationEvent();
+                    blockingStateDao.unactiveBlockingState(cancellationEvent.getId(), contextWithValidAccountRecordId);
+                } else if (pendingEntitlementCancellationEvents.size() > 0) {
+                    // Reactivate entitlements
+                    // See also https://github.com/killbill/killbill/issues/111
+                    //
+                    // Today we only support cancellation at SUBSCRIPTION level (Not ACCOUNT or BUNDLE), so we should really have only
+                    // one future event in the list
+                    //
+                    for (final BlockingState futureCancellation : pendingEntitlementCancellationEvents) {
+                        blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+                    }
+                } else {
+                    // Entitlement is NOT cancelled (or future cancelled), there is nothing to do
+                    throw new EntitlementApiException(ErrorCode.ENT_UNCANCEL_BAD_STATE, getId());
+                }
+
+                // If billing was previously cancelled, reactivate
+                if (getSubscriptionBase().getFutureEndDate() != null) {
+                    try {
+                        getSubscriptionBase().uncancel(callContext);
+                    } catch (final SubscriptionBaseApiException e) {
+                        throw new EntitlementApiException(e);
+                    }
+                }
+                return null;
             }
-        }
+        };
+
+        pluginExecution.executeWithPlugin(uncancelEntitlementWithPlugin, pluginContext);
     }
 
     @Override
     public Entitlement cancelEntitlementWithPolicyOverrideBillingPolicy(final EntitlementActionPolicy entitlementPolicy, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logCancelEntitlement(log, this, null, null, entitlementPolicy, billingPolicy);
+
         // Get the latest state from disk - required to have the latest CTD
         refresh(callContext);
 
@@ -398,10 +449,11 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         return cancelEntitlementWithDateOverrideBillingPolicy(cancellationDate, billingPolicy, properties, callContext);
     }
 
-
     // See also EntitlementInternalApi#cancel for the bulk API
     @Override
-    public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(@Nullable final LocalDate localCancelDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(@Nullable final LocalDate entitlementEffectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logCancelEntitlement(log, this, entitlementEffectiveDate, null, null, billingPolicy);
 
         checkForPermissions(Permission.ENTITLEMENT_CAN_CANCEL, callContext);
 
@@ -414,7 +466,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                                                                                getBundleId(),
                                                                                getExternalKey(),
                                                                                null,
-                                                                               localCancelDate,
+                                                                               entitlementEffectiveDate,
+                                                                               null,
+                                                                               billingPolicy,
                                                                                properties,
                                                                                callContext);
 
@@ -429,7 +483,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 // (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
                 // computations won't work).
                 final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
 
                 try {
                     // Cancel subscription base first, to correctly compute the add-ons entitlements we need to cancel (see below)
@@ -438,14 +491,15 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                     throw new EntitlementApiException(e);
                 }
 
-                final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveDate);
+                final DateTime effectiveCancelDate = dateHelper.fromLocalDateAndReferenceTimeWithMinimum(entitlementEffectiveDate, getEventsStream().getEntitlementEffectiveStartDateTime(), contextWithValidAccountRecordId);
+                final BlockingState newBlockingState = new DefaultBlockingState(getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, effectiveCancelDate);
                 final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
-                final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveDate, notificationEvents, callContext, contextWithValidAccountRecordId);
+                final Collection<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveCancelDate, notificationEvents, callContext, contextWithValidAccountRecordId);
 
                 // Record the new state first, then insert the notifications to avoid race conditions
                 setBlockingStates(newBlockingState, addOnsBlockingStates, contextWithValidAccountRecordId);
                 for (final NotificationEvent notificationEvent : notificationEvents) {
-                    recordFutureNotification(effectiveDate, notificationEvent, contextWithValidAccountRecordId);
+                    recordFutureNotification(effectiveCancelDate, notificationEvent, contextWithValidAccountRecordId);
                 }
 
                 return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -462,7 +516,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 break;
             case END_OF_TERM:
                 if (getSubscriptionBase().getChargedThroughDate() != null) {
-                    cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), getSubscriptionBase().getStartDate());
+                    cancellationDate = internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate());
                 } else {
                     cancellationDate = clock.getToday(eventsStream.getAccountTimeZone());
                 }
@@ -474,8 +528,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     }
 
     @Override
-    public Entitlement changePlan(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement changePlan(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
 
+        logChangePlan(log, this, spec, overrides, null, null);
 
         checkForPermissions(Permission.ENTITLEMENT_CAN_CHANGE_PLAN, callContext);
 
@@ -489,6 +544,8 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                                                                                getExternalKey(),
                                                                                null,
                                                                                null,
+                                                                               null,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
@@ -503,9 +560,11 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
                 final DateTime effectiveChangeDate;
                 try {
-                    effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), productName, billingPeriod, priceList, null, null, context);
+                    effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), spec, null, null, overrides, context);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                } catch (final CatalogApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
                 }
 
                 try {
@@ -515,7 +574,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 try {
-                    getSubscriptionBase().changePlan(productName, billingPeriod, priceList, overrides, callContext);
+                    getSubscriptionBase().changePlan(spec, overrides, callContext);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
                 }
@@ -535,7 +594,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     }
 
     @Override
-    public Entitlement changePlanWithDate(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate localDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement changePlanWithDate(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logChangePlan(log, this, spec, overrides, effectiveDate, null);
 
         checkForPermissions(Permission.ENTITLEMENT_CAN_CHANGE_PLAN, callContext);
 
@@ -548,7 +609,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                                                                                getBundleId(),
                                                                                getExternalKey(),
                                                                                null,
-                                                                               localDate,
+                                                                               effectiveDate,
+                                                                               effectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
@@ -560,34 +623,36 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                final DateTime effectiveChangeDateComputed = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), context);
+                final DateTime effectiveChangeDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), context);
 
-                final DateTime effectiveChangeDate;
+                final DateTime resultingEffectiveDate;
                 try {
-                    effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), productName, billingPeriod, priceList, effectiveChangeDateComputed, null, context);
+                    resultingEffectiveDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), spec, effectiveChangeDate, null, overrides, context);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                } catch (final CatalogApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
                 }
 
                 try {
-                    checker.checkBlockedChange(getSubscriptionBase(), effectiveChangeDate, context);
+                    checker.checkBlockedChange(getSubscriptionBase(), resultingEffectiveDate, context);
                 } catch (final BlockingApiException e) {
                     throw new EntitlementApiException(e, e.getCode(), e.getMessage());
                 }
 
                 try {
-                    getSubscriptionBase().changePlanWithDate(productName, billingPeriod, priceList, overrides, effectiveChangeDate, callContext);
+                    getSubscriptionBase().changePlanWithDate(spec, overrides, resultingEffectiveDate, callContext);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
                 }
 
                 final Collection<NotificationEvent> notificationEvents = new ArrayList<NotificationEvent>();
-                final Iterable<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(effectiveChangeDate, notificationEvents, callContext, context);
+                final Iterable<BlockingState> addOnsBlockingStates = computeAddOnBlockingStates(resultingEffectiveDate, notificationEvents, callContext, context);
 
                 // Record the new state first, then insert the notifications to avoid race conditions
                 setBlockingStates(addOnsBlockingStates, context);
                 for (final NotificationEvent notificationEvent : notificationEvents) {
-                    recordFutureNotification(effectiveChangeDate, notificationEvent, context);
+                    recordFutureNotification(resultingEffectiveDate, notificationEvent, context);
                 }
 
                 return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -597,7 +662,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     }
 
     @Override
-    public Entitlement changePlanOverrideBillingPolicy(final String productName, final BillingPeriod billingPeriod, final String priceList, final List<PlanPhasePriceOverride> overrides, final LocalDate localDate, final BillingActionPolicy actionPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement changePlanOverrideBillingPolicy(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final LocalDate entitlementEffectiveDate, final BillingActionPolicy actionPolicy, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logChangePlan(log, this, spec, overrides, entitlementEffectiveDate, actionPolicy);
 
         checkForPermissions(Permission.ENTITLEMENT_CAN_CHANGE_PLAN, callContext);
 
@@ -610,7 +677,9 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                                                                                getBundleId(),
                                                                                getExternalKey(),
                                                                                null,
-                                                                               localDate,
+                                                                               entitlementEffectiveDate,
+                                                                               null,
+                                                                               actionPolicy,
                                                                                properties,
                                                                                callContext);
 
@@ -625,9 +694,11 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
                 final DateTime effectiveChangeDate;
                 try {
-                    effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), productName, billingPeriod, priceList, null, actionPolicy, context);
+                    effectiveChangeDate = subscriptionInternalApi.getDryRunChangePlanEffectiveDate(getSubscriptionBase(), spec, null, actionPolicy, overrides, context);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                } catch (final CatalogApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
                 }
 
                 try {
@@ -637,7 +708,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 try {
-                    getSubscriptionBase().changePlanWithPolicy(productName, billingPeriod, priceList, overrides, actionPolicy, callContext);
+                    getSubscriptionBase().changePlanWithPolicy(spec, overrides, actionPolicy, callContext);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
                 }
@@ -657,6 +728,19 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         return pluginExecution.executeWithPlugin(changePlanWithPlugin, pluginContext);
     }
 
+    @Override
+    public void updateBCD(final int newBCD, @Nullable final LocalDate effectiveFromDate, final CallContext callContext) throws EntitlementApiException {
+
+        logUpdateBCD(log, this, newBCD, effectiveFromDate);
+
+        final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
+        try {
+            subscriptionInternalApi.updateBCD(getId(), newBCD, effectiveFromDate, context);
+        } catch (final SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+    }
+
     private void refresh(final TenantContext context) throws EntitlementApiException {
         eventsStream = eventsStreamBuilder.refresh(eventsStream, context);
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index 67342e1..f26c167 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -47,6 +47,7 @@ import org.killbill.billing.entitlement.block.BlockingChecker;
 import org.killbill.billing.entitlement.dao.BlockingStateDao;
 import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
 import org.killbill.billing.entitlement.engine.core.EventsStreamBuilder;
+import org.killbill.billing.entitlement.logging.EntitlementLoggingHelper;
 import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
 import org.killbill.billing.entitlement.plugin.api.OperationType;
 import org.killbill.billing.junction.DefaultBlockingState;
@@ -71,8 +72,15 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlement;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlementWithAOs;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logPauseResumeEntitlement;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logTransferEntitlement;
+
 public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements EntitlementApi {
 
+
+    public static final String ENT_STATE_START = "ENT_STARTED";
     public static final String ENT_STATE_BLOCKED = "ENT_BLOCKED";
     public static final String ENT_STATE_CLEAR = "ENT_CLEAR";
     public static final String ENT_STATE_CANCELLED = "ENT_CANCELLED";
@@ -116,7 +124,11 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     }
 
     @Override
-    public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final List<PlanPhasePriceOverride> overrides,
+                                             @Nullable final LocalDate entitlementEffectiveDate, @Nullable  LocalDate billingEffectiveDate, final boolean isMigrated,
+                                             final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logCreateEntitlement(log, null, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
 
         final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
         final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
@@ -127,7 +139,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                null,
                                                                                externalKey,
                                                                                entitlementSpecifierList,
-                                                                               effectiveDate,
+                                                                               entitlementEffectiveDate,
+                                                                               billingEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
@@ -142,10 +156,13 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
                     final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
 
-                    final DateTime referenceTime = clock.getUTCNow();
-                    final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), referenceTime, contextWithValidAccountRecordId);
+                    final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithValidAccountRecordId);
                     final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(updatedPluginContext.getEntitlementSpecifiers());
-                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), requestedDate, contextWithValidAccountRecordId);
+                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundle.getId(), specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, contextWithValidAccountRecordId);
+
+                    final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithValidAccountRecordId);
+                    final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+                    entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), contextWithValidAccountRecordId);
 
                     return new DefaultEntitlement(accountId, subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
                                                   blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
@@ -167,19 +184,12 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
     @Override
     public Entitlement createBaseEntitlementWithAddOns(final UUID accountId, final String externalKey, final Iterable<EntitlementSpecifier> entitlementSpecifiers,
-                                                       @Nullable final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext)
+                                                       @Nullable LocalDate entitlementEffectiveDate,  @Nullable LocalDate billingEffectiveDate, final boolean isMigrated,
+                                                       final Iterable<PluginProperty> properties, final CallContext callContext)
             throws EntitlementApiException {
 
-        final EntitlementSpecifier baseSpecifier = Iterables.tryFind(entitlementSpecifiers, new Predicate<EntitlementSpecifier>() {
-            @Override
-            public boolean apply(final EntitlementSpecifier specifier) {
-                return specifier.getPlanPhaseSpecifier() != null && ProductCategory.BASE.equals(specifier.getPlanPhaseSpecifier().getProductCategory());
-            }
-        }).orNull();
+        logCreateEntitlementWithAOs(log, externalKey, entitlementSpecifiers, entitlementEffectiveDate, billingEffectiveDate);
 
-        if (baseSpecifier == null) {
-            throw new EntitlementApiException(new IllegalArgumentException(), ErrorCode.SUB_CREATE_NO_BP.getCode(), "Missing Base Subscription.");
-        }
 
         final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
         Iterables.addAll(entitlementSpecifierList, entitlementSpecifiers);
@@ -190,7 +200,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                null,
                                                                                externalKey,
                                                                                entitlementSpecifierList,
-                                                                               effectiveDate,
+                                                                               entitlementEffectiveDate,
+                                                                               billingEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
@@ -206,11 +218,20 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
                     final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.createBundleForAccount(accountId, externalKey, contextWithValidAccountRecordId);
 
-                    final DateTime referenceTime = clock.getUTCNow();
-                    final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), referenceTime, contextWithValidAccountRecordId);
-                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createBaseSubscriptionWithAddOns(bundle.getId(), entitlementSpecifiers, requestedDate, contextWithValidAccountRecordId);
+                    final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithValidAccountRecordId);
+                    final List<SubscriptionBase> subscriptionBases = subscriptionBaseInternalApi.createBaseSubscriptionWithAddOns(bundle.getId(), entitlementSpecifiers, billingRequestedDate, isMigrated, contextWithValidAccountRecordId);
 
-                    return new DefaultEntitlement(accountId, subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
+                    final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithValidAccountRecordId);
+                    final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+                    for (final SubscriptionBase cur : subscriptionBases) {
+                        final BlockingState blockingState = new DefaultBlockingState(cur.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+                        blockingStates.add(blockingState);
+                    }
+                    final SubscriptionBase subscriptionBaseBP = subscriptionBases.get(0);
+                    entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(blockingStates, subscriptionBaseBP.getBundleId(), contextWithValidAccountRecordId);
+
+
+                    return new DefaultEntitlement(accountId, subscriptionBaseBP.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
                                                   blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
                                                   entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
 
@@ -224,7 +245,11 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     }
 
     @Override
-    public Entitlement addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+    public Entitlement addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate,
+            final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+
+        logCreateEntitlement(log, bundleId, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
 
         final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
         final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
@@ -235,7 +260,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                bundleId,
                                                                                null,
                                                                                entitlementSpecifierList,
-                                                                               effectiveDate,
+                                                                               entitlementEffectiveDate,
+                                                                               billingEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                callContext);
 
@@ -254,12 +281,16 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                     throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
                 }
 
-                final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), eventsStreamForBaseSubscription.getSubscriptionBase().getStartDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
-
+                final DateTime billingRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
                 try {
-                    final InternalCallContext context = internalCallContextFactory.createInternalCallContext(callContext);
+                    final InternalCallContext context = internalCallContextFactory.createInternalCallContext(eventsStreamForBaseSubscription.getAccountId(), callContext);
                     final EntitlementSpecifier specifier = getFirstEntitlementSpecifier(updatedPluginContext.getEntitlementSpecifiers());
-                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), requestedDate, context);
+                    final SubscriptionBase subscription = subscriptionBaseInternalApi.createSubscription(bundleId, specifier.getPlanPhaseSpecifier(), specifier.getOverrides(), billingRequestedDate, isMigrated, context);
+
+                    final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
+                    final BlockingState newBlockingState = new DefaultBlockingState(subscription.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+                    entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscription.getBundleId(), context);
+
 
                     return new DefaultEntitlement(eventsStreamForBaseSubscription.getAccountId(), subscription.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
                                                   blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
@@ -280,7 +311,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
             final SubscriptionBase baseSubscription = subscriptionBaseInternalApi.getBaseSubscription(bundleId, internalContext);
 
             final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(bundle.getAccountId(), context);
-            final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, baseSubscription.getStartDate(), contextWithValidAccountRecordId);
+            final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, contextWithValidAccountRecordId);
             return subscriptionBaseInternalApi.getDryRunChangePlanStatus(baseSubscription.getId(), targetProductName, requestedDate, contextWithValidAccountRecordId);
         } catch (final SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
@@ -314,7 +345,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
     @Override
     public List<Entitlement> getAllEntitlementsForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext tenantContext) throws EntitlementApiException {
-        // getAllEntitlementsForAccountId should be fast (uses account_record_id)
+        // getAllEntitlementsForAccount should be fast (uses account_record_id)
         return ImmutableList.<Entitlement>copyOf(Iterables.<Entitlement>filter(getAllEntitlementsForAccountId(accountId, tenantContext),
                                                                                new Predicate<Entitlement>() {
                                                                                    @Override
@@ -345,6 +376,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     @Override
     public void pause(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
 
+        logPauseResumeEntitlement(log, "Pause", bundleId, localEffectiveDate);
+
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
         super.pause(bundleId, localEffectiveDate, properties, contextWithValidAccountRecordId);
     }
@@ -352,22 +385,11 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     @Override
     public void resume(final UUID bundleId, final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
 
-        final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
-        super.resume(bundleId, localEffectiveDate, properties, contextWithValidAccountRecordId);
-
-    }
+        logPauseResumeEntitlement(log, "Resume", bundleId, localEffectiveDate);
 
-    @Override
-    public void setBlockingState(final UUID bundleId, final String stateName, final String serviceName, final LocalDate effectiveDate, final boolean blockBilling, final boolean blockEntitlement, final boolean blockChange, final Iterable<PluginProperty> properties, final CallContext context)
-            throws EntitlementApiException {
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
-        super.setBlockingState(bundleId, stateName, serviceName, effectiveDate, blockBilling, blockEntitlement, blockChange, properties, contextWithValidAccountRecordId);
-    }
+        super.resume(bundleId, localEffectiveDate, properties, contextWithValidAccountRecordId);
 
-    @Override
-    public Iterable<BlockingState> getBlockingStatesForServiceAndType(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final TenantContext tenantContext) {
-        // Not implemented see #431
-        return null;
     }
 
     @Override
@@ -378,6 +400,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     @Override
     public UUID transferEntitlementsOverrideBillingPolicy(final UUID sourceAccountId, final UUID destAccountId, final String externalKey, @Nullable final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
 
+        logTransferEntitlement(log, sourceAccountId, destAccountId, externalKey, effectiveDate, billingPolicy);
+
         final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.TRANSFER_BUNDLE,
                                                                                sourceAccountId,
                                                                                destAccountId,
@@ -385,6 +409,8 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                externalKey,
                                                                                new ArrayList<EntitlementSpecifier>(),
                                                                                effectiveDate,
+                                                                               effectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                context);
 
@@ -403,30 +429,44 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                         throw new RuntimeException("Unexpected billing policy " + billingPolicy);
                 }
 
-                final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
+                final InternalCallContext contextWithSourceAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
                 try {
 
-                    final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithValidAccountRecordId);
+                    final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, contextWithSourceAccountRecordId);
                     final SubscriptionBase baseSubscription = activeSubscriptionIdForKey != null ?
-                                                              subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithValidAccountRecordId) : null;
+                                                              subscriptionBaseInternalApi.getSubscriptionFromId(activeSubscriptionIdForKey, contextWithSourceAccountRecordId) : null;
                     final SubscriptionBaseBundle baseBundle = baseSubscription != null ?
-                                                              subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithValidAccountRecordId) : null;
+                                                              subscriptionBaseInternalApi.getBundleFromId(baseSubscription.getBundleId(), contextWithSourceAccountRecordId) : null;
 
                     if (baseBundle == null || !baseBundle.getAccountId().equals(sourceAccountId)) {
                         throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey));
                     }
 
-                    final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), contextWithValidAccountRecordId);
+                    final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getBillingEffectiveDate(), contextWithSourceAccountRecordId);
                     final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, externalKey, requestedDate, true, cancelImm, context);
 
+
+                    final Map<BlockingState, UUID> blockingStates = new HashMap<BlockingState, UUID>();
+
                     // Block all associated subscriptions - TODO Do we want to block the bundle as well (this will add an extra STOP_ENTITLEMENT event in the bundle timeline stream)?
                     // Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
-                    final Map<BlockingState, UUID> blockingStates = new HashMap<BlockingState, UUID>();
-                    for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), null, contextWithValidAccountRecordId)) {
+                    for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(baseBundle.getId(), null, contextWithSourceAccountRecordId)) {
                         final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, false, requestedDate);
                         blockingStates.put(blockingState, subscriptionBase.getBundleId());
                     }
-                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithValidAccountRecordId);
+                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithSourceAccountRecordId);
+
+                    // Add blocking events for transferred subscriptions..
+                    final InternalCallContext contextWithDestAccountRecordId = internalCallContextFactory.createInternalCallContext(destAccountId, context);
+
+                    blockingStates.clear();
+                    final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), contextWithDestAccountRecordId);
+                    for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, contextWithDestAccountRecordId)) {
+                        final BlockingState newBlockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
+                        blockingStates.put(newBlockingState, subscriptionBase.getBundleId());
+                    }
+                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithDestAccountRecordId);
+
 
                     return newBundle.getId();
                 } catch (final SubscriptionBaseTransferApiException e) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
index 000c1a0..d5ec969 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementContext.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
 import org.killbill.billing.entitlement.plugin.api.OperationType;
 import org.killbill.billing.entitlement.plugin.api.PriorEntitlementResult;
@@ -41,7 +42,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
     private final UUID bundleId;
     private final String externalKey;
     private final List<EntitlementSpecifier> entitlementSpecifiers;
-    private final LocalDate effectiveDate;
+    private final LocalDate entitlementEffectiveDate;
+    private final LocalDate billingEffectiveDate;
+    private final BillingActionPolicy billingActionPolicy;
     private final Iterable<PluginProperty> pluginProperties;
     private final UUID userToken;
     private final String userName;
@@ -62,7 +65,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
              prev.getBundleId(),
              prev.getExternalKey(),
              pluginResult != null && pluginResult.getAdjustedEntitlementSpecifiers() != null ? pluginResult.getAdjustedEntitlementSpecifiers() : prev.getEntitlementSpecifiers(),
-             pluginResult != null && pluginResult.getAdjustedEffectiveDate() != null ? pluginResult.getAdjustedEffectiveDate() : prev.getEffectiveDate(),
+             pluginResult != null && pluginResult.getAdjustedEntitlementEffectiveDate() != null ? pluginResult.getAdjustedEntitlementEffectiveDate() : prev.getEntitlementEffectiveDate(),
+             pluginResult != null && pluginResult.getAdjustedBillingEffectiveDate() != null ? pluginResult.getAdjustedBillingEffectiveDate() : prev.getBillingEffectiveDate(),
+             pluginResult != null && pluginResult.getAdjustedBillingActionPolicy() != null ? pluginResult.getAdjustedBillingActionPolicy() : prev.getBillingActionPolicy(),
              pluginResult != null && pluginResult.getAdjustedPluginProperties() != null ? pluginResult.getAdjustedPluginProperties() : prev.getPluginProperties(),
              prev);
     }
@@ -73,10 +78,12 @@ public class DefaultEntitlementContext implements EntitlementContext {
                                      final UUID bundleId,
                                      final String externalKey,
                                      final List<EntitlementSpecifier> entitlementSpecifiers,
-                                     @Nullable final LocalDate effectiveDate,
+                                     @Nullable final LocalDate entitlementEffectiveDate,
+                                     @Nullable final LocalDate billingEffectiveDate,
+                                     @Nullable final BillingActionPolicy actionPolicy,
                                      final Iterable<PluginProperty> pluginProperties,
                                      final CallContext callContext) {
-        this(operationType, accountId, destinationAccountId, bundleId, externalKey, entitlementSpecifiers, effectiveDate, pluginProperties,
+        this(operationType, accountId, destinationAccountId, bundleId, externalKey, entitlementSpecifiers, entitlementEffectiveDate, billingEffectiveDate, actionPolicy, pluginProperties,
              callContext.getUserToken(), callContext.getUserName(), callContext.getCallOrigin(), callContext.getUserType(), callContext.getReasonCode(),
              callContext.getComments(), callContext.getCreatedDate(), callContext.getUpdatedDate(), callContext.getTenantId());
     }
@@ -88,7 +95,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
                                      final UUID bundleId,
                                      final String externalKey,
                                      final List<EntitlementSpecifier> entitlementSpecifiers,
-                                     @Nullable final LocalDate effectiveDate,
+                                     @Nullable final LocalDate entitlementEffectiveDate,
+                                     @Nullable final LocalDate billingEffectiveDate,
+                                     @Nullable final BillingActionPolicy actionPolicy,
                                      final Iterable<PluginProperty> pluginProperties,
                                      final UUID userToken,
                                      final String userName,
@@ -105,7 +114,9 @@ public class DefaultEntitlementContext implements EntitlementContext {
         this.bundleId = bundleId;
         this.externalKey = externalKey;
         this.entitlementSpecifiers = entitlementSpecifiers;
-        this.effectiveDate = effectiveDate;
+        this.entitlementEffectiveDate = entitlementEffectiveDate;
+        this.billingEffectiveDate = billingEffectiveDate;
+        this.billingActionPolicy = actionPolicy;
         this.pluginProperties = pluginProperties;
         this.userToken = userToken;
         this.userName = userName;
@@ -148,9 +159,20 @@ public class DefaultEntitlementContext implements EntitlementContext {
         return entitlementSpecifiers;
     }
 
+
+    @Override
+    public LocalDate getEntitlementEffectiveDate() {
+        return entitlementEffectiveDate;
+    }
+
+    @Override
+    public LocalDate getBillingEffectiveDate() {
+        return billingEffectiveDate;
+    }
+
     @Override
-    public LocalDate getEffectiveDate() {
-        return effectiveDate;
+    public BillingActionPolicy getBillingActionPolicy() {
+        return billingActionPolicy;
     }
 
     @Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
index 9da4080..0d7dc82 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
@@ -31,7 +31,7 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
 
     @Override
     public LocalDate getBillingStartDate() {
-        return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate(), getSubscriptionBase().getStartDate());
+        return internalTenantContext.toLocalDate(getSubscriptionBase().getStartDate());
     }
 
     @Override
@@ -51,12 +51,12 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
             futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
         }
 
-        return futureOrCurrentEndDate != null ? internalTenantContext.toLocalDate(futureOrCurrentEndDate, getSubscriptionBase().getStartDate()) : null;
+        return futureOrCurrentEndDate != null ? internalTenantContext.toLocalDate(futureOrCurrentEndDate) : null;
     }
 
     @Override
     public LocalDate getChargedThroughDate() {
-        return getSubscriptionBase().getChargedThroughDate() != null ? internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate(), getSubscriptionBase().getStartDate()) : null;
+        return getSubscriptionBase().getChargedThroughDate() != null ? internalTenantContext.toLocalDate(getSubscriptionBase().getChargedThroughDate()) : null;
     }
 
     @Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
index 959435f..a6fbfb9 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -26,16 +26,29 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
-import org.joda.time.DateTimeZone;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.AccountEntitlements;
 import org.killbill.billing.entitlement.EntitlementInternalApi;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
+import org.killbill.billing.entitlement.dao.BlockingStateDao;
 import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
+import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
+import org.killbill.billing.entitlement.plugin.api.OperationType;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -46,6 +59,7 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.customfield.ShouldntHappenException;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
+import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,8 +68,11 @@ import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logAddBlockingState;
+import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logUpdateExternalKey;
 import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationNoException;
 
 public class DefaultSubscriptionApi implements SubscriptionApi {
@@ -73,24 +90,38 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
                 if (compared2 != 0) {
                     return compared2;
                 } else {
-                    // Default, stable, ordering
+                    // Default stable ordering (in the sense that doing twice the same call will lead to same result)
                     return o1.getId().compareTo(o2.getId());
                 }
             }
         }
     };
 
+    private final AccountInternalApi accountApi;
     private final EntitlementInternalApi entitlementInternalApi;
     private final SubscriptionBaseInternalApi subscriptionBaseInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
     private final EntitlementUtils entitlementUtils;
+    private final Clock clock;
+    private final EntitlementPluginExecution pluginExecution;
+    private final BlockingStateDao blockingStateDao;
 
     @Inject
-    public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
-                                  final InternalCallContextFactory internalCallContextFactory, final EntitlementUtils entitlementUtils) {
+    public DefaultSubscriptionApi(final AccountInternalApi accountApi,
+                                  final EntitlementInternalApi entitlementInternalApi,
+                                  final SubscriptionBaseInternalApi subscriptionInternalApi,
+                                  final InternalCallContextFactory internalCallContextFactory,
+                                  final Clock clock,
+                                  final EntitlementPluginExecution pluginExecution,
+                                  final BlockingStateDao blockingStateDao,
+                                  final EntitlementUtils entitlementUtils) {
+        this.accountApi = accountApi;
         this.entitlementInternalApi = entitlementInternalApi;
         this.subscriptionBaseInternalApi = subscriptionInternalApi;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.clock = clock;
+        this.pluginExecution = pluginExecution;
+        this.blockingStateDao = blockingStateDao;
         this.entitlementUtils = entitlementUtils;
     }
 
@@ -102,7 +133,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
         try {
             final UUID accountId = internalCallContextFactory.getAccountId(entitlementId, ObjectType.SUBSCRIPTION, tenantContext);
             final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
-            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccount(internalTenantContextWithValidAccountRecordId);
         } catch (final EntitlementApiException e) {
             throw new SubscriptionApiException(e);
         }
@@ -138,12 +169,6 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
     }
 
     @Override
-    public void updateExternalKey(final UUID uuid, final String newExternalKey, final CallContext callContext) {
-        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(callContext);
-        subscriptionBaseInternalApi.updateExternalKey(uuid, newExternalKey, internalContext);
-    }
-
-    @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
         return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
                                                                                              new Predicate<SubscriptionBundle>() {
@@ -156,7 +181,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
 
     @Override
     public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         try {
             final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(externalKey, internalContext);
             if (activeSubscriptionIdForKey == null) {
@@ -171,7 +196,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
 
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         final List<SubscriptionBaseBundle> baseBundles = subscriptionBaseInternalApi.getBundlesForKey(externalKey, internalContext);
 
         final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
@@ -179,8 +204,8 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
             final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
             result.add(bundle);
         }
-
-        return result;
+        // Sorting by createdDate will likely place the active bundle last, but this is the same ordering we already use for getSubscriptionBundlesForAccount
+        return Ordering.from(SUBSCRIPTION_BUNDLE_COMPARATOR).sortedCopy(result);
     }
 
     @Override
@@ -190,7 +215,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
 
     @Override
     public Pagination<SubscriptionBundle> getSubscriptionBundles(final Long offset, final Long limit, final TenantContext context) {
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         return getEntityPaginationNoException(limit,
                                               new SourcePaginationBuilder<SubscriptionBaseBundle, SubscriptionApiException>() {
                                                   @Override
@@ -214,7 +239,7 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
 
     @Override
     public Pagination<SubscriptionBundle> searchSubscriptionBundles(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         return getEntityPaginationNoException(limit,
                                               new SourcePaginationBuilder<SubscriptionBaseBundle, SubscriptionApiException>() {
                                                   @Override
@@ -236,13 +261,186 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
                                              );
     }
 
+    @Override
+    public void updateExternalKey(final UUID bundleId, final String newExternalKey, final CallContext callContext) throws EntitlementApiException {
+
+        logUpdateExternalKey(log, bundleId, newExternalKey);
+
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(callContext);
+
+        final SubscriptionBaseBundle bundle;
+        final ImmutableAccountData account;
+        try {
+            bundle = subscriptionBaseInternalApi.getBundleFromId(bundleId, internalCallContext);
+            account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
+        } catch (final SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        } catch (AccountApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        final LocalDate effectiveDate = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.UPDATE_BUNDLE_EXTERNAL_KEY,
+                                                                               bundle.getAccountId(),
+                                                                               null,
+                                                                               bundleId,
+                                                                               newExternalKey,
+                                                                               new ArrayList<EntitlementSpecifier>(),
+                                                                               effectiveDate,
+                                                                               effectiveDate,
+                                                                               null,
+                                                                               ImmutableList.<PluginProperty>of(),
+                                                                               callContext);
+
+        final WithEntitlementPlugin<Void> updateExternalKeyWithPlugin = new WithEntitlementPlugin<Void>() {
+
+            final InternalCallContext internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+            @Override
+            public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+                subscriptionBaseInternalApi.updateExternalKey(bundleId, newExternalKey, internalCallContextWithValidAccountId);
+                return null;
+            }
+        };
+        pluginExecution.executeWithPlugin(updateExternalKeyWithPlugin, pluginContext);
+    }
+
+    @Override
+    public void addBlockingState(final BlockingState inputBlockingState, @Nullable final LocalDate inputEffectiveDate, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
+
+        logAddBlockingState(log, inputBlockingState, inputEffectiveDate);
+
+        // This is in no way an exhaustive arg validation, but to to ensure plugin would not hijack private entitlement state or service name
+        if (inputBlockingState.getService() == null || inputBlockingState.getService().equals(EntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+            throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid serviceName");
+        }
+
+        if (inputBlockingState.getStateName() == null ||
+            inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED) ||
+            inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_BLOCKED) ||
+            inputBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CLEAR)) {
+            throw new EntitlementApiException(ErrorCode.SUB_BLOCKING_STATE_INVALID_ARG, "Need to specify a valid stateName");
+        }
+
+        final InternalCallContext internalCallContextWithValidAccountId;
+        final ImmutableAccountData account;
+        final UUID accountId;
+        final UUID bundleId;
+        final String externalKey;
+        try {
+            switch (inputBlockingState.getType()) {
+                case ACCOUNT:
+                    internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.ACCOUNT, callContext);
+                    account = accountApi.getImmutableAccountDataById(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
+                    externalKey = account.getExternalKey();
+                    accountId = account.getId();
+                    bundleId = null;
+                    break;
+
+                case SUBSCRIPTION_BUNDLE:
+                    internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.BUNDLE, callContext);
+                    final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getBundleFromId(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
+                    externalKey = bundle.getExternalKey();
+                    bundleId = bundle.getId();
+                    accountId = bundle.getAccountId();
+                    break;
+
+                case SUBSCRIPTION:
+                    internalCallContextWithValidAccountId = internalCallContextFactory.createInternalCallContext(inputBlockingState.getBlockedId(), ObjectType.SUBSCRIPTION, callContext);
+                    final Entitlement entitlement = entitlementInternalApi.getEntitlementForId(inputBlockingState.getBlockedId(), internalCallContextWithValidAccountId);
+                    bundleId = entitlement.getBundleId();
+                    accountId = entitlement.getAccountId();
+                    externalKey = null;
+                    break;
+
+                default:
+                    throw new IllegalStateException("Invalid blockingStateType " + inputBlockingState.getType());
+            }
+        } catch (final AccountApiException e) {
+            throw new EntitlementApiException(e);
+        } catch (final SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        final DateTime effectiveDate = inputEffectiveDate == null ? clock.getUTCNow() : internalCallContextWithValidAccountId.toUTCDateTime(inputEffectiveDate);
+        final DefaultBlockingState blockingState = new DefaultBlockingState(inputBlockingState, effectiveDate);
+
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.ADD_BLOCKING_STATE,
+                                                                               accountId,
+                                                                               null,
+                                                                               bundleId,
+                                                                               externalKey,
+                                                                               new ArrayList<EntitlementSpecifier>(),
+                                                                               internalCallContextWithValidAccountId.toLocalDate(effectiveDate),
+                                                                               null,
+                                                                               null,
+                                                                               properties,
+                                                                               callContext);
+
+        final WithEntitlementPlugin<Void> addBlockingStateWithPlugin = new WithEntitlementPlugin<Void>() {
+
+            @Override
+            public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
+                entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingState, internalCallContextWithValidAccountId);
+                return null;
+            }
+        };
+        pluginExecution.executeWithPlugin(addBlockingStateWithPlugin, pluginContext);
+    }
+
+    @Override
+    public Iterable<BlockingState> getBlockingStates(final UUID accountId, @Nullable final List<BlockingStateType> typeFilter, @Nullable final List<String> svcsFilter, final OrderingType orderingType, final int timeFilter, final TenantContext tenantContext) throws EntitlementApiException {
+
+        try {
+
+            final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+            final List<BlockingState> allBlockingStates = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithValidAccountRecordId);
+
+            final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, internalTenantContextWithValidAccountRecordId);
+
+            final Iterable<BlockingState> filteredByTypes = typeFilter != null && !typeFilter.isEmpty() ?
+                                                            Iterables.filter(allBlockingStates, new Predicate<BlockingState>() {
+                                                                @Override
+                                                                public boolean apply(final BlockingState input) {
+                                                                    return typeFilter.contains(input.getType());
+                                                                }
+                                                            }) : allBlockingStates;
+
+            final Iterable<BlockingState> filteredByTypesAndSvcs = svcsFilter != null && !svcsFilter.isEmpty() ?
+                                                                   Iterables.filter(filteredByTypes, new Predicate<BlockingState>() {
+                                                                       @Override
+                                                                       public boolean apply(final BlockingState input) {
+                                                                           return svcsFilter.contains(input.getService());
+                                                                       }
+                                                                   }) : filteredByTypes;
+
+            final LocalDate localDateNowInAccountTimezone = new LocalDate(clock.getUTCNow(), account.getTimeZone());
+            final List<BlockingState> result = new ArrayList<BlockingState>();
+            for (final BlockingState cur : filteredByTypesAndSvcs) {
+
+                final LocalDate eventDate = new LocalDate(cur.getEffectiveDate(), account.getTimeZone());
+                final int comp = eventDate.compareTo(localDateNowInAccountTimezone);
+                if ((comp <= 1 && ((timeFilter & SubscriptionApi.PAST_EVENTS) == SubscriptionApi.PAST_EVENTS)) ||
+                    (comp == 0 && ((timeFilter & SubscriptionApi.PRESENT_EVENTS) == SubscriptionApi.PRESENT_EVENTS)) ||
+                    (comp >= 1 && ((timeFilter & SubscriptionApi.FUTURE_EVENTS) == SubscriptionApi.FUTURE_EVENTS))) {
+                    result.add(cur);
+                }
+            }
+
+            return orderingType == OrderingType.ASCENDING ? result : Lists.reverse(result);
+
+        } catch (AccountApiException e) {
+            throw new EntitlementApiException(e);
+        }
+    }
+
     private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
         final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
 
         // Retrieve entitlements
         final AccountEntitlements accountEntitlements;
         try {
-            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, internalTenantContextWithValidAccountRecordId);
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccount(internalTenantContextWithValidAccountRecordId);
         } catch (final EntitlementApiException e) {
             throw new SubscriptionApiException(e);
         }
@@ -250,8 +448,6 @@ public class DefaultSubscriptionApi implements SubscriptionApi {
         // Build subscriptions
         final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
 
-        final DateTimeZone accountTimeZone = accountEntitlements.getAccount().getTimeZone();
-
         // Build subscription bundles
         final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
         for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
index 0bd996d..8e6b127 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscriptionEvent.java
@@ -21,7 +21,6 @@ package org.killbill.billing.entitlement.api;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
@@ -30,6 +29,7 @@ import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 
+
 public class DefaultSubscriptionEvent implements SubscriptionEvent {
 
     private final UUID id;
@@ -52,7 +52,6 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
     private final PriceList nextPriceList;
     private final BillingPeriod nextBillingPeriod;
     private final DateTime createdDate;
-    private final DateTime referenceTime;
     private final InternalTenantContext internalTenantContext;
 
     public DefaultSubscriptionEvent(final UUID id,
@@ -74,7 +73,6 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
                                     final PriceList nextPriceList,
                                     final BillingPeriod nextBillingPeriod,
                                     final DateTime createDate,
-                                    final DateTime referenceTime,
                                     final InternalTenantContext internalTenantContext) {
         this.id = id;
         this.entitlementId = entitlementId;
@@ -96,7 +94,6 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
         this.nextPriceList = nextPriceList;
         this.nextBillingPeriod = nextBillingPeriod;
         this.createdDate = createDate;
-        this.referenceTime = referenceTime;
         this.internalTenantContext = internalTenantContext;
     }
 
@@ -120,12 +117,7 @@ public class DefaultSubscriptionEvent implements SubscriptionEvent {
 
     @Override
     public LocalDate getEffectiveDate() {
-        return effectiveDate != null ? internalTenantContext.toLocalDate(effectiveDate, referenceTime) : null;
-    }
-
-    @Override
-    public LocalDate getRequestedDate() {
-        return requestedDate != null ? internalTenantContext.toLocalDate(requestedDate, referenceTime) : null;
+        return effectiveDate != null ? internalTenantContext.toLocalDate(effectiveDate) : null;
     }
 
     @Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
index 9ab7b85..21845a5 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
@@ -23,7 +23,6 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.clock.Clock;
 
@@ -35,8 +34,14 @@ public class EntitlementDateHelper {
         this.clock = clock;
     }
 
-    public DateTime fromLocalDateAndReferenceTime(@Nullable final LocalDate requestedDate, final DateTime referenceDateTime, final InternalTenantContext callContext) throws EntitlementApiException {
-        return requestedDate == null ? clock.getUTCNow() : callContext.toUTCDateTime(requestedDate, referenceDateTime);
+    public DateTime fromLocalDateAndReferenceTime(@Nullable final LocalDate requestedDate, final InternalTenantContext callContext) throws EntitlementApiException {
+        return requestedDate == null ? clock.getUTCNow() : callContext.toUTCDateTime(requestedDate);
+    }
+
+
+    public DateTime fromLocalDateAndReferenceTimeWithMinimum(@Nullable final LocalDate requestedDate, final DateTime min, final InternalTenantContext callContext) throws EntitlementApiException {
+        final DateTime candidate = fromLocalDateAndReferenceTime(requestedDate, callContext);
+        return candidate.compareTo(min) < 0 ? min : candidate;
     }
 
     /**
@@ -48,9 +53,9 @@ public class EntitlementDateHelper {
      * @return true if the inputDate, once converted into a LocalDate using account timezone is less or equals than today
      */
     // TODO Move to ClockUtils
-    public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTime referenceDatetime, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
+    public boolean isBeforeOrEqualsToday(final DateTime inputDate, final DateTimeZone accountTimeZone, final InternalTenantContext internalTenantContext) {
         final LocalDate localDateNowInAccountTimezone = clock.getToday(accountTimeZone);
-        final LocalDate targetDateInAccountTimezone = internalTenantContext.toLocalDate(inputDate, referenceDatetime);
+        final LocalDate targetDateInAccountTimezone = internalTenantContext.toLocalDate(inputDate);
         return targetDateInAccountTimezone.compareTo(localDateNowInAccountTimezone) <= 0;
     }
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index b6614b5..9283363 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
@@ -24,7 +24,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
 import org.killbill.billing.subscription.api.SubscriptionBase;
@@ -80,7 +79,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
             for (final SubscriptionBaseTransition tr : baseTransitions) {
                 final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
                 for (final SubscriptionEventType eventType : eventTypes) {
-                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, base.getStartDate(), internalTenantContext);
+                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, internalTenantContext);
                     insertSubscriptionEvent(event, result);
                 }
             }
@@ -92,12 +91,8 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
     private List<SubscriptionEventType> toEventTypes(final SubscriptionBaseTransitionType in) {
         switch (in) {
             case CREATE:
-                return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT, SubscriptionEventType.START_BILLING);
+                return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_BILLING);
             case TRANSFER:
-                return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT, SubscriptionEventType.START_BILLING);
-            case MIGRATE_ENTITLEMENT:
-                return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_ENTITLEMENT);
-            case MIGRATE_BILLING:
                 return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.START_BILLING);
             case CHANGE:
                 return ImmutableList.<SubscriptionEventType>of(SubscriptionEventType.CHANGE);
@@ -151,7 +146,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
         result.add(index, event);
     }
 
-    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTime referenceTime, final InternalTenantContext internalTenantContext) {
+    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final InternalTenantContext internalTenantContext) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
                                             in.getEffectiveTransitionTime(),
@@ -171,7 +166,6 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
                                             in.getNextPriceList(),
                                             (in.getNextPlan() != null ? in.getNextPlan().getRecurringBillingPeriod() : null),
                                             in.getCreatedDate(),
-                                            referenceTime,
                                             internalTenantContext);
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 7d1ef3d..3044aca 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -17,7 +17,6 @@
 
 package org.killbill.billing.entitlement.api.svcs;
 
-import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -28,21 +27,15 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.killbill.billing.ErrorCode;
-import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.AccountEntitlements;
 import org.killbill.billing.entitlement.AccountEventsStreams;
-import org.killbill.billing.entitlement.DefaultEntitlementService;
 import org.killbill.billing.entitlement.EntitlementService;
-import org.killbill.billing.entitlement.EntitlementTransitionType;
 import org.killbill.billing.entitlement.EventsStream;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
-import org.killbill.billing.entitlement.api.DefaultEffectiveEntitlementEvent;
 import org.killbill.billing.entitlement.api.DefaultEntitlement;
 import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
 import org.killbill.billing.entitlement.api.DefaultEntitlementContext;
@@ -54,8 +47,6 @@ import org.killbill.billing.entitlement.api.EntitlementPluginExecution;
 import org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin;
 import org.killbill.billing.entitlement.block.BlockingChecker;
 import org.killbill.billing.entitlement.dao.BlockingStateDao;
-import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKey;
-import org.killbill.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import org.killbill.billing.entitlement.engine.core.EntitlementUtils;
 import org.killbill.billing.entitlement.engine.core.EventsStreamBuilder;
 import org.killbill.billing.entitlement.plugin.api.EntitlementContext;
@@ -66,15 +57,10 @@ import org.killbill.billing.security.api.SecurityApi;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.bus.api.PersistentBus;
-import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.clock.Clock;
-import org.killbill.notificationq.api.NotificationEvent;
-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;
 
@@ -82,7 +68,7 @@ import com.google.common.collect.ImmutableList;
 
 public class DefaultEntitlementApiBase {
 
-    private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementApiBase.class);
+    protected static final Logger log = LoggerFactory.getLogger(DefaultEntitlementApiBase.class);
 
     protected final EntitlementApi entitlementApi;
     protected final AccountInternalApi accountApi;
@@ -123,7 +109,7 @@ public class DefaultEntitlementApiBase {
         this.dateHelper = new EntitlementDateHelper(clock);
     }
 
-    public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final InternalTenantContext tenantContext) throws EntitlementApiException {
+    public AccountEntitlements getAllEntitlementsForAccount(final InternalTenantContext tenantContext) throws EntitlementApiException {
         final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(tenantContext);
 
         final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
@@ -152,13 +138,15 @@ public class DefaultEntitlementApiBase {
 
     public void pause(final UUID bundleId, @Nullable final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
 
-        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_SUBSCRIPTION,
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.PAUSE_BUNDLE,
                                                                                null,
                                                                                null,
                                                                                bundleId,
                                                                                null,
                                                                                null,
                                                                                localEffectiveDate,
+                                                                               localEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                internalCallContextFactory.createCallContext(internalCallContext));
 
@@ -166,36 +154,10 @@ public class DefaultEntitlementApiBase {
             @Override
             public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
                 try {
-
-                    final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalCallContext);
-                    final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
                     final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
-                    final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
-
-                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, baseSubscription.getStartDate(), account.getTimeZone(), internalCallContext)) {
-                        recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, true, internalCallContext);
-                        return null;
-                    }
-
-                    final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, true, true, true, baseSubscription, internalCallContext);
-
-                    // Should we send one event per entitlement in the bundle?
-                    // Code below only sends one event for the bundle and use the base entitlementId
-                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.BLOCK_BUNDLE,
-                                                                                                        effectiveDate, clock.getUTCNow(),
-                                                                                                        internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
-                                                                                                        internalCallContext.getUserToken());
-
-                    try {
-                        eventBus.post(event);
-                    } catch (EventBusException e) {
-                        log.warn("Failed to post event {}", event, e);
-                    }
-
+                    blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, true, true, true, baseSubscription, internalCallContext);
                 } catch (SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
-                } catch (AccountApiException e) {
-                    throw new EntitlementApiException(e);
                 }
                 return null;
             }
@@ -205,49 +167,25 @@ public class DefaultEntitlementApiBase {
 
     public void resume(final UUID bundleId, @Nullable final LocalDate localEffectiveDate, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext) throws EntitlementApiException {
 
-        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_SUBSCRIPTION,
+        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.RESUME_BUNDLE,
                                                                                null,
                                                                                null,
                                                                                bundleId,
                                                                                null,
                                                                                null,
                                                                                localEffectiveDate,
+                                                                               localEffectiveDate,
+                                                                               null,
                                                                                properties,
                                                                                internalCallContextFactory.createCallContext(internalCallContext));
         final WithEntitlementPlugin<Void> resumeWithPlugin = new WithEntitlementPlugin<Void>() {
             @Override
             public Void doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
                 try {
-                    final SubscriptionBaseBundle bundle = subscriptionInternalApi.getBundleFromId(bundleId, internalCallContext);
-                    final ImmutableAccountData account = accountApi.getImmutableAccountDataById(bundle.getAccountId(), internalCallContext);
                     final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext);
-
-                    final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), baseSubscription.getStartDate(), internalCallContext);
-
-                    if (!dateHelper.isBeforeOrEqualsToday(effectiveDate, baseSubscription.getStartDate(), account.getTimeZone(), internalCallContext)) {
-                        recordPauseResumeNotificationEntry(baseSubscription.getId(), bundleId, effectiveDate, false, internalCallContext);
-                        return null;
-                    }
-
-                    final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
-
-                    // Should we send one event per entitlement in the bundle?
-                    // Code below only sends one event for the bundle and use the base entitlementId
-                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.UNBLOCK_BUNDLE,
-                                                                                                        effectiveDate, clock.getUTCNow(),
-                                                                                                        internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
-                                                                                                        internalCallContext.getUserToken());
-
-                    try {
-                        eventBus.post(event);
-                    } catch (EventBusException e) {
-                        log.warn("Failed to post event {}", event, e);
-                    }
-
+                    blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
                 } catch (SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
-                } catch (AccountApiException e) {
-                    throw new EntitlementApiException(e);
                 }
                 return null;
             }
@@ -255,39 +193,11 @@ public class DefaultEntitlementApiBase {
         pluginExecution.executeWithPlugin(resumeWithPlugin, pluginContext);
     }
 
-    public void setBlockingState(final UUID bundleId, final String stateName, final String serviceName, final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext)
-            throws EntitlementApiException {
-        blockUnblockBundle(bundleId, stateName, serviceName, localEffectiveDate, blockBilling, blockEntitlement, blockChange, null, internalCallContext);
-    }
-
     private UUID blockUnblockBundle(final UUID bundleId, final String stateName, final String serviceName, @Nullable final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, @Nullable final SubscriptionBase inputBaseSubscription, final InternalCallContext internalCallContext)
             throws EntitlementApiException {
-        try {
-            final SubscriptionBase baseSubscription = inputBaseSubscription == null ? subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext) : inputBaseSubscription;
-            final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), internalCallContext);
-            final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
-            entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(state), bundleId, internalCallContext);
-            return state.getId();
-        } catch (final SubscriptionBaseApiException e) {
-            throw new EntitlementApiException(e);
-        }
-    }
-
-    protected void recordPauseResumeNotificationEntry(final UUID entitlementId, final UUID bundleId, final DateTime effectiveDate, final boolean isPause, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
-        final NotificationEvent notificationEvent = new EntitlementNotificationKey(entitlementId,
-                                                                                   bundleId,
-                                                                                   isPause ? EntitlementNotificationKeyAction.PAUSE : EntitlementNotificationKeyAction.RESUME,
-                                                                                   effectiveDate);
-
-        try {
-            final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                                                           DefaultEntitlementService.NOTIFICATION_QUEUE_NAME);
-            subscriptionEventQueue.recordFutureNotification(effectiveDate, notificationEvent, contextWithValidAccountRecordId.getUserToken(), contextWithValidAccountRecordId.getAccountRecordId(), contextWithValidAccountRecordId.getTenantRecordId());
-        } catch (final NoSuchNotificationQueue e) {
-            throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE);
-        } catch (final IOException e) {
-            throw new EntitlementApiException(e, ErrorCode.__UNKNOWN_ERROR_CODE);
-        }
+        final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, internalCallContext);
+        final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
+        entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(state), bundleId, internalCallContext);
+        return state.getId();
     }
-
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
index 8398dc9..3fc96da 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -114,6 +114,8 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
                                                                                    entitlement.getExternalKey(),
                                                                                    null,
                                                                                    effectiveDate,
+                                                                                   null,
+                                                                                   billingPolicy,
                                                                                    properties,
                                                                                    callContext);
             pluginContexts.add(pluginContext);
@@ -217,7 +219,7 @@ public class DefaultEntitlementInternalApi extends DefaultEntitlementApiBase imp
 
         @Override
         public Entitlement doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
-            DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), entitlement.getSubscriptionBase().getStartDate(), internalCallContext);
+            DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEntitlementEffectiveDate(), internalCallContext);
             // Avoid timing issues for IMM cancellations (we don't want an entitlement cancel date one second or so after the subscription cancel date or
             // add-ons cancellations computations won't work).
             if (effectiveDate.compareTo(entitlement.getSubscriptionBase().getEndDate()) > 0) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
index 7615862..db2577e 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -227,9 +227,11 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
                         }
                     }
 
+                    boolean inserted = false;
                     // Create the state, if needed
                     if (!blockingStatesToRemove.contains(newBlockingStateModelDao.getId())) {
                         sqlDao.create(newBlockingStateModelDao, context);
+                        inserted = true;
                     }
 
                     final BlockingAggregator currentState = getBlockedStatus(sqlDao, entitySqlDaoWrapperFactory.getHandle(), state.getBlockedId(), state.getType(), bundleId, upToDate, context);
@@ -239,7 +241,9 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
                                                                      state.getEffectiveDate(),
                                                                      state.getBlockedId(),
                                                                      state.getType(),
+                                                                     state.getStateName(),
                                                                      state.getService(),
+                                                                     inserted,
                                                                      previousState,
                                                                      currentState,
                                                                      context);
@@ -278,7 +282,9 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
                                                               final DateTime effectiveDate,
                                                               final UUID blockableId,
                                                               final BlockingStateType type,
+                                                              final String stateName,
                                                               final String serviceName,
+                                                              final boolean blockingStateInserted,
                                                               final BlockingAggregator previousState,
                                                               final BlockingAggregator currentState,
                                                               final InternalCallContext context) {
@@ -290,18 +296,31 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
 
         if (effectiveDate.compareTo(clock.getUTCNow()) > 0) {
             // Add notification entry to send the bus event at the effective date
-            final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId, blockableId, type,
-                                                                                              isTransitionToBlockedBilling, isTransitionToUnblockedBilling,
-                                                                                              isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement);
+            final NotificationEvent notificationEvent = new BlockingTransitionNotificationKey(blockingStateId,
+                                                                                              blockableId,
+                                                                                              stateName,
+                                                                                              serviceName,
+                                                                                              effectiveDate,
+                                                                                              type,
+                                                                                              isTransitionToBlockedBilling,
+                                                                                              isTransitionToUnblockedBilling,
+                                                                                              isTransitionToBlockedEntitlement,
+                                                                                              isTransitionToUnblockedEntitlement);
             recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory, effectiveDate, notificationEvent, context);
         } else {
-            // TODO Do we want to send a DefaultEffectiveEntitlementEvent for entitlement specific blocking states?
-            // Don't post if nothing has changed for entitlement-service
-            if (!serviceName.equals(EntitlementService.ENTITLEMENT_SERVICE_NAME) || !previousState.equals(currentState)) {
-                final BusEvent event = new DefaultBlockingTransitionInternalEvent(blockableId, type,
-                                                                                  isTransitionToBlockedBilling, isTransitionToUnblockedBilling,
-                                                                                  isTransitionToBlockedEntitlement, isTransitionToUnblockedEntitlement,
-                                                                                  context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+            if (blockingStateInserted) {
+                final BusEvent event = new DefaultBlockingTransitionInternalEvent(blockableId,
+                                                                                  stateName,
+                                                                                  serviceName,
+                                                                                  effectiveDate,
+                                                                                  type,
+                                                                                  isTransitionToBlockedBilling,
+                                                                                  isTransitionToUnblockedBilling,
+                                                                                  isTransitionToBlockedEntitlement,
+                                                                                  isTransitionToUnblockedEntitlement,
+                                                                                  context.getAccountRecordId(),
+                                                                                  context.getTenantRecordId(),
+                                                                                  context.getUserToken());
                 notifyBusFromTransaction(entitySqlDaoWrapperFactory, event);
             } else {
                 log.debug("Skipping event for service {} and blockableId {} (previousState={}, currentState={})", serviceName, blockableId, previousState, currentState);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
index d74b8be..0dcf6f6 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/DefaultEntitlementService.java
@@ -142,9 +142,9 @@ public class DefaultEntitlementService implements EntitlementService {
                 EntitlementNotificationKeyAction.CANCEL.equals(entitlementNotificationKeyAction)) {
                 blockAddOnsIfRequired(key, (DefaultEntitlement) entitlement, callContext, internalCallContext);
             } else if (EntitlementNotificationKeyAction.PAUSE.equals(entitlementNotificationKeyAction)) {
-                entitlementInternalApi.pause(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), ((DefaultEntitlement) entitlement).getSubscriptionBase().getStartDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
+                entitlementInternalApi.pause(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
             } else if (EntitlementNotificationKeyAction.RESUME.equals(entitlementNotificationKeyAction)) {
-                entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate(), ((DefaultEntitlement) entitlement).getSubscriptionBase().getStartDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
+                entitlementInternalApi.resume(key.getBundleId(), internalCallContext.toLocalDate(key.getEffectiveDate()), ImmutableList.<PluginProperty>of(), internalCallContext);
             }
         } catch (final EntitlementApiException e) {
             log.error("Error processing event for entitlementId='{}'", entitlement.getId(), e);
@@ -182,10 +182,18 @@ public class DefaultEntitlementService implements EntitlementService {
             return;
         }
 
-        final BusEvent event = new DefaultBlockingTransitionInternalEvent(key.getBlockableId(), key.getBlockingType(),
-                                                                          key.isTransitionedToBlockedBilling(), key.isTransitionedToUnblockedBilling(),
-                                                                          key.isTransitionedToBlockedEntitlement(), key.isTransitionToUnblockedEntitlement(),
-                                                                          internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), internalCallContext.getUserToken());
+        final BusEvent event = new DefaultBlockingTransitionInternalEvent(key.getBlockableId(),
+                                                                          key.getStateName(),
+                                                                          key.getService(),
+                                                                          key.getEffectiveDate(),
+                                                                          key.getBlockingType(),
+                                                                          key.isTransitionedToBlockedBilling(),
+                                                                          key.isTransitionedToUnblockedBilling(),
+                                                                          key.isTransitionedToBlockedEntitlement(),
+                                                                          key.isTransitionToUnblockedEntitlement(),
+                                                                          internalCallContext.getAccountRecordId(),
+                                                                          internalCallContext.getTenantRecordId(),
+                                                                          internalCallContext.getUserToken());
 
         try {
             eventBus.post(event);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
index fc4b02d..93035ad 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/BlockingTransitionNotificationKey.java
@@ -18,6 +18,7 @@ package org.killbill.billing.entitlement.engine.core;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.notificationq.api.NotificationEvent;
 
@@ -29,6 +30,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
     private final UUID blockingStateId;
     private final UUID blockableId;
     private final BlockingStateType blockingType;
+    private final String stateName;
+    private final String service;
+    private final DateTime effectiveDate;
     private final Boolean isTransitionToBlockedBilling;
     private final Boolean isTransitionToUnblockedBilling;
     private final Boolean isTransitionToBlockedEntitlement;
@@ -37,6 +41,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
     @JsonCreator
     public BlockingTransitionNotificationKey(@JsonProperty("blockingStateId") final UUID blockingStateId,
                                              @JsonProperty("blockableId") final UUID blockableId,
+                                             @JsonProperty("stateName") final String stateName,
+                                             @JsonProperty("service") final String service,
+                                             @JsonProperty("effectiveDate") final DateTime effectiveDate,
                                              @JsonProperty("type") final BlockingStateType blockingType,
                                              @JsonProperty("isTransitionToBlockedBilling") final Boolean isTransitionToBlockedBilling,
                                              @JsonProperty("isTransitionToUnblockedBilling") final Boolean isTransitionToUnblockedBilling,
@@ -45,6 +52,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
 
         this.blockingStateId = blockingStateId;
         this.blockableId = blockableId;
+        this.service = service;
+        this.stateName = stateName;
+        this.effectiveDate = effectiveDate;
         this.blockingType = blockingType;
         this.isTransitionToBlockedBilling = isTransitionToBlockedBilling;
         this.isTransitionToUnblockedBilling = isTransitionToUnblockedBilling;
@@ -64,6 +74,18 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
         return blockingType;
     }
 
+    public String getStateName() {
+        return stateName;
+    }
+
+    public String getService() {
+        return service;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
     @JsonProperty("isTransitionToBlockedBilling")
     public Boolean isTransitionedToBlockedBilling() {
         return isTransitionToBlockedBilling;
@@ -115,6 +137,15 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
         if (blockableId != null ? !blockableId.equals(that.blockableId) : that.blockableId != null) {
             return false;
         }
+        if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+            return false;
+        }
+        if (service != null ? !service.equals(that.service) : that.service != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
         if (blockingType != that.blockingType) {
             return false;
         }
@@ -138,6 +169,9 @@ public class BlockingTransitionNotificationKey implements NotificationEvent {
     public int hashCode() {
         int result = blockingStateId != null ? blockingStateId.hashCode() : 0;
         result = 31 * result + (blockableId != null ? blockableId.hashCode() : 0);
+        result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+        result = 31 * result + (service != null ? service.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (blockingType != null ? blockingType.hashCode() : 0);
         result = 31 * result + (isTransitionToBlockedBilling != null ? isTransitionToBlockedBilling.hashCode() : 0);
         result = 31 * result + (isTransitionToUnblockedBilling != null ? isTransitionToUnblockedBilling.hashCode() : 0);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index b3cccf9..19d7cb9 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -70,17 +70,26 @@ public class DefaultEventsStream implements EventsStream {
     private final List<SubscriptionBase> allSubscriptionsForBundle;
     private final InternalTenantContext internalTenantContext;
     private final DateTime utcNow;
+    private final int defaultBillCycleDayLocal;
 
     private BlockingAggregator blockingAggregator;
     private List<BlockingState> subscriptionEntitlementStates;
+    private LocalDate entitlementEffectiveStartDate;
+    private DateTime entitlementEffectiveStartDateTime;
+
     private LocalDate entitlementEffectiveEndDate;
+    private DateTime entitlementEffectiveEndDateTime;
+
+    private BlockingState entitlementStartEvent;
     private BlockingState entitlementCancelEvent;
     private EntitlementState entitlementState;
 
     public DefaultEventsStream(final ImmutableAccountData account, final SubscriptionBaseBundle bundle,
                                final List<BlockingState> blockingStates, final BlockingChecker blockingChecker,
                                @Nullable final SubscriptionBase baseSubscription, final SubscriptionBase subscription,
-                               final List<SubscriptionBase> allSubscriptionsForBundle, final InternalTenantContext contextWithValidAccountRecordId, final DateTime utcNow) {
+                               final List<SubscriptionBase> allSubscriptionsForBundle,
+                               final int defaultBillCycleDayLocal,
+                               final InternalTenantContext contextWithValidAccountRecordId, final DateTime utcNow) {
         this.account = account;
         this.bundle = bundle;
         this.blockingStates = blockingStates;
@@ -88,6 +97,7 @@ public class DefaultEventsStream implements EventsStream {
         this.baseSubscription = baseSubscription;
         this.subscription = subscription;
         this.allSubscriptionsForBundle = allSubscriptionsForBundle;
+        this.defaultBillCycleDayLocal = defaultBillCycleDayLocal;
         this.internalTenantContext = contextWithValidAccountRecordId;
         this.utcNow = utcNow;
 
@@ -140,11 +150,26 @@ public class DefaultEventsStream implements EventsStream {
     }
 
     @Override
+    public DateTime getEntitlementEffectiveStartDateTime() {
+        return entitlementEffectiveStartDateTime;
+    }
+
+    @Override
+    public DateTime getEntitlementEffectiveEndDateTime() {
+        return entitlementEffectiveEndDateTime;
+    }
+
+    @Override
     public EntitlementState getEntitlementState() {
         return entitlementState;
     }
 
     @Override
+    public LocalDate getEntitlementEffectiveStartDate() {
+        return entitlementEffectiveStartDate;
+    }
+
+    @Override
     public boolean isBlockChange() {
         return blockingAggregator.isBlockChange();
     }
@@ -173,6 +198,11 @@ public class DefaultEventsStream implements EventsStream {
     }
 
     @Override
+    public int getDefaultBillCycleDayLocal() {
+        return defaultBillCycleDayLocal;
+    }
+
+    @Override
     public Collection<BlockingState> getBlockingStates() {
         return blockingStates;
     }
@@ -355,6 +385,7 @@ public class DefaultEventsStream implements EventsStream {
     private void setup() {
         computeEntitlementBlockingStates();
         computeBlockingAggregator();
+        computeEntitlementStartEvent();
         computeEntitlementCancelEvent();
         computeStateForEntitlement();
     }
@@ -392,6 +423,22 @@ public class DefaultEventsStream implements EventsStream {
         return ImmutableList.<BlockingState>copyOf(currentBlockingStatePerService.values());
     }
 
+    private void computeEntitlementStartEvent() {
+        entitlementStartEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
+                                                                  new Predicate<BlockingState>() {
+                                                                      @Override
+                                                                      public boolean apply(final BlockingState input) {
+                                                                          return DefaultEntitlementApi.ENT_STATE_START.equals(input.getStateName());
+                                                                      }
+                                                                  }).orNull();
+
+        // Note that we still default to subscriptionBase.startDate (for compatibility issue where ENT_STATE_START does not exist)
+        entitlementEffectiveStartDateTime = entitlementStartEvent != null ?
+                                            entitlementStartEvent.getEffectiveDate() :
+                                            getSubscriptionBase().getStartDate();
+        entitlementEffectiveStartDate = internalTenantContext.toLocalDate(entitlementEffectiveStartDateTime);
+
+    }
 
     private void computeEntitlementCancelEvent() {
         entitlementCancelEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
@@ -401,16 +448,21 @@ public class DefaultEventsStream implements EventsStream {
                                                                           return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
                                                                       }
                                                                   }).orNull();
-        entitlementEffectiveEndDate = entitlementCancelEvent != null ? internalTenantContext.toLocalDate(entitlementCancelEvent.getEffectiveDate(),getSubscriptionBase().getStartDate()) : null;
+        entitlementEffectiveEndDateTime =  entitlementCancelEvent != null ? entitlementCancelEvent.getEffectiveDate() : null;
+        entitlementEffectiveEndDate = entitlementEffectiveEndDateTime != null ? internalTenantContext.toLocalDate(entitlementEffectiveEndDateTime) : null;
     }
 
     private void computeStateForEntitlement() {
         // Current state for the ENTITLEMENT_SERVICE_NAME is set to cancelled
-        if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(internalTenantContext.toLocalDate(utcNow, getSubscriptionBase().getStartDate())) <= 0) {
+        if (entitlementEffectiveEndDate != null && entitlementEffectiveEndDate.compareTo(internalTenantContext.toLocalDate(utcNow)) <= 0) {
             entitlementState = EntitlementState.CANCELLED;
         } else {
-            // Gather states across all services and check if one of them is set to 'blockEntitlement'
-            entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
+            if (entitlementEffectiveStartDate.compareTo(new LocalDate(utcNow, account.getTimeZone())) > 0) {
+                entitlementState = EntitlementState.PENDING;
+            } else {
+                // Gather states across all services and check if one of them is set to 'blockEntitlement'
+                entitlementState = (blockingAggregator != null && blockingAggregator.isBlockEntitlement() ? EntitlementState.BLOCKED : EntitlementState.ACTIVE);
+            }
         }
     }
 
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index dc6a097..bd6e009 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -35,11 +35,17 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PhaseType;
+import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.AccountEventsStreams;
 import org.killbill.billing.entitlement.EventsStream;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.svcs.DefaultAccountEventsStreams;
 import org.killbill.billing.entitlement.block.BlockingChecker;
@@ -88,7 +94,6 @@ public class EventsStreamBuilder {
         this.checker = checker;
         this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
-
         this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
         this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, notificationQueueService, eventBus, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory);
     }
@@ -324,15 +329,59 @@ public class EventsStreamBuilder {
                                              final List<SubscriptionBase> allSubscriptionsForBundle,
                                              final List<BlockingState> blockingStates,
                                              final InternalTenantContext internalTenantContext) throws EntitlementApiException {
-        return new DefaultEventsStream(account,
-                                       bundle,
-                                       blockingStates,
-                                       checker,
-                                       baseSubscription,
-                                       subscription,
-                                       allSubscriptionsForBundle,
-                                       internalTenantContext,
-                                       clock.getUTCNow());
+
+
+        try {
+            int accountBCD = accountInternalApi.getBCD(account.getId(), internalTenantContext);
+            int defaultAlignmentDay = subscriptionInternalApi.getDefaultBillCycleDayLocal(subscription, baseSubscription, createPlanPhaseSpecifier(subscription), account.getTimeZone(), accountBCD, clock.getUTCNow(), internalTenantContext);
+            return new DefaultEventsStream(account,
+                                           bundle,
+                                           blockingStates,
+                                           checker,
+                                           baseSubscription,
+                                           subscription,
+                                           allSubscriptionsForBundle,
+                                           defaultAlignmentDay,
+                                           internalTenantContext,
+                                           clock.getUTCNow());
+        } catch (final SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        } catch (final AccountApiException e) {
+            throw new EntitlementApiException(e);
+        }
+    }
+
+    private PlanPhaseSpecifier createPlanPhaseSpecifier(final SubscriptionBase subscription) {
+
+        final String lastActiveProductName;
+        final BillingPeriod billingPeriod;
+        final ProductCategory productCategory;
+        final String priceListName;
+        final PhaseType phaseType;
+
+        if (subscription.getState() == EntitlementState.PENDING) {
+            final SubscriptionBaseTransition transition = subscription.getPendingTransition();
+            final Product pendingProduct = transition.getNextPlan().getProduct();
+            lastActiveProductName = pendingProduct.getName();
+            productCategory = pendingProduct.getCategory();
+            final PlanPhase pendingPlanPhase = transition.getNextPhase();
+            billingPeriod = pendingPlanPhase.getRecurring() != null ? pendingPlanPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
+            priceListName = transition.getNextPriceList().getName();
+            phaseType = transition.getNextPhase().getPhaseType();
+        } else {
+            final Product lastActiveProduct = subscription.getLastActiveProduct();
+            lastActiveProductName = lastActiveProduct.getName();
+            productCategory = lastActiveProduct.getCategory();
+            final PlanPhase lastActivePlanPhase = subscription.getLastActivePhase();
+            billingPeriod = lastActivePlanPhase.getRecurring() != null ? lastActivePlanPhase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
+            priceListName = subscription.getLastActivePlan().getPriceListName();
+            phaseType = subscription.getLastActivePhase().getPhaseType();
+        }
+        return new PlanPhaseSpecifier(lastActiveProductName,
+                                      billingPeriod,
+                                      priceListName,
+                                      phaseType);
+
     }
 
     private SubscriptionBase findBaseSubscription(final Iterable<SubscriptionBase> subscriptions) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
new file mode 100644
index 0000000..7e7f2e2
--- /dev/null
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/logging/EntitlementLoggingHelper.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.entitlement.logging;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.EntitlementSpecifier;
+import org.slf4j.Logger;
+
+public abstract class EntitlementLoggingHelper {
+
+    public static void logCreateEntitlement(final Logger log,
+                                            final UUID bundleId,
+                                            final PlanPhaseSpecifier spec,
+                                            final List<PlanPhasePriceOverride> overrides,
+                                            final LocalDate entitlementDate,
+                                            final LocalDate billingDate) {
+
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Create ")
+                    .append(bundleId != null ? " AO " : " BP ")
+                    .append("Entitlement: ");
+
+            if (bundleId != null) {
+                logLine.append(", bundleId='")
+                       .append(bundleId)
+                       .append("'");
+            }
+            logPlanPhaseSpecifier(logLine, spec, true, true);
+            if (overrides != null && !overrides.isEmpty()) {
+                logPlanPhasePriceOverrides(logLine, overrides);
+            }
+            if (entitlementDate != null) {
+                logLine.append(", entDate='")
+                       .append(entitlementDate)
+                       .append("'");
+            }
+            if (billingDate != null) {
+                logLine.append(", billDate='")
+                       .append(billingDate)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logCreateEntitlementWithAOs(final Logger log,
+                                                   final String externalKey,
+                                                   final Iterable<EntitlementSpecifier> entitlementSpecifiers,
+                                                   final LocalDate entitlementDate,
+                                                   final LocalDate billingDate) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Create Entitlements: ");
+
+            if (externalKey != null) {
+                logLine.append("key='")
+                       .append(externalKey)
+                       .append("'");
+            }
+            if (entitlementDate != null) {
+                logLine.append(", entDate='")
+                       .append(entitlementDate)
+                       .append("'");
+            }
+            if (billingDate != null) {
+                logLine.append(", billDate='")
+                       .append(billingDate)
+                       .append("'");
+            }
+            logEntitlementSpecifier(logLine, entitlementSpecifiers);
+            log.info(logLine.toString());
+        }
+
+    }
+
+    public static void logPauseResumeEntitlement(final Logger log,
+                                                 final String op,
+                                                 final UUID bundleId,
+                                                 final LocalDate effectiveDate) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder(op)
+                    .append(" Entitlement: ");
+
+            if (bundleId != null) {
+                logLine.append(", bundleId='")
+                       .append(bundleId)
+                       .append("'");
+            }
+            if (effectiveDate != null) {
+                logLine.append(", date='")
+                       .append(effectiveDate)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logTransferEntitlement(final Logger log,
+                                              final UUID sourceAccountId,
+                                              final UUID destAccountId,
+                                              final String externalKey,
+                                              final LocalDate effectiveDate,
+                                              final BillingActionPolicy billingPolicy) {
+
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Transfer Entitlement: ");
+            if (sourceAccountId != null) {
+                logLine.append(", src='")
+                       .append(sourceAccountId)
+                       .append("'");
+            }
+            if (destAccountId != null) {
+                logLine.append(", dst='")
+                       .append(destAccountId)
+                       .append("'");
+            }
+            if (externalKey != null) {
+                logLine.append(", key='")
+                       .append(externalKey)
+                       .append("'");
+            }
+            if (effectiveDate != null) {
+                logLine.append(", date='")
+                       .append(effectiveDate)
+                       .append("'");
+            }
+            if (effectiveDate != null) {
+                logLine.append(", policy='")
+                       .append(billingPolicy)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logCancelEntitlement(final Logger log, final Entitlement entitlement, final LocalDate entitlementEffectiveDate, final Boolean overrideBillingEffectiveDate, final EntitlementActionPolicy entitlementPolicy, final BillingActionPolicy billingPolicy) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Cancel Entitlement: ")
+                    .append(" id = '")
+                    .append(entitlement.getId())
+                    .append("'");
+            if (entitlementEffectiveDate != null) {
+                logLine.append(", entDate='")
+                       .append(entitlementEffectiveDate)
+                       .append("'");
+            }
+            if (overrideBillingEffectiveDate != null) {
+                logLine.append(", overrideBillDate='")
+                       .append(overrideBillingEffectiveDate)
+                       .append("'");
+            }
+            if (entitlementPolicy != null) {
+                logLine.append(", entPolicy='")
+                       .append(entitlementPolicy)
+                       .append("'");
+            }
+            if (billingPolicy != null) {
+                logLine.append(", billPolicy='")
+                       .append(billingPolicy)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logUncancelEntitlement(final Logger log, final Entitlement entitlement) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Uncancel Entitlement: ")
+                    .append(" id = '")
+                    .append(entitlement.getId())
+                    .append("'");
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logChangePlan(final Logger log, final Entitlement entitlement, final PlanSpecifier spec,
+                                     final List<PlanPhasePriceOverride> overrides, final LocalDate entitlementEffectiveDate, final BillingActionPolicy actionPolicy) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Change Entitlement Plan: ")
+                    .append(" id = '")
+                    .append(entitlement.getId())
+                    .append("'");
+            if (entitlementEffectiveDate != null) {
+                logLine.append(", entDate='")
+                       .append(entitlementEffectiveDate)
+                       .append("'");
+            }
+            if (spec.getPlanName() != null) {
+                logLine.append(", plan='")
+                       .append(spec.getPlanName())
+                       .append("'");
+            }
+            if (spec.getProductName() != null) {
+                logLine.append(", product='")
+                       .append(spec.getProductName())
+                       .append("'");
+            }
+            if (spec.getBillingPeriod() != null) {
+                logLine.append(", billingPeriod='")
+                       .append(spec.getBillingPeriod())
+                       .append("'");
+            }
+            if (spec.getPriceListName() != null) {
+                logLine.append(", priceList='")
+                       .append(spec.getBillingPeriod())
+                       .append("'");
+            }
+            logPlanPhasePriceOverrides(logLine, overrides);
+            if (actionPolicy != null) {
+                logLine.append(", actionPolicy='")
+                       .append(actionPolicy)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logUpdateBCD(final Logger log, final Entitlement entitlement, final int newBCD, final LocalDate effectiveFromDate) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Update Entitlement BCD: ")
+                    .append(" id = '")
+                    .append(entitlement.getId())
+                    .append("'");
+
+            logLine.append(", newBCD='")
+                   .append(newBCD)
+                   .append("'");
+            if (effectiveFromDate != null) {
+                logLine.append(", date='")
+                       .append(effectiveFromDate)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logUpdateExternalKey(final Logger log, final UUID bundleId, final String newExternalKey) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Update Entitlement Key: ");
+            if (bundleId != null) {
+                logLine.append(", bundleId='")
+                       .append(bundleId)
+                       .append("'");
+            }
+            if (newExternalKey != null) {
+                logLine.append(", key='")
+                       .append(newExternalKey)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    public static void logAddBlockingState(final Logger log, final BlockingState inputBlockingState, final LocalDate inputEffectiveDate) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder("Add BlockingState Entitlement: ");
+            logBlockingState(logLine, inputBlockingState);
+            if (inputEffectiveDate != null) {
+                logLine.append(", date='")
+                       .append(inputEffectiveDate)
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+
+    private static void logBlockingState(final StringBuilder logLine, final BlockingState blk) {
+        if (blk != null) {
+            logLine.append("blk='");
+            logLine.append(blk.getBlockedId() != null ? blk.getBlockedId() : "null");
+            logLine.append(":");
+            logLine.append(blk.getType() != null ? blk.getType() : "null");
+            logLine.append(":");
+            logLine.append(blk.getService() != null ? blk.getService() : "null");
+            logLine.append(":");
+            logLine.append(blk.getStateName() != null ? blk.getStateName() : "null");
+            logLine.append("'");
+        }
+    }
+
+    private static void logEntitlementSpecifier(final StringBuilder logLine, final Iterable<EntitlementSpecifier> entitlementSpecifiers) {
+        if (entitlementSpecifiers != null && entitlementSpecifiers.iterator().hasNext()) {
+            logLine.append(",'[");
+            boolean first = true;
+            for (EntitlementSpecifier cur : entitlementSpecifiers) {
+                if (!first) {
+                    logLine.append(",");
+                }
+                logPlanPhaseSpecifier(logLine, cur.getPlanPhaseSpecifier(), false, false);
+                logPlanPhasePriceOverrides(logLine, cur.getOverrides());
+                first = false;
+            }
+            logLine.append("]'");
+        }
+    }
+
+    private static void logPlanPhaseSpecifier(final StringBuilder logLine, final PlanPhaseSpecifier spec, boolean addComma, boolean addParentheseQuote) {
+        if (spec != null) {
+            if (addComma) {
+                logLine.append(", ");
+            }
+            logLine.append("spec=");
+            if (addParentheseQuote) {
+                logLine.append("'(");
+            }
+            logLine.append(spec.getProductName() != null ? spec.getProductName() : "null");
+            logLine.append(":");
+            logLine.append(spec.getBillingPeriod() != null ? spec.getBillingPeriod() : "null");
+            logLine.append(":");
+            logLine.append(spec.getPhaseType() != null ? spec.getPhaseType() : "null");
+            logLine.append(":");
+            logLine.append(spec.getPriceListName() != null ? spec.getPriceListName() : "null");
+            if (addParentheseQuote) {
+                logLine.append(")'");
+            }
+        }
+    }
+
+    private static void logPlanPhasePriceOverrides(final StringBuilder logLine, final List<PlanPhasePriceOverride> overrides) {
+        if (overrides != null && !overrides.isEmpty()) {
+            logLine.append(", overrides='[");
+            boolean first = true;
+            for (final PlanPhasePriceOverride cur : overrides) {
+                if (!first) {
+                    logLine.append(",");
+                }
+                logPlanPhasePriceOverride(logLine, cur);
+                first = false;
+            }
+            logLine.append("]'");
+
+        }
+    }
+
+    private static void logPlanPhasePriceOverride(final StringBuilder logLine, final PlanPhasePriceOverride override) {
+        if (override != null) {
+            logLine.append("(");
+            logPlanPhaseSpecifier(logLine, override.getPlanPhaseSpecifier(), false, false);
+            logLine.append(":");
+            logLine.append(override.getPhaseName() != null ? override.getPhaseName() : "null");
+            logLine.append(":");
+            logLine.append(override.getCurrency() != null ? override.getCurrency() : "null");
+            logLine.append(":");
+            logLine.append(override.getFixedPrice() != null ? override.getFixedPrice() : "null");
+            logLine.append(":");
+            logLine.append(override.getRecurringPrice() != null ? override.getRecurringPrice() : "null");
+            logLine.append(")");
+        }
+    }
+}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
index b085b5c..692995f 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlement.java
@@ -27,10 +27,12 @@ import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
@@ -50,24 +52,25 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getSourceType(), EntitlementSourceType.NATIVE);
 
         clock.addDays(5);
 
         testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
-        final LocalDate cancelDate = new LocalDate(clock.getUTCNow());
-        entitlement.cancelEntitlementWithDate(cancelDate, true, ImmutableList.<PluginProperty>of(), callContext);
+        entitlement.cancelEntitlementWithDate(null, true, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         final Entitlement entitlement2 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
         assertEquals(entitlement2.getState(), EntitlementState.CANCELLED);
-        assertEquals(entitlement2.getEffectiveEndDate(), cancelDate);
+        assertEquals(entitlement2.getEffectiveEndDate(), clock.getUTCToday());
+        assertEquals(entitlement2.getSourceType(), EntitlementSourceType.NATIVE);
     }
 
     @Test(groups = "slow")
@@ -77,11 +80,11 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
 
@@ -95,7 +98,6 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
         assertEquals(entitlement2.getEffectiveEndDate(), cancelDate);
 
         clock.addDays(1);
-
         testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
         assertListenerStatus();
 
@@ -111,11 +113,11 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
 
@@ -144,11 +146,11 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
@@ -169,11 +171,11 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
@@ -212,11 +214,11 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
@@ -252,16 +254,16 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Immediate change during trial
         testListener.pushExpectedEvent(NextEvent.CHANGE);
-        entitlement.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), callContext);
+        entitlement.changePlan(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Verify the change is immediate
@@ -279,52 +281,29 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
         assertEquals(subscription.getBillingEndDate(), new LocalDate(2013, 8, 7));
     }
 
-    @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/452")
-    public void testBlockedEntitlementChange() throws AccountApiException, EntitlementApiException {
+    @Test(groups = "slow")
+    public void testEntitlementStartedInFuture() throws AccountApiException, EntitlementApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
         clock.setDay(initialDate);
 
-        final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final LocalDate startDate = initialDate.plusDays(10);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, startDate, startDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
+        assertEquals(entitlement.getState(), EntitlementState.PENDING);
 
-        clock.addDays(1);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        clock.addDays(10);
         assertListenerStatus();
 
-        testListener.pushExpectedEvent(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(entitlement.getBundleId(), "MY_BLOCK", "test", clock.getUTCToday(), false, false, true, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
+        final Entitlement entitlement1 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        assertEquals(entitlement1.getState(), EntitlementState.ACTIVE);
 
-        try {
-            entitlement.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), callContext);
-            fail();
-        } catch (final EntitlementApiException e) {
-            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
-            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
-            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
-        }
-
-        try {
-            entitlement.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
-            fail();
-        } catch (final EntitlementApiException e) {
-            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
-            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
-            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
-        }
-
-        try {
-            entitlement.changePlanOverrideBillingPolicy("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, clock.getUTCToday(), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
-            fail();
-        } catch (final EntitlementApiException e) {
-            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
-            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
-            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
-        }
     }
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index e060144..fe59e7d 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -58,24 +58,24 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Add ADD_ON
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement addOnEntitlement = entitlementApi.addEntitlement(entitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         try {
             entitlement.uncancelEntitlement(ImmutableList.<PluginProperty>of(), callContext);
             Assert.fail("Entitlement hasn't been cancelled yet");
         } catch (final EntitlementApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.ENT_UNCANCEL_BAD_STATE.getCode());
         }
 
         clock.addDays(3);
@@ -121,11 +121,11 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -154,11 +154,11 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement.getAccountId(), account.getId());
         assertEquals(entitlement.getExternalKey(), account.getExternalKey());
@@ -246,17 +246,17 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Add ADD_ON
-        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         assertEquals(telescopicEntitlement.getAccountId(), account.getId());
@@ -285,24 +285,24 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         clock.addDays(1);
         final LocalDate effectiveDateSpec1 = new LocalDate(clock.getUTCNow(), account.getTimeZone());
-        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, ImmutableList.<PluginProperty>of(), callContext);
+        final PlanPhaseSpecifier spec1 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement telescopicEntitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Block all entitlement in the bundle
         clock.addDays(5);
 
-        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         entitlementApi.pause(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -321,21 +321,19 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         // Try to add an ADD_ON, it should fail
         try {
-            final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-            final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, ImmutableList.<PluginProperty>of(), callContext);
+            final PlanPhaseSpecifier spec3 = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+            final Entitlement telescopicEntitlement3 = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), spec1, null, effectiveDateSpec1, effectiveDateSpec1, false, ImmutableList.<PluginProperty>of(), callContext);
         } catch (EntitlementApiException e) {
             assertEquals(e.getCode(), ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION.getCode());
         }
 
         clock.addDays(3);
 
-        testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        // Verify call is idempotent : The current semantics is to post the RESUME because we went through the operation, but not the BLOCK because the DAO logic
-        // filtered the event as the subscription was already resumed.
-        testListener.pushExpectedEvents(NextEvent.RESUME);
+        // Verify call is idempotent
         entitlementApi.resume(baseEntitlement.getBundleId(), new LocalDate(clock.getUTCNow()), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -353,18 +351,17 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         }
     }
 
-
     @Test(groups = "slow", description = "Test pause / unpause in the future")
-    public void testPauseUnpauseInTheFuture() throws AccountApiException, EntitlementApiException {
+    public void testPauseUnpauseInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
         clock.setDay(initialDate);
 
         final Account account = createAccount(getAccountData(7));
 
         // Create entitlement
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Get the phase event out of the way
@@ -377,12 +374,20 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         // No event yet
         assertListenerStatus();
 
+        final Entitlement refreshedAfterFuturePause = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+        assertEquals(refreshedAfterFuturePause.getState(), EntitlementState.ACTIVE);
+
+
         final LocalDate resumeDate = new LocalDate(2013, 12, 24);
         entitlementApi.resume(baseEntitlement.getBundleId(), resumeDate, ImmutableList.<PluginProperty>of(), callContext);
         // No event yet
         assertListenerStatus();
 
-        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+        // Not worth writing another test in TestDefaultSubscriptionApi just for that subscription call. We want to check that future PAUSE/RESUME events are visible
+        final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(baseEntitlement.getId(), callContext);
+        Assert.assertEquals(subscription.getSubscriptionEvents().size(), 7);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         clock.setDay(pauseDate);
         assertListenerStatus();
 
@@ -390,7 +395,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         final Entitlement baseEntitlementPaused = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
         assertEquals(baseEntitlementPaused.getState(), EntitlementState.BLOCKED);
 
-        testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         clock.setDay(resumeDate);
         assertListenerStatus();
 
@@ -408,12 +413,16 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         // internal context will be configured for accountSrc
         final Account accountSrc = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
-        // Create entitlement
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(accountSrc.getId(), spec, accountSrc.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        // Create entitlement (with migrated flag so we can check later that transferred subscription is in right status)
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(accountSrc.getId(), spec, accountSrc.getExternalKey(), null, null, null, true, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
+        assertEquals(baseEntitlement.getSourceType(), EntitlementSourceType.MIGRATED);
+        // Again to make sure this flag is correctly wrote/set
+        assertEquals(entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext).getSourceType(), EntitlementSourceType.MIGRATED);
+
 
         final DateTime ctd = clock.getUTCNow().plusDays(30).plusMonths(1);
         testListener.pushExpectedEvent(NextEvent.PHASE);
@@ -424,7 +433,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         // Transfer bundle to dest account
         final LocalDate effectiveDate = new LocalDate(clock.getUTCNow(), accountSrc.getTimeZone());
-        testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.BLOCK);
         final UUID newBundleId = entitlementApi.transferEntitlementsOverrideBillingPolicy(accountSrc.getId(), accountDesc.getId(), baseEntitlement.getExternalKey(), effectiveDate, BillingActionPolicy.END_OF_TERM, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -439,78 +448,11 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         assertEquals(newBaseEntitlement.getState(), EntitlementState.ACTIVE);
         assertEquals(newBaseEntitlement.getEffectiveStartDate(), effectiveDate);
         assertEquals(newBaseEntitlement.getEffectiveEndDate(), null);
-    }
-
-    @Test(groups = "slow")
-    public void testBlockBundle() throws AccountApiException, EntitlementApiException {
-        final LocalDate initialDate = new LocalDate(2013, 8, 7);
-        clock.setDay(initialDate);
-
-        final Account account = createAccount(getAccountData(7));
-
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-
-        // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        clock.addDays(5);
-
-        testListener.pushExpectedEvents(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
-        assertEquals(bundleEntitlements.size(), 1);
-        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
-
-        final BlockingState blockingState = blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
-        assertTrue(blockingState.isBlockBilling());
-        assertTrue(blockingState.isBlockChange());
-        assertTrue(blockingState.isBlockEntitlement());
-
-
-        // Check unblocking on another service will not bring the sate back to ACTIVE
-        clock.addDays(1);
-        testListener.pushExpectedEvents(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "bar", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
-        assertEquals(bundleEntitlements.size(), 1);
-        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
-
+        assertEquals(newBaseEntitlement.getSourceType(), EntitlementSourceType.TRANSFERRED);
 
-        testListener.pushExpectedEvents(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
-        assertEquals(bundleEntitlements.size(), 1);
-        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
-
-        blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
-        clock.addDays(1);
-
-        testListener.pushExpectedEvents(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
-        assertEquals(bundleEntitlements.size(), 1);
-        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
-
-        // Same day but happened after so should take precedence
-        testListener.pushExpectedEvents(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
-        assertListenerStatus();
-
-        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
-        assertEquals(bundleEntitlements.size(), 1);
-        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
     }
 
+
     @Test(groups = "slow")
     public void testCreateEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
@@ -519,11 +461,11 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Keep the same object for the whole test, to make sure we refresh its state before r/w calls
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         assertEquals(entitlement.getAccountId(), account.getId());
@@ -564,8 +506,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Cleaning", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -575,8 +517,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         specifierList.add(baseEntitlementSpecifier);
         specifierList.add(addOnEntitlementSpecifier);
 
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         assertNotNull(entitlement);
@@ -602,8 +544,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Invalid",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "baseExternalKey";
         EntitlementSpecifier baseEntitlementSpecifier = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -614,7 +556,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         specifierList.add(addOnEntitlementSpecifier);
 
         try {
-            entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+            entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
             fail();
         } catch (EntitlementApiException e) {
             assertEquals(e.getMessage(), "Could not find any product named 'Invalid'");
@@ -632,8 +574,8 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Cleaning",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Bullets",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final String externalKey = "addOnExternalKey";
         EntitlementSpecifier addOnEntitlementSpecifier1 = new DefaultEntitlementSpecifier(baseSpec, null);
@@ -644,7 +586,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         specifierList.add(addOnEntitlementSpecifier2);
 
         try {
-            entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+            entitlementApi.createBaseEntitlementWithAddOns(account.getId(), externalKey, specifierList, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
             fail();
         } catch (EntitlementApiException e) {
             assertEquals(e.getMessage(), "Missing Base Subscription.");
@@ -652,7 +594,146 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
 
         final List<Entitlement> allEntitlementsForAccount = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
         assertTrue(allEntitlementsForAccount.size() == 0);
+    }
+
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithDifferentInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = initialDate.plusDays(3);
+        final LocalDate billingDate = initialDate.plusDays(5);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.PENDING);
+        assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        clock.addDays(3);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvents(NextEvent.CREATE);
+        clock.addDays(2);
+        assertListenerStatus();
+    }
 
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithEntitlementInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = initialDate.plusDays(3);
+        final LocalDate billingDate = null;
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.PENDING);
+        assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        clock.addDays(3);
+        assertListenerStatus();
+
+        final Entitlement entitlementActive = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        assertEquals(entitlementActive.getState(), EntitlementState.ACTIVE);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithBillingInTheFuture() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = null;
+        final LocalDate billingDate = initialDate.plusDays(5);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getEffectiveStartDate(), initialDate);
+
+        testListener.pushExpectedEvents(NextEvent.CREATE);
+        clock.addDays(5);
+        assertListenerStatus();
+    }
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithDifferentInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = initialDate.minusDays(3);
+        final LocalDate billingDate = initialDate.minusDays(5);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = initialDate.minusDays(3);
+        final LocalDate billingDate = null;
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getEffectiveStartDate(), entitlementDate);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateBaseWithBillingInThePast() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final LocalDate entitlementDate = null;
+        final LocalDate billingDate = initialDate.minusDays(5);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CREATE);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, entitlementDate, billingDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+        assertEquals(entitlement.getEffectiveStartDate(), initialDate);
     }
 
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
index fd9162d..f23a365 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -16,36 +16,40 @@
 
 package org.killbill.billing.entitlement.api;
 
+import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
-import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.api.AuditLevel;
 import org.killbill.billing.util.audit.AuditLog;
 import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
 import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
 
@@ -55,12 +59,19 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         clock.setDay(initialDate);
 
         final Account account = createAccount(getAccountData(7));
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE, NextEvent.BLOCK);
-        final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
-        // Sleep 1 sec so created date are apparts from each other and ordering in the bundle does not default on the UUID which is random.
-        try {Thread.sleep(1000); } catch (InterruptedException ignore) {};
-        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement1 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        // Sleep 1 sec so created date are apart from each other and ordering in the bundle does not default on the UUID which is random.
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException ignore) {
+        }
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "stateName", "service", false, false, false, clock.getUTCNow()),
                                                                         internalCallContextFactory.createInternalCallContext(account.getId(), callContext));
         assertListenerStatus();
@@ -99,11 +110,11 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement.getAccountId(), account.getId());
         assertEquals(entitlement.getExternalKey(), externalKey);
@@ -133,11 +144,11 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         clock.addDays(1);
         // Re-create a new bundle with same externalKey
-        final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec2 = new PlanPhaseSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         // Create entitlement and check each field
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec2, externalKey, null, new LocalDate(clock.getUTCNow(), account.getTimeZone()), ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement2 = entitlementApi.createBaseEntitlement(account.getId(), spec2, externalKey, null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
         assertEquals(entitlement2.getAccountId(), account.getId());
         assertEquals(entitlement2.getExternalKey(), externalKey);
@@ -167,7 +178,7 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account2 = createAccount(getAccountData(7));
 
-        testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.CANCEL, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.TRANSFER, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.BLOCK);
         entitlementApi.transferEntitlements(account.getId(), account2.getId(), externalKey, new LocalDate(clock.getUTCNow(), account.getTimeZone()), ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
@@ -206,9 +217,9 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         final Account account = createAccount(getAccountData(7));
 
         // Create entitlement
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Get the phase event out of the way
@@ -219,14 +230,14 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         final LocalDate pauseDate = new LocalDate(2013, 9, 17);
         entitlementApi.pause(baseEntitlement.getBundleId(), pauseDate, ImmutableList.<PluginProperty>of(), callContext);
 
-        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         clock.setDay(pauseDate);
         assertListenerStatus();
 
         final LocalDate resumeDate = new LocalDate(2013, 12, 24);
         entitlementApi.resume(baseEntitlement.getBundleId(), resumeDate, ImmutableList.<PluginProperty>of(), callContext);
 
-        testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
         clock.setDay(resumeDate);
         assertListenerStatus();
 
@@ -251,7 +262,6 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         checkSubscriptionEventAuditLog(transitions, 8, SubscriptionEventType.STOP_BILLING);
     }
 
-
     @Test(groups = "slow")
     public void testSubscriptionCreationWithFutureDate() throws AccountApiException, SubscriptionApiException, EntitlementApiException {
         final String externalKey = "vritti";
@@ -261,13 +271,13 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         //2013-08-07
         final LocalDate effectiveDate = initialDate.plusMonths(1);
 
         // Create entitlement and check each field
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, effectiveDate, effectiveDate, false, ImmutableList.<PluginProperty>of(), callContext);
 
         final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(entitlement.getId(), callContext);
 
@@ -276,21 +286,16 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         assertEquals(events.get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
         assertEquals(events.get(0).getEffectiveDate().compareTo(effectiveDate), 0);
-        assertEquals(events.get(0).getRequestedDate().compareTo(effectiveDate), 0);
 
         assertEquals(events.get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
         assertEquals(events.get(1).getEffectiveDate().compareTo(effectiveDate), 0);
-        assertEquals(events.get(1).getRequestedDate().compareTo(effectiveDate), 0);
 
         assertEquals(events.get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
         assertEquals(events.get(2).getEffectiveDate().compareTo(effectiveDate.plusMonths(1)), 0);
-        assertEquals(events.get(2).getRequestedDate().compareTo(effectiveDate.plusMonths(1)), 0);
 
         assertListenerStatus();
-
     }
 
-
     @Test(groups = "slow")
     public void testCancelFutureSubscription() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
 
@@ -299,12 +304,12 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
         final LocalDate futureDate = new LocalDate(2013, 9, 1);
 
         // No CREATE event as this is set in the future
-        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, ImmutableList.<PluginProperty>of(), callContext);
+        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, futureDate, futureDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(createdEntitlement.getEffectiveStartDate().compareTo(futureDate), 0);
         assertEquals(createdEntitlement.getEffectiveEndDate(), null);
 
@@ -321,6 +326,244 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         assertListenerStatus();
     }
 
+    @Test(groups = "slow")
+    public void testAddBlockingState() throws AccountApiException, EntitlementApiException, SubscriptionApiException {
+
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement createdEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+
+        final Iterable<BlockingState> iterableForCreateState = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), null, OrderingType.ASCENDING, SubscriptionApi.ALL_EVENTS, callContext);
+        assertTrue(iterableForCreateState.iterator().hasNext());
+        final BlockingState createState = iterableForCreateState.iterator().next();
+        assertEquals(createState.getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, "accountBlock", "svc1", false, true, false, clock.getUTCNow());
+        subscriptionApi.addBlockingState(state1, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        Entitlement updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+        Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+        clock.addDays(1);
+
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state2 = new DefaultBlockingState(createdEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "subscriptionBlock", "svc2", false, false, false, clock.getUTCNow());
+        subscriptionApi.addBlockingState(state2, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        // Still blocked because this is a different service
+        updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+        Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+        // Now we remove the blocking state for the same service but at the SUBSCRIPTION level
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state3 = new DefaultBlockingState(createdEntitlement.getId(), BlockingStateType.SUBSCRIPTION, "subscriptionUnBlock", "svc1", false, false, false, clock.getUTCNow());
+        subscriptionApi.addBlockingState(state3, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        updateEntitlement = entitlementApi.getEntitlementForId(createdEntitlement.getId(), callContext);
+        Assert.assertEquals(updateEntitlement.getState(), EntitlementState.BLOCKED);
+
+        final DateTime futureEffectiveDate = clock.getUTCNow().plusDays(1);
+        final BlockingState state4 = new DefaultBlockingState(createdEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "blockBilling", "svc1", true, false, false, futureEffectiveDate);
+        subscriptionApi.addBlockingState(state4, internalCallContext.toLocalDate(futureEffectiveDate), ImmutableList.<PluginProperty>of(), callContext);
+
+        final Iterable<BlockingState> blockingStates1 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.ACCOUNT, BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc1", "svc2"), OrderingType.ASCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+        verifyBlockingStates(blockingStates1, ImmutableList.<BlockingState>of(state1, state2, state3));
+
+        final Iterable<BlockingState> blockingStates2 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc1", "svc2"), OrderingType.DESCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+        verifyBlockingStates(blockingStates2, ImmutableList.<BlockingState>of(state3, state2));
+
+        final Iterable<BlockingState> blockingStates3 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION), ImmutableList.of("svc2"), OrderingType.DESCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+        verifyBlockingStates(blockingStates3, ImmutableList.<BlockingState>of(state2));
+
+        final Iterable<BlockingState> blockingStates4 = subscriptionApi.getBlockingStates(account.getId(), null, null, OrderingType.DESCENDING, SubscriptionApi.ALL_EVENTS, callContext);
+        verifyBlockingStates(blockingStates4, ImmutableList.<BlockingState>of(state4, state3, state2, state1, createState));
+
+        final Iterable<BlockingState> blockingStates5 = subscriptionApi.getBlockingStates(account.getId(), ImmutableList.of(BlockingStateType.SUBSCRIPTION_BUNDLE), null, OrderingType.ASCENDING, SubscriptionApi.FUTURE_EVENTS, callContext);
+        verifyBlockingStates(blockingStates5, ImmutableList.<BlockingState>of(state4));
+
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        clock.addDays(1);
+        assertListenerStatus();
+
+        final Iterable<BlockingState> blockingStates6 = subscriptionApi.getBlockingStates(account.getId(), null, null, OrderingType.ASCENDING, SubscriptionApi.PAST_OR_PRESENT_EVENTS, callContext);
+        verifyBlockingStates(blockingStates6, ImmutableList.<BlockingState>of(createState, state1, state2, state3, state4));
+
+    }
+
+    @Test(groups = "slow")
+    public void testBlockBundle() throws AccountApiException, EntitlementApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Create entitlement and check each field
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        clock.addDays(5);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final BlockingState state1 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "BLOCK", "foo", true, true, true, null);
+        subscriptionApi.addBlockingState(state1, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+        final BlockingState blockingState = blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
+        assertTrue(blockingState.isBlockBilling());
+        assertTrue(blockingState.isBlockChange());
+        assertTrue(blockingState.isBlockEntitlement());
+
+        // Check unblocking on another service will not bring the state back to ACTIVE
+        clock.addDays(1);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final BlockingState state2 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "UNBLOCK", "bar", false, false, false, null);
+        subscriptionApi.addBlockingState(state2, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final BlockingState state3 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "UNBLOCK", "foo", false, false, false, null);
+        subscriptionApi.addBlockingState(state3, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
+
+        blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
+        clock.addDays(1);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final BlockingState state4 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "BLOCK", "foo", true, true, true, null);
+        subscriptionApi.addBlockingState(state4, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+        // Same day but happened after so should take precedence
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        final BlockingState state5 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "UNBLOCK", "foo", false, false, false, null);
+        subscriptionApi.addBlockingState(state5, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
+    }
+
+
+    @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/452")
+    public void testBlockedEntitlementChange() throws AccountApiException, EntitlementApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Create entitlement and check each field
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        clock.addDays(1);
+        assertListenerStatus();
+
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "MY_BLOCK", "test", true, false, false, null);
+        subscriptionApi.addBlockingState(state, null, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        try {
+            entitlement.changePlan(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), callContext);
+            fail();
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
+        }
+
+        try {
+            entitlement.changePlanWithDate(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+            fail();
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
+        }
+
+        try {
+            entitlement.changePlanOverrideBillingPolicy(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, clock.getUTCToday(), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
+            fail();
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.BLOCK_BLOCKED_ACTION.getCode());
+            final Entitlement latestEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+            assertEquals(latestEntitlement.getLastActivePlan().getProduct().getName(), "Shotgun");
+        }
+    }
+
+    @Test(groups = "slow")
+    public void testSubscriptionCreationWithExternalKeyOverLimit() throws AccountApiException, SubscriptionApiException, EntitlementApiException {
+        final String externalKey = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.";
+
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = createAccount(getAccountData(7));
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        //2013-08-07
+        final LocalDate effectiveDate = initialDate.plusMonths(1);
+
+        try {
+            entitlementApi.createBaseEntitlement(account.getId(), spec, externalKey, null, effectiveDate, effectiveDate, false, ImmutableList.<PluginProperty>of(), callContext);
+            Assert.fail();
+        } catch (final EntitlementApiException e) {
+            assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
+        }
+    }
+
+    private void verifyBlockingStates(final Iterable<BlockingState> result, final List<BlockingState> expected) {
+        int i = 0;
+        final Iterator<BlockingState> iterator = result.iterator();
+        while (iterator.hasNext()) {
+            final BlockingState cur = iterator.next();
+            final BlockingState expectedItem = expected.get(i);
+            assertEquals(cur.isBlockBilling(), expectedItem.isBlockBilling());
+            assertEquals(cur.isBlockEntitlement(), expectedItem.isBlockEntitlement());
+            assertEquals(cur.isBlockChange(), expectedItem.isBlockChange());
+            assertEquals(cur.getService(), expectedItem.getService());
+            assertEquals(cur.getStateName(), expectedItem.getStateName());
+            assertEquals(cur.getBlockedId(), expectedItem.getBlockedId());
+            assertEquals(internalCallContext.toLocalDate(cur.getEffectiveDate()).compareTo(internalCallContext.toLocalDate(expectedItem.getEffectiveDate())), 0);
+            i++;
+        }
+        assertEquals(i, expected.size());
+    }
 
     private void checkSubscriptionEventAuditLog(final List<SubscriptionEvent> transitions, final int idx, final SubscriptionEventType expectedType) {
         assertEquals(transitions.get(idx).getSubscriptionEventType(), expectedType);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 5208a5b..dc0198b 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -86,7 +86,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
                                                 null,
                                                 null,
                                                 null,
-                                                effectiveDate,
                                                 internalCallContext);
 
         }
@@ -265,7 +264,17 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
     }
 
     @Test(groups = "fast")
-    public void testOneEntitlementNoBlockingStates() throws CatalogApiException {
+    public void testOneSimpleEntitlement() throws CatalogApiException {
+        testOneSimpleEntitlementImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneSimpleEntitlementWithRegression() throws CatalogApiException {
+        testOneSimpleEntitlementImpl(true);
+    }
+
+
+    private void testOneSimpleEntitlementImpl(boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -273,6 +282,8 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final UUID bundleId = UUID.randomUUID();
         final String externalKey = "foo";
 
+        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
+
         final UUID entitlementId = UUID.randomUUID();
 
         final List<SubscriptionBaseTransition> allTransitions = new ArrayList<SubscriptionBaseTransition>();
@@ -282,6 +293,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+            blockingStates.add(bsCreate);
+        }
+
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -293,7 +312,7 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         allTransitions.add(tr3);
 
         final List<Entitlement> entitlements = new ArrayList<Entitlement>();
-        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions);
+        final Entitlement entitlement = createEntitlement(entitlementId, allTransitions, blockingStates);
         entitlements.add(entitlement);
 
         final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, entitlements, internalCallContext);
@@ -321,7 +340,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(3).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
         assertEquals(events.get(2).getPrevPhase().getName(), "trial");
@@ -330,8 +348,18 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertNull(events.get(3).getNextPhase());
     }
 
+
+
     @Test(groups = "fast")
     public void testCancelBundleBeforeSubscription() throws CatalogApiException {
+        testCancelBundleBeforeSubscriptionImpl(false);
+    }
+    @Test(groups = "fast")
+    public void testCancelBundleBeforeSubscriptionWithRegression() throws CatalogApiException {
+        testCancelBundleBeforeSubscriptionImpl(true);
+    }
+
+    private void testCancelBundleBeforeSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -349,6 +377,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+            blockingStates.add(bsCreate);
+        }
+
         // Block the bundle before the subscription
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
@@ -392,17 +427,24 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(3).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
-        assertEquals(events.get(2).getPrevPhase().getName(), "trial");
-        assertNull(events.get(2).getNextPhase());
         assertEquals(events.get(3).getPrevPhase().getName(), "trial");
         assertNull(events.get(3).getNextPhase());
     }
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
     public void testOneEntitlementWithPauseResume() throws CatalogApiException {
+        testOneEntitlementWithPauseResumeImpl(false);
+    }
+
+    @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
+    public void testOneEntitlementWithPauseResumeWithRegression() throws CatalogApiException {
+        testOneEntitlementWithPauseResumeImpl(true);
+    }
+
+
+    private void testOneEntitlementWithPauseResumeImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -420,6 +462,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -508,7 +559,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(8).getServiceName(), service);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -531,6 +581,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/147 and https://github.com/killbill/killbill/issues/148")
     public void testOneEntitlementWithOverduePauseThenCancel() throws CatalogApiException {
+        testOneEntitlementWithOverduePauseThenCancelImpl(false);
+    }
+
+    public void testOneEntitlementWithOverduePauseThenCancelWithRegression() throws CatalogApiException {
+        testOneEntitlementWithOverduePauseThenCancelImpl(true);
+    }
+
+    private void testOneEntitlementWithOverduePauseThenCancelImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -548,6 +606,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -653,7 +719,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(9).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -680,6 +745,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneEntitlementWithInitialBlockingState() throws CatalogApiException {
+        testOneEntitlementWithInitialBlockingStateImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneEntitlementWithInitialBlockingStateWithRegression() throws CatalogApiException {
+        testOneEntitlementWithInitialBlockingStateImpl(true);
+    }
+
+    private void testOneEntitlementWithInitialBlockingStateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -704,6 +778,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -753,7 +835,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(4).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
         assertEquals(events.get(2).getPrevPhase().getName(), "trial");
@@ -766,6 +847,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneEntitlementWithBlockingStatesSubscription() throws CatalogApiException {
+        testOneEntitlementWithBlockingStatesSubscriptionImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneEntitlementWithBlockingStatesSubscriptionWithRegression() throws CatalogApiException {
+        testOneEntitlementWithBlockingStatesSubscriptionImpl(true);
+    }
+
+    private void testOneEntitlementWithBlockingStatesSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -783,6 +873,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -836,7 +934,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(5).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -854,6 +951,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testWithMultipleEntitlements() throws CatalogApiException {
+        testWithMultipleEntitlementsImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testWithMultipleEntitlementsWithRegression() throws CatalogApiException {
+        testWithMultipleEntitlementsImpl(true);
+    }
+
+    private void testWithMultipleEntitlementsImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -873,11 +979,27 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition ent1Tr1 = createTransition(entitlementId1, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial1");
         allTransitions1.add(ent1Tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition ent2Tr1 = createTransition(entitlementId2, EventType.API_USER, ApiEventType.TRANSFER, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase2");
         allTransitions2.add(ent2Tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X ) {
+            final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
+                                                                     DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate2);
+        }
+
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition ent1Tr2 = createTransition(entitlementId1, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial1", "phase1");
@@ -952,7 +1074,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(8).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial1");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial1");
 
@@ -979,6 +1100,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testWithOverdueOffline() throws CatalogApiException {
+        testWithOverdueOfflineImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testWithOverdueOfflineWithRegression() throws CatalogApiException {
+        testWithOverdueOfflineImpl(true);
+    }
+
+    private void testWithOverdueOfflineImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -997,6 +1127,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
         final SubscriptionBaseTransition tr2 = createTransition(entitlementId, EventType.PHASE, null, requestedDate, effectiveDate, clock.getUTCNow(), "trial", "phase");
@@ -1075,7 +1213,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(5).getServiceName(), EntitlementOrderingBase.BILLING_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -1093,6 +1230,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
     public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+        testRemoveOverlappingBlockingStatesImpl(false);
+    }
+
+    public void testRemoveOverlappingBlockingStatesWithRegression() throws CatalogApiException {
+        testRemoveOverlappingBlockingStatesImpl(true);
+    }
+
+    public void testRemoveOverlappingBlockingStatesImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1110,6 +1255,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         // Overlapping ENT_STATE_BLOCKED - should merge
         effectiveDate = effectiveDate.plusDays(5);
         clock.addDays(5);
@@ -1165,7 +1318,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(3).getServiceName(), DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -1178,6 +1330,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/149")
     public void testVariousBlockingStatesAtTheSameEffectiveDate() throws CatalogApiException {
+        testVariousBlockingStatesAtTheSameEffectiveDateImpl(false);
+    }
+
+    public void testVariousBlockingStatesAtTheSameEffectiveDateWithRegression() throws CatalogApiException {
+        testVariousBlockingStatesAtTheSameEffectiveDateImpl(true);
+    }
+
+    private void testVariousBlockingStatesAtTheSameEffectiveDateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1195,6 +1355,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
+
         // 2013-02-10
         effectiveDate = effectiveDate.plusDays(40);
         clock.addDays(40);
@@ -1287,7 +1455,6 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertEquals(events.get(10).getServiceName(), overdueService);
 
         assertNull(events.get(0).getPrevPhase());
-        assertEquals(events.get(0).getNextPhase().getName(), "trial");
         assertNull(events.get(1).getPrevPhase());
         assertEquals(events.get(1).getNextPhase().getName(), "trial");
 
@@ -1404,9 +1571,11 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
                                                   null,
                                                   null,
                                                   null,
+                                                  null,
                                                   nextPlan,
                                                   nextPhase,
                                                   nextPriceList,
+                                                  null,
                                                   1L,
                                                   createdDate,
                                                   UUID.randomUUID(),
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
index 9a22d1d..07ddabf 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEntitlementDateHelper.java
@@ -18,13 +18,16 @@
 
 package org.killbill.billing.entitlement.api;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.Account;
-import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.entitlement.EntitlementTestSuiteNoDB;
-import org.mockito.Mockito;
+import org.killbill.billing.mock.MockAccountBuilder;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -33,54 +36,48 @@ import static org.testng.Assert.assertTrue;
 
 public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
 
-    private Account account;
     private EntitlementDateHelper dateHelper;
 
     @BeforeClass(groups = "fast")
     public void beforeMethod() throws Exception {
         super.beforeClass();
 
-        account = Mockito.mock(Account.class);
-        Mockito.when(accountInternalApi.getAccountByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(account);
-        Mockito.when(accountInternalApi.getImmutableAccountDataByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(account);
         dateHelper = new EntitlementDateHelper(clock);
         clock.resetDeltaFromReality();
     }
 
     @Test(groups = "fast")
-    public void testWithAccountInUtc() throws EntitlementApiException {
-
+    public void testWithAccountInUtc() throws AccountApiException, EntitlementApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
         clock.setDay(initialDate.plusDays(1));
 
-        Mockito.when(account.getTimeZone()).thenReturn(DateTimeZone.UTC);
+        final DateTime referenceDateTime = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
+        createAccount(DateTimeZone.UTC, referenceDateTime);
 
-        final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 15, 43, 25, 0, DateTimeZone.UTC);
-        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(initialDate, refererenceDateTime, internalCallContext);
+        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(initialDate, internalCallContext);
         final DateTime expectedDate = new DateTime(2013, 8, 7, 15, 43, 25, 0, DateTimeZone.UTC);
         Assert.assertEquals(targetDate, expectedDate);
     }
 
     @Test(groups = "fast")
-    public void testWithAccountInUtcMinus8() throws EntitlementApiException {
-
+    public void testWithAccountInUtcMinus8() throws AccountApiException, EntitlementApiException {
         final LocalDate inputDate = new LocalDate(2013, 8, 7);
         // Current time is in the future so we don't go through logic that will default to a Clock.getUTCNow.
         clock.setDay(inputDate.plusDays(3));
 
         final DateTimeZone timeZoneUtcMinus8 = DateTimeZone.forOffsetHours(-8);
-        Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcMinus8);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
-
         // We also use a reference time of 1, 28, 10, 0 -> DateTime in accountTimeZone will be (2013, 8, 7, 1, 28, 10)
-        final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 1, 28, 10, 0, DateTimeZone.UTC);
-        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, refererenceDateTime, internalCallContext);
+        final DateTime referenceDateTime = new DateTime(2013, 1, 1, 1, 28, 10, 0, DateTimeZone.UTC);
+
+        createAccount(timeZoneUtcMinus8, referenceDateTime);
+
+        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, internalCallContext);
 
         // Things to verify:
         // 1. Verify the resulting DateTime brings us back into the correct LocalDate (in the account timezone)
         Assert.assertEquals(new LocalDate(targetDate, timeZoneUtcMinus8), inputDate);
         // 2. Verify the resulting DateTime has the same reference time as we indicated (in UTC)
-        Assert.assertEquals(targetDate.toLocalTime(), refererenceDateTime.toLocalTime());
+        Assert.assertEquals(targetDate.toLocalTime(), referenceDateTime.toLocalTime());
 
         //
         // To be more specific, we should find a UTC Date, with the exact specified reference time, and with a LocalDate one day
@@ -90,24 +87,23 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testWithAccountInUtcPlus5() throws EntitlementApiException {
-
+    public void testWithAccountInUtcPlus5() throws AccountApiException, EntitlementApiException {
         final LocalDate inputDate = new LocalDate(2013, 8, 7);
         clock.setDay(inputDate.plusDays(1));
 
         final DateTimeZone timeZoneUtcPlus5 = DateTimeZone.forOffsetHours(+5);
-        Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcPlus5);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
-
         // We also use a reference time of 20, 28, 10, 0 -> DateTime in accountTimeZone will be (2013, 8, 7, 20, 28, 10)
-        final DateTime refererenceDateTime = new DateTime(2013, 1, 1, 20, 28, 10, 0, DateTimeZone.UTC);
-        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, refererenceDateTime, internalCallContext);
+        final DateTime referenceDateTime = new DateTime(2013, 1, 1, 20, 28, 10, 0, DateTimeZone.UTC);
+
+        createAccount(timeZoneUtcPlus5, referenceDateTime);
+
+        final DateTime targetDate = dateHelper.fromLocalDateAndReferenceTime(inputDate, internalCallContext);
 
         // Things to verify:
         // 1. Verify the resulting DateTime brings us back into the correct LocalDate (in the account timezone)
         Assert.assertEquals(new LocalDate(targetDate, timeZoneUtcPlus5), inputDate);
         // 2. Verify the resulting DateTime has the same reference time as we indicated (in UTC)
-        Assert.assertEquals(targetDate.toLocalTime(), refererenceDateTime.toLocalTime());
+        Assert.assertEquals(targetDate.toLocalTime(), referenceDateTime.toLocalTime());
 
         //
         // To be more specific, we should find a UTC Date, with the exact specified reference time, and with a LocalDate one day
@@ -117,18 +113,34 @@ public class TestEntitlementDateHelper extends EntitlementTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testIsBeforeOrEqualsToday() {
-
+    public void testIsBeforeOrEqualsToday() throws AccountApiException {
         clock.setTime(new DateTime(2013, 8, 7, 3, 28, 10, 0, DateTimeZone.UTC));
+
         final DateTimeZone timeZoneUtcMinus8 = DateTimeZone.forOffsetHours(-8);
-        Mockito.when(account.getTimeZone()).thenReturn(timeZoneUtcMinus8);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+
+        createAccount(timeZoneUtcMinus8, clock.getUTCNow());
 
         final DateTime inputDateEquals = new DateTime(2013, 8, 6, 23, 28, 10, 0, timeZoneUtcMinus8);
         // Check that our input date is greater than now
         assertTrue(inputDateEquals.compareTo(clock.getUTCNow()) > 0);
         // And yet since the LocalDate match the function returns true
-        final DateTime referenceDateTimeThatDoesNotMatter = new DateTime();
-        assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, referenceDateTimeThatDoesNotMatter, timeZoneUtcMinus8, internalCallContext));
+        assertTrue(dateHelper.isBeforeOrEqualsToday(inputDateEquals, timeZoneUtcMinus8, internalCallContext));
+    }
+
+    private void createAccount(final DateTimeZone dateTimeZone, final DateTime referenceDateTime) throws AccountApiException {
+        final Account accountData = new MockAccountBuilder().externalKey(UUID.randomUUID().toString())
+                                                            .timeZone(dateTimeZone)
+                                                            .createdDate(referenceDateTime)
+                                                            .build();
+
+        GuicyKillbillTestSuiteNoDB.createMockAccount(accountData,
+                                                     accountUserApi,
+                                                     accountInternalApi,
+                                                     immutableAccountInternalApi,
+                                                     nonEntityDao,
+                                                     clock,
+                                                     internalCallContextFactory,
+                                                     callContext,
+                                                     internalCallContext);
     }
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
index 3422f32..dd7b6b8 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestEventJson.java
@@ -31,7 +31,9 @@ public class TestEventJson extends EntitlementTestSuiteNoDB {
 
     @Test(groups = "fast", description = "Test Blocking event deserialization")
     public void testDefaultBlockingTransitionInternalEvent() throws Exception {
-        final BlockingTransitionInternalEvent e = new DefaultBlockingTransitionInternalEvent(UUID.randomUUID(), BlockingStateType.ACCOUNT, true, false, false, true, 1L, 2L, null);
+        final BlockingTransitionInternalEvent e = new DefaultBlockingTransitionInternalEvent(UUID.randomUUID(), "state", "svc",
+                                                                                             clock.getUTCNow(), BlockingStateType.ACCOUNT,
+                                                                                             true, false, false, true, 1L, 2L, null);
 
         final String json = mapper.writeValueAsString(e);
 
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
new file mode 100644
index 0000000..ae12fdc
--- /dev/null
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.entitlement.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestRegessionSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow", description = "Verify behavior with or without ENT_STARTED event works as expected")
+    public void testRegressionForNew_ENT_STARTED_event() throws Exception {
+
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        // Start the entitlement yesterday (does not m,ake sense, but we want to check later different of behavior)
+        final LocalDate entitlementEffectiveDate = initialDate.minusDays(1);
+
+        final Account account = createAccount(getAccountData(7));
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun",  BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, entitlementEffectiveDate, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        // Because of the BlockingState event ENT_STARTED, the entitlement date should be correctly set
+        Assert.assertEquals(entitlement.getEffectiveStartDate(), entitlementEffectiveDate);
+
+        final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+        Assert.assertEquals(bundles.size(), 1);
+        subscriptionBundleChecker(bundles, initialDate, entitlement, 0);
+
+        // Let's do some surgery and inactivate the ENT_STARTED BlockingState
+        final List<BlockingState> blockingStates = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, clock.getUTCNow(), internalCallContext);
+        assertEquals(blockingStates.size(), 1);
+        assertEquals(blockingStates.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_START);
+        blockingStateDao.unactiveBlockingState(blockingStates.get(0).getId(), internalCallContext);
+
+        final Entitlement oldSchoolEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        // Because the ENT_STARTED BlockingState has been invalidated, the startDate should now default to the billingDate
+        Assert.assertEquals(oldSchoolEntitlement.getEffectiveStartDate(), initialDate);
+
+        final List<SubscriptionBundle> oldSchoolBundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+        Assert.assertEquals(oldSchoolBundles.size(), 1);
+        subscriptionBundleChecker(oldSchoolBundles, initialDate, oldSchoolEntitlement, 0);
+    }
+
+    private void subscriptionBundleChecker(final List<SubscriptionBundle> bundles, final LocalDate billingStartDate, final Entitlement entitlement, final int idx) {
+        Assert.assertEquals(bundles.get(idx).getId(), entitlement.getBundleId());
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().size(), 1);
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().get(0).getId(), entitlement.getId());
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().size(), 3);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getEffectiveDate(), entitlement.getEffectiveStartDate());
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getEffectiveDate(), billingStartDate);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getEffectiveDate(), new LocalDate(2013, 9, 6));
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+    }
+
+}
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
index e8b76b0..d96a38c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
@@ -132,13 +132,13 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
 
         testListener.pushExpectedEvent(NextEvent.BLOCK);
         final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameBlock, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
-        blockingInternalApi.setBlockingState(state1, internalCallContext);
+        subscriptionApi.addBlockingState(state1, null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
@@ -146,7 +146,8 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         // Add blocking at bundle level.
         clock.addDays(1);
         testListener.pushExpectedEvent(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameBlock, service, clock.getUTCToday(), blockBilling, blockEntitlement, blockChange, ImmutableList.<PluginProperty>of(), callContext);
+        final BlockingState state2 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, stateNameBlock, service, blockChange, blockEntitlement, blockBilling, null);
+        subscriptionApi.addBlockingState(state2, null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
 
@@ -156,8 +157,8 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         // Remove blocking at account level
         clock.addDays(1);
         testListener.pushExpectedEvent(NextEvent.BLOCK);
-        final BlockingState state2 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameUnBlock, service, false, false, false, clock.getUTCNow());
-        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        final BlockingState state3 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameUnBlock, service, false, false, false, clock.getUTCNow());
+        subscriptionApi.addBlockingState(state3, null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
@@ -167,7 +168,8 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         // Remove blocking at bundle level.
         clock.addDays(1);
         testListener.pushExpectedEvent(NextEvent.BLOCK);
-        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameUnBlock, service, clock.getUTCToday(), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        final BlockingState state4 = new DefaultBlockingState(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, stateNameUnBlock, service, false, false, false, null);
+        subscriptionApi.addBlockingState(state4, null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index 19f605f..c15abba 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -58,9 +58,9 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         // This is a simple smoke test at the dao level only to make sure we do sane
         // things in case there are no future add-on cancellation events to add in the stream.
         // See TestEntitlementUtils for a more comprehensive test
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         final BlockingStateType type = BlockingStateType.SUBSCRIPTION;
@@ -68,14 +68,15 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         final String service = "service";
 
         // Verify initial state
-        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 0);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
 
-        // Set a state
+        // Set a state in the future so no event
         final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
         final BlockingState blockingState = new DefaultBlockingState(entitlement.getId(), type, state, service, false, false, false, stateDateTime);
         blockingStateDao.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableMap.<BlockingState, Optional<UUID>>of(blockingState, Optional.<UUID>of(entitlement.getBundleId())), internalCallContext);
+        assertListenerStatus();
 
-        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
+        Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 2);
     }
 
     // See https://github.com/killbill/killbill/issues/111
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
index c58a695..2f98acc 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -26,22 +26,15 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.killbill.billing.ObjectType;
-import org.killbill.billing.entitlement.AccountEventsStreams;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.testng.Assert;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.AccountEventsStreams;
 import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import org.killbill.billing.entitlement.EventsStream;
@@ -52,7 +45,10 @@ import org.killbill.billing.entitlement.api.DefaultEntitlementApi;
 import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
-import org.killbill.billing.entitlement.dao.BlockingStateSqlDao;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -61,8 +57,6 @@ import com.google.common.collect.Iterables;
 
 public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
-    private BlockingStateSqlDao sqlDao;
-    private Account account;
     private DefaultEntitlement baseEntitlement;
     private DefaultEntitlement addOnEntitlement;
     // Dates for the base plan only
@@ -73,20 +67,21 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     @BeforeMethod(groups = "slow")
     public void setUp() throws Exception {
-        sqlDao = dbi.onDemand(BlockingStateSqlDao.class);
-
         clock.setDay(initialDate);
-        account = createAccount(getAccountData(7));
+        final Account account = createAccount(getAccountData(7));
 
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.CREATE);
 
         // Create base entitlement
-        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        baseEntitlement = (DefaultEntitlement) entitlementApi.createBaseEntitlement(account.getId(), baseSpec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier baseSpec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        baseEntitlement = (DefaultEntitlement) entitlementApi.createBaseEntitlement(account.getId(), baseSpec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
 
         // Add ADD_ON
-        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        addOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier addOnSpec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        addOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOnSpec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
 
         // Verify the initial state
         checkFutureBlockingStatesToCancel(baseEntitlement, null, null);
@@ -238,7 +233,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     @Test(groups = "slow", description = "Verify add-ons blocking states are added for EOT change plans")
     public void testChangePlanEOT() throws Exception {
         // Change plan EOT to Assault-Rifle (Telescopic-Scope is included)
-        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, new LocalDate(2013, 10, 7), ImmutableList.<PluginProperty>of(), callContext);
+        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlanWithDate(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, new LocalDate(2013, 10, 7), ImmutableList.<PluginProperty>of(), callContext);
         // No blocking event (EOT)
         assertListenerStatus();
 
@@ -271,13 +266,13 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     @Test(groups = "slow", description = "Verify we don't mix add-ons for EOT changes")
     public void testChangePlanEOTWith2AddOns() throws Exception {
         // Add a second ADD_ON (Laser-Scope is available, not included)
-        testListener.pushExpectedEvents(NextEvent.CREATE);
-        final PlanPhaseSpecifier secondAddOnSpec = new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final DefaultEntitlement secondAddOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), secondAddOnSpec, null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier secondAddOnSpec = new PlanPhaseSpecifier("Laser-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final DefaultEntitlement secondAddOnEntitlement = (DefaultEntitlement) entitlementApi.addEntitlement(baseEntitlement.getBundleId(), secondAddOnSpec, null, clock.getUTCToday(), clock.getUTCToday(), false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Change plan EOT to Assault-Rifle (Telescopic-Scope is included)
-        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlanWithDate("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, new LocalDate(2013, 10, 7), ImmutableList.<PluginProperty>of(), callContext);
+        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlanWithDate(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, new LocalDate(2013, 10, 7), ImmutableList.<PluginProperty>of(), callContext);
         // No blocking event (EOT)
         assertListenerStatus();
 
@@ -285,7 +280,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         checkBlockingStatesDAO(changedBaseEntitlement, addOnEntitlement, baseEffectiveCancellationOrChangeDate, false);
         // ...but not for the second one
         final List<BlockingState> blockingStatesForSecondAddOn = blockingStatesForBlockedId(secondAddOnEntitlement.getId());
-        Assert.assertEquals(blockingStatesForSecondAddOn.size(), 0);
+        Assert.assertEquals(blockingStatesForSecondAddOn.size(), 1);
     }
 
     @Test(groups = "slow", description = "Verify add-ons blocking states are added for IMM change plans")
@@ -296,7 +291,7 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
         // Change plan IMM (upgrade) to Assault-Rifle (Telescopic-Scope is included)
         testListener.pushExpectedEvents(NextEvent.CHANGE, NextEvent.CANCEL, NextEvent.BLOCK);
-        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlan("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), callContext);
+        final DefaultEntitlement changedBaseEntitlement = (DefaultEntitlement) baseEntitlement.changePlan(new PlanSpecifier("Assault-Rifle", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // We need to add a 1s delay before invoking the eventsStreamBuilder in the checks below, because
@@ -330,9 +325,9 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     @Test(groups = "slow", description = "Verify add-ons are not active after base entitlement is cancelled")
     public void testCancelAddonsWhenBaseEntitlementIsCancelled() throws Exception {
         // Add a second ADD_ON
-        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
-        final PlanPhaseSpecifier addOn2Spec = new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final Entitlement addOn2Entitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOn2Spec, null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.PHASE);
+        final PlanPhaseSpecifier addOn2Spec = new PlanPhaseSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement addOn2Entitlement = entitlementApi.addEntitlement(baseEntitlement.getBundleId(), addOn2Spec, null, initialDate, initialDate, false, ImmutableList.<PluginProperty>of(), callContext);
         assertListenerStatus();
 
         // Date prior to the base cancellation date to verify it is not impacted by the base cancellation (in contrary to the second add-on)
@@ -359,8 +354,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         Assert.assertEquals(entitlementApi.getEntitlementForId(addOnEntitlement.getId(), callContext).getEffectiveEndDate(), addOn1CancellationDate);
         Assert.assertEquals(entitlementApi.getEntitlementForId(addOn2Entitlement.getId(), callContext).getEffectiveEndDate(), baseCancellationDate);
 
-        testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK, NextEvent.BLOCK);
-        clock.setDay(new LocalDate(2013, 10, 30));
+        // Move to addOn1CancellationDate
+        testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+        clock.setDay(new LocalDate(2013, 9, 9));
+        assertListenerStatus();
+
+
+        testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.BLOCK);
+        clock.setDay(new LocalDate(2013, 10, 10));
         assertListenerStatus();
 
         // Verify the cancellation dates
@@ -428,22 +429,22 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     // Test the DAO
     private void checkBlockingStatesDAO(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, final LocalDate effectiveBaseCancellationDate, final LocalDate effectiveAddOnCancellationDate, final boolean isBaseCancelled) {
         final List<BlockingState> blockingStatesForBaseEntitlement = blockingStatesForBlockedId(baseEntitlement.getId());
-        Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 1 : 0);
+        Assert.assertEquals(blockingStatesForBaseEntitlement.size(), isBaseCancelled ? 2 : 1);
         if (isBaseCancelled) {
-            Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getBlockedId(), baseEntitlement.getId());
-            Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getEffectiveDate().toLocalDate(), effectiveBaseCancellationDate);
-            Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getType(), BlockingStateType.SUBSCRIPTION);
-            Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
-            Assert.assertEquals(blockingStatesForBaseEntitlement.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
+            Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getBlockedId(), baseEntitlement.getId());
+            Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getEffectiveDate().toLocalDate(), effectiveBaseCancellationDate);
+            Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getType(), BlockingStateType.SUBSCRIPTION);
+            Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+            Assert.assertEquals(blockingStatesForBaseEntitlement.get(1).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
         }
 
         final List<BlockingState> blockingStatesForAddOn = blockingStatesForBlockedId(addOnEntitlement.getId());
-        Assert.assertEquals(blockingStatesForAddOn.size(), 1);
-        Assert.assertEquals(blockingStatesForAddOn.get(0).getBlockedId(), addOnEntitlement.getId());
-        Assert.assertEquals(blockingStatesForAddOn.get(0).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
-        Assert.assertEquals(blockingStatesForAddOn.get(0).getType(), BlockingStateType.SUBSCRIPTION);
-        Assert.assertEquals(blockingStatesForAddOn.get(0).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
-        Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
+        Assert.assertEquals(blockingStatesForAddOn.size(), 2);
+        Assert.assertEquals(blockingStatesForAddOn.get(1).getBlockedId(), addOnEntitlement.getId());
+        Assert.assertEquals(blockingStatesForAddOn.get(1).getEffectiveDate().toLocalDate(), effectiveAddOnCancellationDate);
+        Assert.assertEquals(blockingStatesForAddOn.get(1).getType(), BlockingStateType.SUBSCRIPTION);
+        Assert.assertEquals(blockingStatesForAddOn.get(1).getService(), EntitlementService.ENTITLEMENT_SERVICE_NAME);
+        Assert.assertEquals(blockingStatesForAddOn.get(1).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
     }
 
     private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
@@ -452,7 +453,6 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     }
 
     private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
-        setContextAccountRecordId();
         final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(internalCallContext);
 
         final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()),
@@ -471,7 +471,6 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
     }
 
     private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
-        setContextAccountRecordId();
         final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(internalCallContext);
 
         final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()),
@@ -493,8 +492,4 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
                                                                                        }
                                                                                    }));
     }
-
-    private void setContextAccountRecordId() {
-        internalCallContext.setAccountRecordId(nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID)));
-    }
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
index b4f0792..df95234 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteNoDB.java
@@ -20,6 +20,8 @@ package org.killbill.billing.entitlement;
 
 import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.entitlement.block.BlockingChecker;
 import org.killbill.billing.entitlement.dao.BlockingStateDao;
@@ -27,6 +29,7 @@ import org.killbill.billing.entitlement.glue.TestEntitlementModuleNoDB;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.tag.dao.TagDao;
 import org.killbill.bus.api.PersistentBus;
 import org.testng.annotations.AfterMethod;
@@ -40,8 +43,12 @@ import com.google.inject.Injector;
 public abstract class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @Inject
+    protected AccountUserApi accountUserApi;
+    @Inject
     protected AccountInternalApi accountInternalApi;
     @Inject
+    protected ImmutableAccountInternalApi immutableAccountInternalApi;
+    @Inject
     protected BlockingInternalApi blockingInternalApi;
     @Inject
     protected BlockingStateDao blockingStateDao;
@@ -57,6 +64,8 @@ public abstract class EntitlementTestSuiteNoDB extends GuicyKillbillTestSuiteNoD
     protected TagInternalApi tagInternalApi;
     @Inject
     protected BlockingChecker blockingChecker;
+    @Inject
+    protected NonEntityDao nonEntityDao;
 
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 82785c0..9f6f218 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -21,18 +21,14 @@ package org.killbill.billing.entitlement;
 import java.util.UUID;
 
 import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.config.Ini;
 import org.apache.shiro.config.IniSecurityManagerFactory;
 import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.subject.Subject;
 import org.apache.shiro.util.Factory;
 import org.apache.shiro.util.ThreadContext;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
-import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
@@ -60,7 +56,6 @@ import org.killbill.billing.subscription.api.SubscriptionBaseService;
 import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.api.AuditUserApi;
-import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.tag.dao.TagDao;
@@ -191,7 +186,7 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     private Catalog initCatalog(final CatalogService catalogService) throws Exception {
 
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog catalog = catalogService.getFullCatalog(true, true, internalCallContext);
         assertNotNull(catalog);
         return catalog;
     }
@@ -289,9 +284,7 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected Account createAccount(final AccountData accountData) throws AccountApiException {
         final Account account = accountApi.createAccount(accountData, callContext);
 
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        refreshCallContext(account.getId());
 
         return account;
     }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/glue/TestEntitlementModule.java b/entitlement/src/test/java/org/killbill/billing/entitlement/glue/TestEntitlementModule.java
index 5b7aab2..0694c41 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/glue/TestEntitlementModule.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/glue/TestEntitlementModule.java
@@ -22,6 +22,7 @@ import org.killbill.billing.mock.glue.MockTenantModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
 import org.killbill.billing.util.glue.KillBillShiroModule;
 import org.killbill.billing.util.glue.SecurityModule;
@@ -40,6 +41,7 @@ public class TestEntitlementModule extends DefaultEntitlementModule {
     protected void configure() {
         super.configure();
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new MockTenantModule(configSource));
 

invoice/pom.xml 35(+33 -2)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 57496d6..24b2082 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
@@ -55,6 +55,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -64,6 +79,11 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
@@ -73,6 +93,10 @@
             <artifactId>jdbi</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.joda</groupId>
+            <artifactId>joda-money</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-account</artifactId>
             <scope>test</scope>
@@ -156,11 +180,14 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
-            <artifactId>killbill-jdbi</artifactId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
@@ -177,6 +204,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-xmlloader</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-all</artifactId>
             <scope>test</scope>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java b/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
index 1c20ed9..a081e7b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/DefaultInvoiceService.java
@@ -18,6 +18,7 @@
 
 package org.killbill.billing.invoice.api;
 
+import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentNotifier;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.billing.invoice.InvoiceListener;
 import org.killbill.billing.invoice.InvoiceTagHandler;
@@ -36,13 +37,16 @@ public class DefaultInvoiceService implements InvoiceService {
     private final InvoiceListener invoiceListener;
     private final InvoiceTagHandler tagHandler;
     private final PersistentBus eventBus;
+    private final ParentInvoiceCommitmentNotifier parentInvoiceNotifier;
 
     @Inject
-    public DefaultInvoiceService(final InvoiceListener invoiceListener, final InvoiceTagHandler tagHandler, final PersistentBus eventBus, final NextBillingDateNotifier dateNotifier) {
+    public DefaultInvoiceService(final InvoiceListener invoiceListener, final InvoiceTagHandler tagHandler, final PersistentBus eventBus,
+                                 final NextBillingDateNotifier dateNotifier, final ParentInvoiceCommitmentNotifier parentInvoiceNotifier) {
         this.invoiceListener = invoiceListener;
         this.tagHandler = tagHandler;
         this.eventBus = eventBus;
         this.dateNotifier = dateNotifier;
+        this.parentInvoiceNotifier = parentInvoiceNotifier;
     }
 
     @Override
@@ -59,11 +63,13 @@ public class DefaultInvoiceService implements InvoiceService {
             throw new RuntimeException("Failed to register bus handlers", e);
         }
         dateNotifier.initialize();
+        parentInvoiceNotifier.initialize();
     }
 
     @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
     public void start() {
         dateNotifier.start();
+        parentInvoiceNotifier.start();
     }
 
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
@@ -75,5 +81,6 @@ public class DefaultInvoiceService implements InvoiceService {
             throw new RuntimeException("Failed to unregister bus handlers", e);
         }
         dateNotifier.stop();
+        parentInvoiceNotifier.stop();
     }
 }
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 2b02a73..7e8c68c 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
@@ -41,7 +41,7 @@ import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.commons.locker.GlobalLock;
 import org.killbill.commons.locker.GlobalLocker;
@@ -54,7 +54,6 @@ import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 
 public class InvoiceApiHelper {
@@ -127,6 +126,7 @@ public class InvoiceApiHelper {
                                             @Nullable final BigDecimal positiveAdjAmount,
                                             @Nullable final Currency currency,
                                             final LocalDate effectiveDate,
+                                            final String description,
                                             final InternalCallContext context) throws InvoiceApiException {
         final InvoiceItem invoiceItemToBeAdjusted = Iterables.<InvoiceItem>tryFind(invoiceToBeAdjusted.getInvoiceItems(),
                                                                                    new Predicate<InvoiceItem>() {
@@ -159,7 +159,7 @@ public class InvoiceApiHelper {
                                       invoiceItemToBeAdjusted.getInvoiceId(),
                                       invoiceItemToBeAdjusted.getAccountId(),
                                       effectiveDate,
-                                      null,
+                                      description,
                                       amountToAdjust,
                                       currencyForAdjustment,
                                       invoiceItemToBeAdjusted.getId());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index 6cd672a..d7eb9fd 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -39,6 +39,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.WithAccountLock;
 import org.killbill.billing.invoice.dao.InvoiceDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -146,8 +147,13 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
     }
 
     @Override
-    public InvoicePayment recordChargeback(final UUID paymentId, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
-        return new DefaultInvoicePayment(dao.postChargeback(paymentId, amount, currency, context));
+    public InvoicePayment recordChargeback(final UUID paymentId, final String chargebackTransactionExternalKey, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
+        return new DefaultInvoicePayment(dao.postChargeback(paymentId, chargebackTransactionExternalKey, amount, currency, context));
+    }
+
+    @Override
+    public InvoicePayment recordChargebackReversal(final UUID paymentId, final String chargebackTransactionExternalKey, final InternalCallContext context) throws InvoiceApiException {
+        return new DefaultInvoicePayment(dao.postChargebackReversal(paymentId, chargebackTransactionExternalKey, context));
     }
 
     @Override
@@ -157,6 +163,10 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
 
     @Override
     public Map<UUID, BigDecimal> validateInvoiceItemAdjustments(final UUID paymentId, final Map<UUID, BigDecimal> idWithAmount, final InternalTenantContext context) throws InvoiceApiException {
+        // We want to validate that only refund with invoice *item* adjustments are allowed (as opposed to refund with invoice adjustment)
+        if (idWithAmount.size() == 0) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_ITEMS_ADJUSTMENT_MISSING);
+        }
         final InvoicePayment invoicePayment = getInvoicePayment(paymentId, InvoicePaymentType.ATTEMPT, context);
         return dao.computeItemAdjustments(invoicePayment.getInvoiceId().toString(), idWithAmount, context);
     }
@@ -173,4 +183,9 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
         }).orNull();
         return resultOrNull != null ? new DefaultInvoicePayment(resultOrNull) : null;
     }
+
+    @Override
+    public void commitInvoice(final UUID invoiceId, final InternalCallContext context) throws InvoiceApiException {
+        dao.changeInvoiceStatus(invoiceId, InvoiceStatus.COMMITTED, context);
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index c7ba438..908e709 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -28,27 +28,29 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.account.api.ImmutableAccountData;
 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;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceApiHelper;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.WithAccountLock;
 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.CreditAdjInvoiceItem;
 import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -76,6 +78,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
@@ -114,8 +117,11 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public List<Invoice> getInvoicesByAccount(final UUID accountId, final TenantContext context) {
-        final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context));
+    public List<Invoice> getInvoicesByAccount(final UUID accountId, boolean includesMigrated, final TenantContext context) {
+        final List<InvoiceModelDao> invoicesByAccount = includesMigrated ?
+                                                        dao.getAllInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context)) :
+                                                        dao.getInvoicesByAccount(internalCallContextFactory.createInternalTenantContext(accountId, context));
+
         return fromInvoiceModelDao(invoicesByAccount);
     }
 
@@ -143,7 +149,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                   @Override
                                                   public Pagination<InvoiceModelDao> build() {
                                                       // Invoices will be shallow, i.e. won't contain items nor payments
-                                                      return dao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return dao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               new Function<InvoiceModelDao, Invoice>() {
@@ -162,7 +168,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                   @Override
                                                   public Pagination<InvoiceModelDao> build() {
                                                       // Invoices will be shallow, i.e. won't contain items nor payments
-                                                      return dao.searchInvoices(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return dao.searchInvoices(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               new Function<InvoiceModelDao, Invoice>() {
@@ -194,7 +200,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     @Override
     public Invoice getInvoiceByNumber(final Integer number, final TenantContext context) throws InvoiceApiException {
         // The account record id will be populated in the DAO
-        return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContext(context)));
+        return new DefaultInvoice(dao.getByNumber(number, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
     }
 
     @Override
@@ -208,15 +214,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                             final CallContext context) throws InvoiceApiException {
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(accountId, context);
 
-        final ImmutableAccountData account;
-        try {
-            account = accountUserApi.getImmutableAccountDataById(accountId, internalContext);
-        } catch (final AccountApiException e) {
-            throw new InvoiceApiException(e, ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, e.toString());
-        }
-
-        final DateTime processingDateTime = targetDate != null ? targetDate.toDateTimeAtCurrentTime(account.getTimeZone()) : null;
-        final Invoice result = dispatcher.processAccount(accountId, processingDateTime, dryRunArguments, internalContext);
+        final Invoice result = dispatcher.processAccount(accountId, targetDate, dryRunArguments, internalContext);
         if (result == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOTHING_TO_DO, accountId, targetDate != null ? targetDate : "null");
         } else {
@@ -261,7 +259,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     }
 
     @Override
-    public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final CallContext context) throws InvoiceApiException {
+    public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
         for (final InvoiceItem charge : charges) {
             if (charge.getAmount() == null || charge.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
                 throw new InvoiceApiException(ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID, charge.getAmount());
@@ -283,7 +281,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                     if (invoiceIdForExternalCharge == null) {
                         final Currency currency = charge.getCurrency();
                         if (newInvoicesForExternalCharges.get(currency) == null) {
-                            final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+                            final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
+                            final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency, status);
                             newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
                         }
                         invoiceForExternalCharge = newInvoicesForExternalCharges.get(currency);
@@ -322,18 +321,23 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         }
 
         return new CreditAdjInvoiceItem(creditItem.getId(), creditItem.getCreatedDate(), creditItem.getInvoiceId(), creditItem.getAccountId(),
-                                        creditItem.getStartDate(), creditItem.getAmount().negate(), creditItem.getCurrency());
+                                        creditItem.getStartDate(), creditItem.getDescription(), creditItem.getAmount().negate(), creditItem.getCurrency());
     }
 
     @Override
     public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount, final LocalDate effectiveDate,
-                                    final Currency currency, final CallContext context) throws InvoiceApiException {
-        return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, context);
+                                    final Currency currency, final boolean autoCommit, final String description, final CallContext context) throws InvoiceApiException {
+        return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, autoCommit, description, context);
     }
 
     @Override
     public InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount,
-                                              final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException {
+                                              final LocalDate effectiveDate, final Currency currency, final String description, final CallContext context) throws InvoiceApiException {
+        return insertCreditForInvoice(accountId, invoiceId, amount, effectiveDate, currency, false, description, context);
+    }
+
+    private InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
+                                               final Currency currency, final boolean autoCommit, final String description, final CallContext context) throws InvoiceApiException {
         if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
             throw new InvoiceApiException(ErrorCode.CREDIT_AMOUNT_INVALID, amount);
         }
@@ -347,9 +351,13 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                 // Create an invoice for that credit if it doesn't exist
                 final Invoice invoiceForCredit;
                 if (invoiceId == null) {
-                    invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+                    final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
+                    invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency, status);
                 } else {
                     invoiceForCredit = getInvoiceAndCheckCurrency(invoiceId, currency, context);
+                    if (InvoiceStatus.COMMITTED.equals(invoiceForCredit.getStatus())) {
+                        throw new InvoiceApiException(ErrorCode.INVOICE_ALREADY_COMMITTED, invoiceId);
+                    }
                 }
 
                 // Create the new credit
@@ -358,6 +366,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                       invoiceForCredit.getId(),
                                                       accountId,
                                                       effectiveDate,
+                                                      description,
                                                       // Note! The amount is negated here!
                                                       amount.negate(),
                                                       currency);
@@ -381,14 +390,14 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
 
     @Override
     public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
-                                                   final LocalDate effectiveDate, final CallContext context) throws InvoiceApiException {
-        return insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, context);
+                                                   final LocalDate effectiveDate, final String description, final CallContext context) throws InvoiceApiException {
+        return insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, description, context);
     }
 
     @Override
     public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId,
                                                    final LocalDate effectiveDate, @Nullable final BigDecimal amount,
-                                                   @Nullable final Currency currency, final CallContext context) throws InvoiceApiException {
+                                                   @Nullable final Currency currency, final String description, final CallContext context) throws InvoiceApiException {
         if (amount != null && amount.compareTo(BigDecimal.ZERO) <= 0) {
             throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE, amount);
         }
@@ -402,6 +411,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
                                                                                          amount,
                                                                                          currency,
                                                                                          effectiveDate,
+                                                                                         description,
                                                                                          internalCallContextFactory.createInternalCallContext(accountId, context));
                 invoice.addInvoiceItem(adjustmentItem);
 
@@ -455,6 +465,40 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         dao.consumeExstingCBAOnAccountWithUnpaidInvoices(accountId, internalCallContextFactory.createInternalCallContext(accountId, context));
     }
 
+    @Override
+    public UUID createMigrationInvoice(final UUID accountId, final LocalDate targetDate, final Iterable<InvoiceItem> items, final CallContext context) {
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(accountId, context);
+        final LocalDate createdDate = internalCallContext.toLocalDate(internalCallContext.getCreatedDate());
+        final InvoiceModelDao migrationInvoice = new InvoiceModelDao(accountId, createdDate, targetDate, items.iterator().next().getCurrency(), true);
+
+
+        final List<InvoiceItemModelDao> itemModelDaos = ImmutableList.copyOf(Iterables.transform(items, new Function<InvoiceItem, InvoiceItemModelDao>() {
+            @Override
+            public InvoiceItemModelDao apply(final InvoiceItem input) {
+                return new InvoiceItemModelDao(internalCallContext.getCreatedDate(),
+                                               input.getInvoiceItemType(),
+                                               migrationInvoice.getId(),
+                                               accountId,
+                                               input.getBundleId(),
+                                               input.getSubscriptionId(),
+                                               input.getDescription(),
+                                               input.getPlanName(),
+                                               input.getPhaseName(),
+                                               input.getUsageName(),
+                                               input.getStartDate(),
+                                               input.getEndDate(),
+                                               input.getAmount(),
+                                               input.getRate(),
+                                               input.getCurrency(),
+                                               input.getLinkedItemId());
+
+            }
+        }));
+
+        dao.createInvoice(migrationInvoice, itemModelDaos, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+        return migrationInvoice.getId();
+    }
+
     private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) {
         final DefaultInvoiceAdjustmentEvent event = new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
         try {
@@ -482,4 +526,46 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
         }
         return invoice;
     }
+
+    @Override
+    public void commitInvoice(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, context);
+        dao.changeInvoiceStatus(invoiceId, InvoiceStatus.COMMITTED, internalCallContext);
+    }
+
+    @Override
+    public void transferChildCreditToParent(final UUID childAccountId, final CallContext context) throws InvoiceApiException {
+
+        final Account childAccount;
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(childAccountId, ObjectType.ACCOUNT, context);
+        try {
+            childAccount = accountUserApi.getAccountById(childAccountId, internalCallContext);
+        } catch (AccountApiException e) {
+            throw new InvoiceApiException(e);
+        }
+
+        if (childAccount.getParentAccountId() == null) {
+            throw new InvoiceApiException(ErrorCode.ACCOUNT_DOES_NOT_HAVE_PARENT_ACCOUNT, childAccountId);
+        }
+
+        final BigDecimal accountCBA = getAccountCBA(childAccountId, context);
+        if (accountCBA.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new InvoiceApiException(ErrorCode.CHILD_ACCOUNT_MISSING_CREDIT, childAccountId);
+        }
+
+        dao.transferChildCreditToParent(childAccount, internalCallContext);
+
+    }
+
+    @Override
+    public List<InvoiceItem> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final TenantContext context) throws InvoiceApiException {
+        final InternalTenantContext  internalTenantContext = internalCallContextFactory.createInternalTenantContext(parentInvoiceId, ObjectType.INVOICE, context);
+        return ImmutableList.copyOf(Collections2.transform(dao.getInvoiceItemsByParentInvoice(parentInvoiceId, internalTenantContext),
+                                                                    new Function<InvoiceItemModelDao, InvoiceItem>() {
+            @Override
+            public InvoiceItem apply(final InvoiceItemModelDao input) {
+                return InvoiceItemFactory.fromModelDao(input);
+            }
+        }));
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
index 7431845..31acf55 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/calculator/InvoiceCalculatorUtils.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -21,7 +23,6 @@ import java.math.BigDecimal;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
@@ -36,10 +37,8 @@ public abstract class InvoiceCalculatorUtils {
 
     // Invoice adjustments
     public static boolean isInvoiceAdjustmentItem(final InvoiceItem invoiceItem, final Iterable<InvoiceItem> otherInvoiceItems) {
-        // Either REFUND_ADJ
-        return InvoiceItemType.REFUND_ADJ.equals(invoiceItem.getInvoiceItemType()) ||
-               // Or invoice level credit, i.e. credit adj, but NOT on its on own invoice
-               (InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType()) &&
+        // Invoice level credit, i.e. credit adj, but NOT on its on own invoice
+        return (InvoiceItemType.CREDIT_ADJ.equals(invoiceItem.getInvoiceItemType()) &&
                 !(Iterables.size(otherInvoiceItems) == 1 &&
                   InvoiceItemType.CBA_ADJ.equals(otherInvoiceItems.iterator().next().getInvoiceItemType()) &&
                   otherInvoiceItems.iterator().next().getInvoiceId().equals(invoiceItem.getInvoiceId()) &&
@@ -56,6 +55,11 @@ public abstract class InvoiceCalculatorUtils {
         return InvoiceItemType.CBA_ADJ.equals(invoiceItem.getInvoiceItemType());
     }
 
+    // Item from Parent Invoice
+    public static boolean isParentSummaryItem(final InvoiceItem invoiceItem) {
+        return InvoiceItemType.PARENT_SUMMARY.equals(invoiceItem.getInvoiceItemType());
+    }
+
     // Regular line item (charges)
     public static boolean isCharge(final InvoiceItem invoiceItem) {
         return InvoiceItemType.TAX.equals(invoiceItem.getInvoiceItemType()) ||
@@ -67,20 +71,45 @@ public abstract class InvoiceCalculatorUtils {
 
     public static BigDecimal computeInvoiceBalance(final Currency currency,
                                                    @Nullable final Iterable<InvoiceItem> invoiceItems,
-                                                   @Nullable final Iterable<InvoicePayment> invoicePayments) {
-        final BigDecimal invoiceBalance = computeInvoiceAmountCharged(currency, invoiceItems)
+                                                   @Nullable final Iterable<InvoicePayment> invoicePayments,
+                                                   boolean writtenOffOrMigrated) {
+        if (writtenOffOrMigrated) {
+            return BigDecimal.ZERO;
+        }
+
+        final BigDecimal amountPaid = computeInvoiceAmountPaid(currency, invoicePayments)
+                .add(computeInvoiceAmountRefunded(currency, invoicePayments));
+
+        final BigDecimal chargedAmount = computeInvoiceAmountCharged(currency, invoiceItems)
                 .add(computeInvoiceAmountCredited(currency, invoiceItems))
-                .add(computeInvoiceAmountAdjustedForAccountCredit(currency, invoiceItems))
-                .add(
-                        computeInvoiceAmountPaid(currency, invoicePayments).negate()
-                                .add(
-                                        computeInvoiceAmountRefunded(currency, invoicePayments).negate()
-                                    )
-                    );
+                .add(computeInvoiceAmountAdjustedForAccountCredit(currency, invoiceItems));
+
+        final BigDecimal invoiceBalance = chargedAmount.add(amountPaid.negate());
 
         return KillBillMoney.of(invoiceBalance, currency);
     }
 
+    public static BigDecimal computeChildInvoiceAmount(final Currency currency,
+                                                       @Nullable final Iterable<InvoiceItem> invoiceItems) {
+
+        final Iterable<InvoiceItem> chargeItems = Iterables.filter(invoiceItems, new Predicate<InvoiceItem>() {
+            @Override
+            public boolean apply(@Nullable final InvoiceItem input) {
+                return isCharge(input);
+            }
+        });
+
+        if (Iterables.isEmpty(chargeItems)) {
+            // return only credit amount to be subtracted to parent item amount
+            return computeInvoiceAmountCredited(currency, invoiceItems).negate();
+        }
+
+        final BigDecimal chargedAmount = computeInvoiceAmountCharged(currency, invoiceItems)
+                .add(computeInvoiceAmountCredited(currency, invoiceItems))
+                .add(computeInvoiceAmountAdjustedForAccountCredit(currency, invoiceItems));
+        return KillBillMoney.of(chargedAmount, currency);
+    }
+
     // Snowflake for the CREDIT_ADJ on its own invoice
     private static BigDecimal computeInvoiceAmountAdjustedForAccountCredit(final Currency currency, final Iterable<InvoiceItem> invoiceItems) {
         BigDecimal amountAdjusted = BigDecimal.ZERO;
@@ -124,7 +153,8 @@ public abstract class InvoiceCalculatorUtils {
 
             if (isCharge(invoiceItem) ||
                 isInvoiceAdjustmentItem(invoiceItem, otherInvoiceItems) ||
-                isInvoiceItemAdjustmentItem(invoiceItem)) {
+                isInvoiceItemAdjustmentItem(invoiceItem) ||
+                isParentSummaryItem(invoiceItem)) {
                 amountCharged = amountCharged.add(invoiceItem.getAmount());
             }
         }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/config/MultiTenantInvoiceConfig.java b/invoice/src/main/java/org/killbill/billing/invoice/config/MultiTenantInvoiceConfig.java
new file mode 100644
index 0000000..79a0a45
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/config/MultiTenantInvoiceConfig.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.invoice.config;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.glue.InvoiceModule;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
+import org.killbill.billing.util.config.definition.KillbillConfig;
+import org.killbill.billing.util.config.tenant.CacheConfig;
+import org.killbill.billing.util.config.tenant.MultiTenantConfigBase;
+import org.skife.config.TimeSpan;
+
+public class MultiTenantInvoiceConfig extends MultiTenantConfigBase implements InvoiceConfig {
+
+    private final InvoiceConfig staticConfig;
+
+    @Inject
+    public MultiTenantInvoiceConfig(@Named(InvoiceModule.STATIC_CONFIG) final InvoiceConfig staticConfig, final CacheConfig cacheConfig) {
+        super(cacheConfig);
+        this.staticConfig = staticConfig;
+    }
+
+    @Override
+    public int getNumberOfMonthsInFuture() {
+        return staticConfig.getNumberOfMonthsInFuture();
+    }
+
+    @Override
+    public int getNumberOfMonthsInFuture(final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getNumberOfMonthsInFuture", tenantContext);
+        if (result != null) {
+            return Integer.parseInt(result);
+        }
+        return getNumberOfMonthsInFuture();
+    }
+
+    @Override
+    public TimeSpan getDryRunNotificationSchedule() {
+        return staticConfig.getDryRunNotificationSchedule();
+    }
+
+    @Override
+    public TimeSpan getDryRunNotificationSchedule(final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getDryRunNotificationSchedule", tenantContext);
+        if (result != null) {
+            return new TimeSpan(result);
+        }
+        return getDryRunNotificationSchedule();
+    }
+
+    @Override
+    public int getMaxRawUsagePreviousPeriod() {
+        return staticConfig.getMaxRawUsagePreviousPeriod();
+    }
+
+    @Override
+    public int getMaxRawUsagePreviousPeriod(final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getMaxRawUsagePreviousPeriod", tenantContext);
+        if (result != null) {
+            return Integer.parseInt(result);
+        }
+        return getMaxRawUsagePreviousPeriod();
+    }
+
+    @Override
+    public boolean isEmailNotificationsEnabled() {
+        return staticConfig.isEmailNotificationsEnabled();
+    }
+
+    @Override
+    public int getMaxGlobalLockRetries() {
+        return staticConfig.getMaxGlobalLockRetries();
+    }
+
+    @Override
+    protected Class<? extends KillbillConfig> getConfigClass() {
+        return InvoiceConfig.class;
+    }
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
index a5cf5c4..230fdd1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
@@ -21,15 +21,20 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entity.EntityPersistenceException;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Ordering;
 
 public class CBADao {
@@ -60,7 +65,7 @@ public class CBADao {
     // We expect a clean up to date invoice, with all the items except the cba, that we will compute in that method
     public InvoiceItemModelDao computeCBAComplexity(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
 
-        final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
+        final BigDecimal balance = getInvoiceBalance(invoice);
 
         // Current balance is negative, we need to generate a credit (positive CBA amount).
         if (balance.compareTo(BigDecimal.ZERO) < 0) {
@@ -82,6 +87,31 @@ public class CBADao {
         }
     }
 
+    private BigDecimal getInvoiceBalance(final InvoiceModelDao invoice) {
+
+        final InvoiceModelDao parentInvoice = invoice.getParentInvoice();
+        if ((parentInvoice != null) && (InvoiceModelDaoHelper.getBalance(parentInvoice).compareTo(BigDecimal.ZERO) == 0)) {
+            final Iterable<InvoiceItemModelDao> items = Iterables.filter(parentInvoice.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
+                @Override
+                public boolean apply(@Nullable final InvoiceItemModelDao input) {
+                    return input.getChildAccountId().equals(invoice.getAccountId());
+                }
+            });
+
+            final BigDecimal childInvoiceAmountCharged = InvoiceModelDaoHelper.getAmountCharged(invoice);
+            BigDecimal parentInvoiceAmountChargedForChild = BigDecimal.ZERO;
+
+            for (InvoiceItemModelDao itemModel : items) {
+                parentInvoiceAmountChargedForChild = parentInvoiceAmountChargedForChild.add(itemModel.getAmount());
+            }
+
+            return childInvoiceAmountCharged.add(parentInvoiceAmountChargedForChild.negate());
+
+        }
+
+        return InvoiceModelDaoHelper.getBalance(invoice);
+    }
+
     // We let the code below rehydrate the invoice before we can add the CBA item
     public void addCBAComplexityFromTransaction(final UUID invoiceId, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException, InvoiceApiException {
 
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 b6ed5c8..204a0e5 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
@@ -32,6 +32,7 @@ import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
@@ -42,15 +43,22 @@ import org.killbill.billing.invoice.api.DefaultInvoicePaymentErrorEvent;
 import org.killbill.billing.invoice.api.DefaultInvoicePaymentInfoEvent;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
+import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
+import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
+import org.killbill.billing.invoice.model.DefaultInvoice;
+import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.invoice.notification.NextBillingDatePoster;
+import org.killbill.billing.invoice.notification.ParentInvoiceCommitmentPoster;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
@@ -83,7 +91,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                                                                                         .onResultOf(new Function<InvoiceModelDao, Comparable>() {
                                                                                             @Override
                                                                                             public Comparable apply(final InvoiceModelDao invoice) {
-                                                                                                return invoice.getTargetDate();
+                                                                                                return invoice.getTargetDate() == null ? invoice.getCreatedDate().toLocalDate() : invoice.getTargetDate();
                                                                                             }
                                                                                         });
 
@@ -91,7 +99,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                                                                                                                        InvoiceItemType.FIXED,
                                                                                                                        InvoiceItemType.RECURRING,
                                                                                                                        InvoiceItemType.TAX,
-                                                                                                                       InvoiceItemType.USAGE);
+                                                                                                                       InvoiceItemType.USAGE,
+                                                                                                                       InvoiceItemType.PARENT_SUMMARY);
 
     private final NextBillingDatePoster nextBillingDatePoster;
     private final PersistentBus eventBus;
@@ -102,6 +111,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     private final Clock clock;
     private final CacheControllerDispatcher cacheControllerDispatcher;
     private final NonEntityDao nonEntityDao;
+    private final ParentInvoiceCommitmentPoster parentInvoiceCommitmentPoster;
 
     @Inject
     public DefaultInvoiceDao(final IDBI dbi,
@@ -113,6 +123,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                              final InvoiceConfig invoiceConfig,
                              final InvoiceDaoHelper invoiceDaoHelper,
                              final CBADao cbaDao,
+                             final ParentInvoiceCommitmentPoster parentInvoiceCommitmentPoster,
                              final InternalCallContextFactory internalCallContextFactory) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao, internalCallContextFactory), InvoiceSqlDao.class);
         this.nextBillingDatePoster = nextBillingDatePoster;
@@ -124,6 +135,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
         this.clock = clock;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.nonEntityDao = nonEntityDao;
+        this.parentInvoiceCommitmentPoster = parentInvoiceCommitmentPoster;
     }
 
     @Override
@@ -136,9 +148,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
             @Override
             public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
-                final List<InvoiceModelDao> invoices = ImmutableList.<InvoiceModelDao>copyOf(INVOICE_MODEL_DAO_ORDERING.sortedCopy(Iterables.<InvoiceModelDao>filter(invoiceDao.getByAccountRecordId(context),
+                final List<InvoiceModelDao> invoices = ImmutableList.<InvoiceModelDao>copyOf(INVOICE_MODEL_DAO_ORDERING.sortedCopy(Iterables.<InvoiceModelDao>filter(invoiceSqlDao.getByAccountRecordId(context),
                                                                                                                                                                      new Predicate<InvoiceModelDao>() {
                                                                                                                                                                          @Override
                                                                                                                                                                          public boolean apply(final InvoiceModelDao invoice) {
@@ -191,9 +203,9 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
             @Override
             public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
 
-                final InvoiceModelDao invoice = invoiceDao.getById(invoiceId.toString(), context);
+                final InvoiceModelDao invoice = invoiceSqlDao.getById(invoiceId.toString(), context);
                 if (invoice == null) {
                     throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
                 }
@@ -266,7 +278,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                         createInvoiceItemFromTransaction(transInvoiceItemSqlDao, invoiceItemModelDao, context);
                     }
                     cbaDao.addCBAComplexityFromTransaction(invoice, entitySqlDaoWrapperFactory, context);
-                    notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoice.getAccountId(), callbackDateTimePerSubscriptions, context);
+                    if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
+                        notifyOfFutureBillingEvents(entitySqlDaoWrapperFactory, invoice.getAccountId(), callbackDateTimePerSubscriptions, context);
+                    }
+                    if (invoice.isParentInvoice()) {
+                        notifyOfParentInvoiceCreation(entitySqlDaoWrapperFactory, invoice, callbackDateTimePerSubscriptions, context);
+                    }
                 }
                 return null;
             }
@@ -284,11 +301,13 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
                 for (final InvoiceModelDao invoiceModelDao : invoices) {
                     boolean madeChanges = false;
+                    boolean newInvoice = false;
 
                     // Create the invoice if needed
                     if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
                         invoiceSqlDao.create(invoiceModelDao, context);
                         madeChanges = true;
+                        newInvoice = true;
                     }
 
                     // Create the invoice items if needed
@@ -302,10 +321,16 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
                     if (madeChanges) {
                         cbaDao.addCBAComplexityFromTransaction(invoiceModelDao.getId(), entitySqlDaoWrapperFactory, context);
+                    }
 
-                        // Notify the bus since the balance of the invoice changed
-                        // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
-                        notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
+                    if (InvoiceStatus.COMMITTED.equals(invoiceModelDao.getStatus())) {
+                        if (newInvoice) {
+                            notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
+                        } else if (madeChanges) {
+                            // Notify the bus since the balance of the invoice changed (only if the invoice is COMMITTED)
+                            // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
+                            notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
+                        }
                     }
                 }
 
@@ -372,7 +397,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 BigDecimal accountBalance = BigDecimal.ZERO;
                 final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(entitySqlDaoWrapperFactory, context);
                 for (final InvoiceModelDao cur : invoices) {
-                    accountBalance = accountBalance.add(InvoiceModelDaoHelper.getBalance(cur));
+                    accountBalance = accountBalance.add((new DefaultInvoice(cur)).getBalance());
                     cba = cba.add(InvoiceModelDaoHelper.getCBAAmount(cur));
                 }
                 return accountBalance.subtract(cba);
@@ -434,7 +459,11 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     public InvoicePaymentModelDao createRefund(final UUID paymentId, final BigDecimal requestedRefundAmount, final boolean isInvoiceAdjusted,
                                                final Map<UUID, BigDecimal> invoiceItemIdsWithNullAmounts, final String transactionExternalKey,
                                                final InternalCallContext context) throws InvoiceApiException {
-        final boolean isInvoiceItemAdjusted = isInvoiceAdjusted && invoiceItemIdsWithNullAmounts.size() > 0;
+
+
+        if (isInvoiceAdjusted && invoiceItemIdsWithNullAmounts.size() == 0) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_ITEMS_ADJUSTMENT_MISSING);
+        }
 
         return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
             @Override
@@ -465,7 +494,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
                 // Before we go further, check if that refund already got inserted -- the payment system keeps a state machine
                 // and so this call may be called several time for the same  paymentCookieId (which is really the refundId)
-                final InvoicePaymentModelDao existingRefund = transactional.getPaymentsForCookieId(transactionExternalKey, context);
+                final InvoicePaymentModelDao existingRefund = transactional.getPaymentForCookieId(transactionExternalKey, context);
                 if (existingRefund != null) {
                     return existingRefund;
                 }
@@ -487,18 +516,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 // At this point, we created the refund which made the invoice balance positive and applied any existing
                 // available CBA to that invoice.
                 // We now need to adjust the invoice and/or invoice items if needed and specified.
-                if (isInvoiceAdjusted && !isInvoiceItemAdjusted) {
-                    // Invoice adjustment
-                    final BigDecimal maxBalanceToAdjust = (invoiceBalanceAfterRefund.compareTo(BigDecimal.ZERO) <= 0) ? BigDecimal.ZERO : invoiceBalanceAfterRefund;
-                    final BigDecimal requestedPositiveAmountToAdjust = requestedPositiveAmount.compareTo(maxBalanceToAdjust) > 0 ? maxBalanceToAdjust : requestedPositiveAmount;
-                    if (requestedPositiveAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
-                        final InvoiceItemModelDao adjItem = new InvoiceItemModelDao(context.getCreatedDate(), InvoiceItemType.REFUND_ADJ, invoice.getId(), invoice.getAccountId(),
-                                                                                    null, null, null, null, null, null, context.getCreatedDate().toLocalDate(), null,
-                                                                                    requestedPositiveAmountToAdjust.negate(), null, invoice.getCurrency(), null);
-                        createInvoiceItemFromTransaction(transInvoiceItemDao, adjItem, context);
-                        invoice.addInvoiceItem(adjItem);
-                    }
-                } else if (isInvoiceAdjusted) {
+                if (isInvoiceAdjusted) {
                     // Invoice item adjustment
                     for (final UUID invoiceItemId : invoiceItemIdsWithAmounts.keySet()) {
                         final BigDecimal adjAmount = invoiceItemIdsWithAmounts.get(invoiceItemId);
@@ -524,7 +542,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     @Override
-    public InvoicePaymentModelDao postChargeback(final UUID paymentId, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
+    public InvoicePaymentModelDao postChargeback(final UUID paymentId, final String chargebackTransactionExternalKey, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
         return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
             @Override
             public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
@@ -561,7 +579,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 final InvoicePaymentModelDao chargeBack = new InvoicePaymentModelDao(UUIDs.randomUUID(), context.getCreatedDate(), InvoicePaymentType.CHARGED_BACK,
                                                                                      payment.getInvoiceId(), payment.getPaymentId(), context.getCreatedDate(),
                                                                                      requestedChargedBackAmount.negate(), payment.getCurrency(), payment.getProcessedCurrency(),
-                                                                                     null, payment.getId(), true);
+                                                                                     chargebackTransactionExternalKey, payment.getId(), true);
                 transactional.create(chargeBack, context);
 
                 // Notify the bus since the balance of the invoice changed
@@ -577,6 +595,42 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     }
 
     @Override
+    public InvoicePaymentModelDao postChargebackReversal(final UUID paymentId, final String chargebackTransactionExternalKey, final InternalCallContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoicePaymentModelDao>() {
+            @Override
+            public InvoicePaymentModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoicePaymentSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoicePaymentSqlDao.class);
+
+                final InvoicePaymentModelDao invoicePayment = transactional.getPaymentForCookieId(chargebackTransactionExternalKey, context);
+                if (invoicePayment == null) {
+                    throw new InvoiceApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId);
+                }
+
+                transactional.updateAttempt(invoicePayment.getRecordId(),
+                                            invoicePayment.getPaymentId().toString(),
+                                            invoicePayment.getPaymentDate().toDate(),
+                                            invoicePayment.getAmount(),
+                                            invoicePayment.getCurrency(),
+                                            invoicePayment.getProcessedCurrency(),
+                                            invoicePayment.getPaymentCookieId(),
+                                            invoicePayment.getLinkedInvoicePaymentId() == null ? null : invoicePayment.getLinkedInvoicePaymentId().toString(),
+                                            false,
+                                            context);
+                final InvoicePaymentModelDao chargebackReversed = transactional.getByRecordId(invoicePayment.getRecordId(), context);
+
+                // Notify the bus since the balance of the invoice changed
+                final UUID accountId = transactional.getAccountIdFromInvoicePaymentId(chargebackReversed.getId().toString(), context);
+
+                cbaDao.addCBAComplexityFromTransaction(chargebackReversed.getInvoiceId(), entitySqlDaoWrapperFactory, context);
+
+                notifyBusOfInvoicePayment(entitySqlDaoWrapperFactory, chargebackReversed, accountId, context.getUserToken(), context);
+
+                return chargebackReversed;
+            }
+        });
+    }
+
+    @Override
     public InvoiceItemModelDao doCBAComplexity(final InvoiceModelDao invoice, final InternalCallContext context) throws InvoiceApiException {
         return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceItemModelDao>() {
             @Override
@@ -855,7 +909,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
     private void notifyOfFutureBillingEvents(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
                                              final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext internalCallContext) {
 
-        final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule().getMillis();
+        final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule(internalCallContext).getMillis();
         final boolean isInvoiceNotificationEnabled = dryRunNotificationTime > 0;
         for (final UUID subscriptionId : callbackDateTimePerSubscriptions.getNotifications().keySet()) {
             final List<SubscriptionNotification> callbackDateTimeUTC = callbackDateTimePerSubscriptions.getNotifications().get(subscriptionId);
@@ -863,10 +917,10 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                 if (isInvoiceNotificationEnabled) {
                     final DateTime curDryRunNotificationTime = cur.getEffectiveDate().minus(dryRunNotificationTime);
                     final DateTime effectiveCurDryRunNotificationTime = (curDryRunNotificationTime.isAfter(clock.getUTCNow())) ? curDryRunNotificationTime : clock.getUTCNow();
-                    nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur.getEffectiveDate(), callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
+                    nextBillingDatePoster.insertNextBillingDryRunNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, effectiveCurDryRunNotificationTime, cur.getEffectiveDate(), internalCallContext);
                 }
                 if (cur.isForInvoiceNotificationTrigger()) {
-                    nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur.getEffectiveDate(), callbackDateTimePerSubscriptions.getAccountDateAndTimeZoneContext(), internalCallContext);
+                    nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, cur.getEffectiveDate(), internalCallContext);
                 }
             }
         }
@@ -941,4 +995,195 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
             throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_ITEM_INVALID, invoiceItemToBeAdjusted.getId());
         }
     }
+
+    @Override
+    public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newStatus,
+                                    final InternalCallContext context) throws InvoiceApiException {
+        transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+                // Retrieve the invoice and make sure it belongs to the right account
+                final InvoiceModelDao invoice = transactional.getById(invoiceId.toString(), context);
+
+                if (invoice == null ) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+                }
+
+                if (invoice.getStatus().equals(newStatus)) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_STATUS, newStatus, invoiceId, invoice.getStatus());
+                }
+
+                transactional.updateStatus(invoiceId.toString(), newStatus.toString(), context);
+
+                if (InvoiceStatus.COMMITTED.equals(newStatus)) {
+                    // notify invoice creation event
+                    notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoice, context);
+                }
+
+                return null;
+            }
+        });
+    }
+
+    private void notifyBusOfInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InvoiceModelDao invoice, final InternalCallContext context) {
+        try {
+            final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
+            final DefaultInvoiceCreationEvent event = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
+                                                                                                            balance, invoice.getCurrency(),
+                                                                                                            context.getAccountRecordId(), context.getTenantRecordId(),
+                                                                                                            context.getUserToken());
+            eventBus.postFromTransaction(event, entitySqlDaoWrapperFactory.getHandle().getConnection());
+        } catch (final EventBusException e) {
+            log.error(String.format("Failed to post invoice creation event %s for account %s", invoice.getAccountId()), e);
+        }
+    }
+
+    private void notifyOfParentInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InvoiceModelDao parentInvoice,
+                                               final FutureAccountNotifications callbackDateTime, final InternalCallContext context) {
+        DateTime futureNotificationDate = parentInvoice.getCreatedDate().withTimeAtStartOfDay().plusDays(1);
+        parentInvoiceCommitmentPoster.insertParentInvoiceFromTransactionInternal(entitySqlDaoWrapperFactory, parentInvoice.getId(), futureNotificationDate, context);
+    }
+
+    @Override
+    public void createParentChildInvoiceRelation(final InvoiceParentChildModelDao invoiceRelation, final InternalCallContext context) throws InvoiceApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceParentChildrenSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceParentChildrenSqlDao.class);
+                transactional.create(invoiceRelation, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceParentChildModelDao> getChildInvoicesByParentInvoiceId(final UUID parentInvoiceId, final InternalCallContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceParentChildModelDao>>() {
+            @Override
+            public List<InvoiceParentChildModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceParentChildrenSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceParentChildrenSqlDao.class);
+                return transactional.getChildInvoicesByParentInvoiceId(parentInvoiceId.toString(), context);
+            }
+        });
+    }
+
+    @Override
+    public InvoiceModelDao getParentDraftInvoice(final UUID parentAccountId, final InternalCallContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
+            @Override
+            public InvoiceModelDao inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                InvoiceModelDao invoice = invoiceSqlDao.getParentDraftInvoice(parentAccountId.toString(), context);
+                if (invoice != null) {
+                    invoiceDaoHelper.populateChildren(invoice, entitySqlDaoWrapperFactory, context);
+                }
+                return invoice;
+            }
+        });
+    }
+
+    @Override
+    public void updateInvoiceItemAmount(final UUID invoiceItemId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
+        transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceItemSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+
+                // Retrieve the invoice and make sure it belongs to the right account
+                final InvoiceItemModelDao invoiceItem = transactional.getById(invoiceItemId.toString(), context);
+
+                if (invoiceItem == null ) {
+                    throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
+                }
+
+                transactional.updateAmount(invoiceItemId.toString(), amount, context);
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public void transferChildCreditToParent(final Account childAccount, final InternalCallContext childAccountContext) throws InvoiceApiException {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+
+                // Need to create an internalCallContext for parent account because it's needed to save the correct accountRecordId in Invoice tables.
+                // Then it's used to load invoices by account.
+                final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getParentAccountId(), childAccountContext);
+                final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), childAccountContext);
+
+                final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+                final InvoiceItemSqlDao transInvoiceItemSqlDao = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+
+                // create child and parent invoices
+
+                final DateTime effectiveDate = childAccountContext.getCreatedDate();
+                final BigDecimal accountCBA = getAccountCBA(childAccount.getId(), childAccountContext);
+
+                // create external charge to child account
+                final Invoice invoiceForExternalCharge = new DefaultInvoice(childAccount.getId(), effectiveDate.toLocalDate(),
+                                                                            effectiveDate.toLocalDate(),
+                                                                            childAccount.getCurrency(), InvoiceStatus.COMMITTED);
+                final String chargeDescription = "Charge to move credit from child to parent account";
+                final InvoiceItem externalChargeItem = new ExternalChargeInvoiceItem(UUIDs.randomUUID(),
+                                                                                 effectiveDate,
+                                                                                 invoiceForExternalCharge.getId(),
+                                                                                 childAccount.getId(),
+                                                                                 null,
+                                                                                 chargeDescription,
+                                                                                 effectiveDate.toLocalDate(),
+                                                                                 accountCBA,
+                                                                                 childAccount.getCurrency());
+                invoiceForExternalCharge.addInvoiceItem(externalChargeItem);
+
+                // create credit to parent account
+                final Invoice invoiceForCredit = new DefaultInvoice(childAccount.getParentAccountId(), effectiveDate.toLocalDate(), effectiveDate.toLocalDate(),
+                                                                    childAccount.getCurrency(), InvoiceStatus.COMMITTED);
+                final String creditDescription = "Credit migrated from child account " + childAccount.getId();
+                final InvoiceItem creditItem = new CreditAdjInvoiceItem(UUIDs.randomUUID(),
+                                                                        effectiveDate,
+                                                                        invoiceForCredit.getId(),
+                                                                        childAccount.getParentAccountId(),
+                                                                        effectiveDate.toLocalDate(),
+                                                                        creditDescription,
+                                                                        // Note! The amount is negated here!
+                                                                        accountCBA.negate(),
+                                                                        childAccount.getCurrency());
+                invoiceForCredit.addInvoiceItem(creditItem);
+
+
+                // save invoices and invoice items
+                InvoiceModelDao childInvoice = new InvoiceModelDao(invoiceForExternalCharge);
+                invoiceSqlDao.create(childInvoice, childAccountContext);
+                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(externalChargeItem), childAccountContext);
+
+                InvoiceModelDao parentInvoice = new InvoiceModelDao(invoiceForCredit);
+                invoiceSqlDao.create(parentInvoice, parentAccountContext);
+                createInvoiceItemFromTransaction(transInvoiceItemSqlDao, new InvoiceItemModelDao(creditItem), parentAccountContext);
+
+                // add CBA complexity and notify bus on child invoice creation
+                cbaDao.addCBAComplexityFromTransaction(childInvoice.getId(), entitySqlDaoWrapperFactory, childAccountContext);
+                notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, childInvoice, childAccountContext);
+
+                cbaDao.addCBAComplexityFromTransaction(parentInvoice.getId(), entitySqlDaoWrapperFactory, parentAccountContext);
+                notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, parentInvoice, parentAccountContext);
+
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceItemModelDao>>() {
+            @Override
+            public List<InvoiceItemModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final InvoiceItemSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceItemSqlDao.class);
+                return transactional.getInvoiceItemsByParentInvoice(parentInvoiceId.toString(), context);
+            }
+        });
+    }
 }
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 3ee47be..ca7c49b 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -26,13 +26,14 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
 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.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.EntityDao;
 
@@ -72,7 +73,9 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
     // Include migrated invoices
     List<InvoiceModelDao> getAllInvoicesByAccount(InternalTenantContext context);
 
-    InvoicePaymentModelDao postChargeback(UUID paymentId, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
+    InvoicePaymentModelDao postChargeback(UUID paymentId, String chargebackTransactionExternalKey, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
+
+    InvoicePaymentModelDao postChargebackReversal(UUID paymentId, String chargebackTransactionExternalKey, InternalCallContext context) throws InvoiceApiException;
 
     InvoiceItemModelDao doCBAComplexity(InvoiceModelDao invoice, InternalCallContext context) throws InvoiceApiException;
 
@@ -141,4 +144,73 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
      * @param context   the callcontext
      */
     public void consumeExstingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context);
+
+    /**
+     * Update invoice status
+     *
+     * @param invoiceId the invoice id
+     * @param newState the new invoice state
+     * @param context the tenant context
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    void changeInvoiceStatus(UUID invoiceId, InvoiceStatus newState, InternalCallContext context) throws InvoiceApiException;
+
+    /**
+     * Save parent/child invoice relationship
+     *
+     * @param invoiceRelation the invoice relation object
+     * @param context the tenant context
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    void createParentChildInvoiceRelation(final InvoiceParentChildModelDao invoiceRelation, final InternalCallContext context) throws InvoiceApiException;
+
+    /**
+     * Get parent/child invoice relationship by parent invoice id
+     *
+     * @param parentInvoiceId the parent invoice id
+     * @param context the tenant context
+     * @return a list of parent-children relation
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    List<InvoiceParentChildModelDao> getChildInvoicesByParentInvoiceId(UUID parentInvoiceId, final InternalCallContext context) throws InvoiceApiException;
+
+
+    /**
+     * Retrieve parent invoice by the parent account id
+     *
+     * @param parentAccountId the parent account id
+     * @param context the tenant context
+     * @return a parent invoice in DRAFT status
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    InvoiceModelDao getParentDraftInvoice(UUID parentAccountId, InternalCallContext context) throws InvoiceApiException;
+
+    /**
+     * Update invoice item amount
+     *
+     * @param invoiceItemId the invoice item id
+     * @param amount the new amount value
+     * @param context the tenant context
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    void updateInvoiceItemAmount(UUID invoiceItemId, BigDecimal amount, InternalCallContext context) throws InvoiceApiException;
+
+    /**
+     * Move a given child credit to the parent level
+     *
+     * @param childAccount the child account
+     * @param childAccountContext the tenant context for the child account id
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    void transferChildCreditToParent(Account childAccount, InternalCallContext childAccountContext) throws InvoiceApiException;
+
+    /**
+     * Retrieve invoice items details associated to Parent SUMMARY invoice item
+     *
+     * @param parentInvoiceId the parent invoice id
+     * @param context the tenant context
+     * @return a list of invoice items associated with a parent invoice
+     * @throws InvoiceApiException if any unexpected error occurs
+     */
+    List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException;
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 6a8f386..139ac52 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -38,7 +38,9 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
@@ -56,10 +58,12 @@ public class InvoiceDaoHelper {
     private static final Logger log = LoggerFactory.getLogger(InvoiceDaoHelper.class);
 
     private final TagInternalApi tagInternalApi;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public InvoiceDaoHelper(final TagInternalApi tagInternalApi) {
+    public InvoiceDaoHelper(final TagInternalApi tagInternalApi, final InternalCallContextFactory internalCallContextFactory) {
         this.tagInternalApi = tagInternalApi;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     /**
@@ -81,10 +85,6 @@ public class InvoiceDaoHelper {
                                                         final InternalTenantContext context) throws InvoiceApiException {
         // Populate the missing amounts for individual items, if needed
         final Map<UUID, BigDecimal> outputItemIdsWithAmounts = new HashMap<UUID, BigDecimal>();
-        if (invoiceItemIdsWithNullAmounts.size() == 0) {
-            return outputItemIdsWithAmounts;
-        }
-
         // Retrieve invoice before the Refund
         final InvoiceModelDao invoice = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getById(invoiceId, context);
         if (invoice != null) {
@@ -176,9 +176,11 @@ public class InvoiceDaoHelper {
         final Collection<InvoiceModelDao> unpaidInvoices = Collections2.filter(invoices, new Predicate<InvoiceModelDao>() {
             @Override
             public boolean apply(final InvoiceModelDao in) {
-                final BigDecimal balance = InvoiceModelDaoHelper.getBalance(in);
+                final InvoiceModelDao invoice = (in.getParentInvoice() == null) ? in : in.getParentInvoice();
+                final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
                 log.debug("Computed balance={} for invoice={}", balance, in);
-                return (!in.isWrittenOff() && balance.compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
+                return InvoiceStatus.COMMITTED.equals(in.getStatus()) && (balance.compareTo(BigDecimal.ZERO) >= 1) &&
+                       (upToDate == null || in.getTargetDate() == null || !in.getTargetDate().isAfter(upToDate));
             }
         });
         return new ArrayList<InvoiceModelDao>(unpaidInvoices);
@@ -225,12 +227,14 @@ public class InvoiceDaoHelper {
         getInvoiceItemsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
         getInvoicePaymentsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
         setInvoiceWrittenOff(invoice, context);
+        getParentInvoice(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
     }
 
     public void populateChildren(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
         getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
         setInvoicesWrittenOff(invoices, context);
+        getParentInvoice(invoices, entitySqlDaoWrapperFactory, context);
     }
 
     public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
@@ -324,4 +328,19 @@ public class InvoiceDaoHelper {
         });
     }
 
+    private void getParentInvoice(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext internalTenantContext) {
+
+        final InvoiceSqlDao invoiceSqlDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+        for (InvoiceModelDao invoice : invoices) {
+            if (invoice.isParentInvoice()) continue;
+            final InvoiceModelDao parentInvoice = invoiceSqlDao.getParentInvoiceByChildInvoiceId(invoice.getId().toString(), internalTenantContext);
+            if (parentInvoice != null) {
+                final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(parentInvoice.getAccountId(), ObjectType.ACCOUNT, internalCallContextFactory.createTenantContext(internalTenantContext));
+                final InternalTenantContext parentContext = internalCallContextFactory.createInternalTenantContext(internalTenantContext.getTenantRecordId(), parentAccountRecordId);
+                populateChildren(parentInvoice, entitySqlDaoWrapperFactory, parentContext);
+                invoice.addParentInvoice(parentInvoice);
+            }
+        }
+    }
+
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
index 9254e11..8805d52 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemModelDao.java
@@ -34,6 +34,7 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
     private InvoiceItemType type;
     private UUID invoiceId;
     private UUID accountId;
+    private UUID childAccountId;
     private UUID bundleId;
     private UUID subscriptionId;
     private String description;
@@ -49,14 +50,15 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
 
     public InvoiceItemModelDao() { /* For the DAO mapper */ }
 
-    public InvoiceItemModelDao(final UUID id, final DateTime createdDate, final InvoiceItemType type, final UUID invoiceId,
-                               final UUID accountId, final UUID bundleId, final UUID subscriptionId, final String description, final String planName,
+    public InvoiceItemModelDao(final UUID id, final DateTime createdDate, final InvoiceItemType type, final UUID invoiceId, final UUID accountId,
+                               final UUID childAccountId, final UUID bundleId, final UUID subscriptionId, final String description, final String planName,
                                final String phaseName, final String usageName, final LocalDate startDate, final LocalDate endDate, final BigDecimal amount,
                                final BigDecimal rate, final Currency currency, final UUID linkedItemId) {
         super(id, createdDate, createdDate);
         this.type = type;
         this.invoiceId = invoiceId;
         this.accountId = accountId;
+        this.childAccountId = childAccountId;
         this.bundleId = bundleId;
         this.subscriptionId = subscriptionId;
         this.description = description;
@@ -75,16 +77,24 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
                                final UUID bundleId, final UUID subscriptionId, final String description, final String planName,
                                final String phaseName, final String usageName, final LocalDate startDate, final LocalDate endDate, final BigDecimal amount,
                                final BigDecimal rate, final Currency currency, final UUID linkedItemId) {
-        this(UUIDs.randomUUID(), createdDate, type, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName,
+        this(UUIDs.randomUUID(), createdDate, type, invoiceId, accountId, null, bundleId, subscriptionId, description, planName, phaseName, usageName,
              startDate, endDate, amount, rate, currency, linkedItemId);
     }
 
     public InvoiceItemModelDao(final InvoiceItem invoiceItem) {
-        this(invoiceItem.getId(), invoiceItem.getCreatedDate(), invoiceItem.getInvoiceItemType(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), invoiceItem.getBundleId(),
+        this(invoiceItem.getId(), invoiceItem.getCreatedDate(), invoiceItem.getInvoiceItemType(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), invoiceItem.getChildAccountId(), invoiceItem.getBundleId(),
              invoiceItem.getSubscriptionId(), invoiceItem.getDescription(), invoiceItem.getPlanName(), invoiceItem.getPhaseName(), invoiceItem.getUsageName(), invoiceItem.getStartDate(), invoiceItem.getEndDate(),
              invoiceItem.getAmount(), invoiceItem.getRate(), invoiceItem.getCurrency(), invoiceItem.getLinkedItemId());
     }
 
+    /*
+    public InvoiceItemModelDao(final InvoiceItem invoiceItem, final UUID invoiceId) {
+        this(invoiceItem.getId(), invoiceItem.getCreatedDate(), invoiceItem.getInvoiceItemType(), invoiceId, invoiceItem.getAccountId(), invoiceItem.getBundleId(),
+             invoiceItem.getSubscriptionId(), invoiceItem.getDescription(), invoiceItem.getPlanName(), invoiceItem.getPhaseName(), invoiceItem.getUsageName(),
+             invoiceItem.getStartDate(), invoiceItem.getEndDate(),
+             invoiceItem.getAmount(), invoiceItem.getRate(), invoiceItem.getCurrency(), invoiceItem.getLinkedItemId());
+    }
+*/
     public InvoiceItemType getType() {
         return type;
     }
@@ -97,6 +107,10 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
         return accountId;
     }
 
+    public UUID getChildAccountId() {
+        return childAccountId;
+    }
+
     public UUID getBundleId() {
         return bundleId;
     }
@@ -157,6 +171,10 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
         this.accountId = accountId;
     }
 
+    public void setChildAccountId(final UUID childAccountId) {
+        this.childAccountId = childAccountId;
+    }
+
     public void setBundleId(final UUID bundleId) {
         this.bundleId = bundleId;
     }
@@ -207,6 +225,7 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
         sb.append("type=").append(type);
         sb.append(", invoiceId=").append(invoiceId);
         sb.append(", accountId=").append(accountId);
+        sb.append(", childAccountId=").append(childAccountId);
         sb.append(", bundleId=").append(bundleId);
         sb.append(", subscriptionId=").append(subscriptionId);
         sb.append(", description='").append(description).append('\'');
@@ -240,6 +259,9 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
         if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
+        if (childAccountId != null ? !childAccountId.equals(that.childAccountId) : that.childAccountId != null) {
+            return false;
+        }
         if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
@@ -292,6 +314,7 @@ public class InvoiceItemModelDao extends EntityModelDaoBase implements EntityMod
         result = 31 * result + (type != null ? type.hashCode() : 0);
         result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (childAccountId != null ? childAccountId.hashCode() : 0);
         result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
         result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (description != null ? description.hashCode() : 0);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
index 4ec1a9f..e30b5d2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -16,8 +16,12 @@
 
 package org.killbill.billing.invoice.dao;
 
+import java.math.BigDecimal;
 import java.util.List;
 
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.entity.dao.Audited;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -26,6 +30,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @EntitySqlDaoStringTemplate
 public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, InvoiceItem> {
@@ -42,4 +47,14 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
     @SqlQuery
     List<InvoiceItemModelDao> getAdjustedOrRepairedInvoiceItemsByLinkedId(@Bind("linkedItemId") final String linkedItemId,
                                                             @BindBean final InternalTenantContext context);
+
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    void updateAmount(@Bind("id") String invoiceItemId,
+                      @Bind("amount")BigDecimal amount,
+                      @BindBean final InternalCallContext context);
+
+    @SqlQuery
+    List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(@Bind("parentInvoiceId") final String parentInvoiceId,
+                                                             @BindBean final InternalTenantContext context);
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index bf3b32e..589d1c7 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -26,9 +26,9 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -42,11 +42,14 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
     private LocalDate targetDate;
     private Currency currency;
     private boolean migrated;
+    private InvoiceStatus status;
+    private boolean isParentInvoice;
 
     // Not in the database, for convenience only
     private List<InvoiceItemModelDao> invoiceItems = new LinkedList<InvoiceItemModelDao>();
     private List<InvoicePaymentModelDao> invoicePayments = new LinkedList<InvoicePaymentModelDao>();
     private Currency processedCurrency;
+    private InvoiceModelDao parentInvoice;
 
     private boolean isWrittenOff;
 
@@ -54,7 +57,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
 
     public InvoiceModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
                            @Nullable final Integer invoiceNumber, final LocalDate invoiceDate, final LocalDate targetDate,
-                           final Currency currency, final boolean migrated) {
+                           final Currency currency, final boolean migrated, final InvoiceStatus status, final boolean isParentInvoice) {
         super(id, createdDate, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
@@ -63,19 +66,29 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         this.currency = currency;
         this.migrated = migrated;
         this.isWrittenOff = false;
+        this.status = status;
+        this.isParentInvoice = isParentInvoice;
     }
 
     public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
-        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated);
+        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated, InvoiceStatus.COMMITTED, false);
+    }
+
+    public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated, final InvoiceStatus status) {
+        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated, status, false);
     }
 
     public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false);
+        this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED, false);
+    }
+
+    public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final Currency currency, final InvoiceStatus status, final boolean isParentInvoice) {
+        this(UUIDs.randomUUID(), invoiceDate.toDateTimeAtCurrentTime(), accountId, null, invoiceDate, null, currency, false, status, isParentInvoice);
     }
 
     public InvoiceModelDao(final Invoice invoice) {
         this(invoice.getId(), invoice.getCreatedDate(), invoice.getAccountId(), invoice.getInvoiceNumber(), invoice.getInvoiceDate(),
-             invoice.getTargetDate(), invoice.getCurrency(), invoice.isMigrationInvoice());
+             invoice.getTargetDate(), invoice.getCurrency(), invoice.isMigrationInvoice(), invoice.getStatus(), invoice.isParentInvoice());
     }
 
     public void addInvoiceItems(final List<InvoiceItemModelDao> invoiceItems) {
@@ -130,6 +143,14 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         return migrated;
     }
 
+    public InvoiceStatus getStatus() {
+        return status;
+    }
+
+    public boolean isParentInvoice() {
+        return isParentInvoice;
+    }
+
     public void setAccountId(final UUID accountId) {
         this.accountId = accountId;
     }
@@ -170,6 +191,22 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         this.isWrittenOff = isWrittenOff;
     }
 
+    public void setStatus(final InvoiceStatus status) {
+        this.status = status;
+    }
+
+    public void setParentInvoice(final boolean isParentInvoice) {
+        this.isParentInvoice = isParentInvoice;
+    }
+
+    public void addParentInvoice(InvoiceModelDao parentInvoice) {
+        this.parentInvoice = parentInvoice;
+    }
+
+    public InvoiceModelDao getParentInvoice() {
+        return parentInvoice;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("InvoiceModelDao{");
@@ -179,10 +216,13 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         sb.append(", targetDate=").append(targetDate);
         sb.append(", currency=").append(currency);
         sb.append(", migrated=").append(migrated);
+        sb.append(", status=").append(status);
         sb.append(", invoiceItems=").append(invoiceItems);
         sb.append(", invoicePayments=").append(invoicePayments);
         sb.append(", processedCurrency=").append(processedCurrency);
         sb.append(", isWrittenOff=").append(isWrittenOff);
+        sb.append(", isParentInvoice=").append(isParentInvoice);
+        sb.append(", parentInvoice=").append(parentInvoice);
         sb.append('}');
         return sb.toString();
     }
@@ -204,23 +244,40 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         if (migrated != that.migrated) {
             return false;
         }
+        if (isWrittenOff != that.isWrittenOff) {
+            return false;
+        }
         if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
+        if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
+            return false;
+        }
+        if (invoiceDate != null ? invoiceDate.compareTo(that.invoiceDate) != 0 : that.invoiceDate != null) {
+            return false;
+        }
+        if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
+            return false;
+        }
         if (currency != that.currency) {
             return false;
         }
-        if (invoiceDate != null ? !invoiceDate.equals(that.invoiceDate) : that.invoiceDate != null) {
+        if (status != that.status) {
             return false;
         }
-        if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
+        if (invoiceItems != null ? !invoiceItems.equals(that.invoiceItems) : that.invoiceItems != null) {
             return false;
         }
-        if (targetDate != null ? !targetDate.equals(that.targetDate) : that.targetDate != null) {
+        if (invoicePayments != null ? !invoicePayments.equals(that.invoicePayments) : that.invoicePayments != null) {
             return false;
         }
-
-        return true;
+        if (isParentInvoice != that.isParentInvoice) {
+            return false;
+        }
+        if (parentInvoice != null ? !parentInvoice.equals(that.parentInvoice) : that.parentInvoice != null) {
+            return false;
+        }
+        return processedCurrency == that.processedCurrency;
     }
 
     @Override
@@ -232,6 +289,13 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         result = 31 * result + (targetDate != null ? targetDate.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (migrated ? 1 : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        result = 31 * result + (invoiceItems != null ? invoiceItems.hashCode() : 0);
+        result = 31 * result + (invoicePayments != null ? invoicePayments.hashCode() : 0);
+        result = 31 * result + (processedCurrency != null ? processedCurrency.hashCode() : 0);
+        result = 31 * result + (isWrittenOff ? 1 : 0);
+        result = 31 * result + (isParentInvoice ? 1 : 0);
+        result = 31 * result + (parentInvoice != null ? parentInvoice.hashCode() : 0);
         return result;
     }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
index ebb19d2..a240128 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDaoHelper.java
@@ -47,7 +47,8 @@ public class InvoiceModelDaoHelper {
                                                                 public InvoicePayment apply(final InvoicePaymentModelDao input) {
                                                                     return new DefaultInvoicePayment(input);
                                                                 }
-                                                            }));
+                                                            }),
+                                                            invoiceModelDao.isMigrated() || invoiceModelDao.isWrittenOff());
     }
 
     public static BigDecimal getCBAAmount(final InvoiceModelDao invoiceModelDao) {
@@ -59,4 +60,14 @@ public class InvoiceModelDaoHelper {
                                                                        }
                                                                    }));
     }
+
+    public static BigDecimal getAmountCharged(final InvoiceModelDao invoiceModelDao) {
+        return InvoiceCalculatorUtils.computeInvoiceAmountCharged(invoiceModelDao.getCurrency(),
+                                                            Iterables.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
+                                                                @Override
+                                                                public InvoiceItem apply(final InvoiceItemModelDao input) {
+                                                                    return InvoiceItemFactory.fromModelDao(input);
+                                                                }
+                                                            }));
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceParentChildModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceParentChildModelDao.java
new file mode 100644
index 0000000..35d28ca
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceParentChildModelDao.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.invoice.dao;
+
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.invoice.api.InvoiceParentChild;
+import org.killbill.billing.util.dao.TableName;
+import org.killbill.billing.util.entity.dao.EntityModelDao;
+import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
+
+public class InvoiceParentChildModelDao extends EntityModelDaoBase implements EntityModelDao<InvoiceParentChild> {
+
+    private UUID parentInvoiceId;
+    private UUID childInvoiceId;
+    private UUID childAccountId;
+
+    public InvoiceParentChildModelDao() { /* For the DAO mapper */ };
+
+    public InvoiceParentChildModelDao(final UUID parentInvoiceId, final UUID childInvoiceId, final UUID childAccountId) {
+        this(UUID.randomUUID(), DateTime.now(), parentInvoiceId, childInvoiceId, childAccountId);
+    }
+
+    public InvoiceParentChildModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID parentInvoiceId, final UUID childInvoiceId, final UUID childAccountId) {
+        super(id, createdDate, createdDate);
+        this.parentInvoiceId = parentInvoiceId;
+        this.childInvoiceId = childInvoiceId;
+        this.childAccountId = childAccountId;
+    }
+
+    public InvoiceParentChildModelDao(final InvoiceParentChild invoiceParentChild) {
+        this(invoiceParentChild.getParentInvoiceId(), invoiceParentChild.getChildInvoiceId(), invoiceParentChild.getChildAccountId());
+    }
+
+    public UUID getParentInvoiceId() {
+        return parentInvoiceId;
+    }
+
+    public void setParentInvoiceId(final UUID parentInvoiceId) {
+        this.parentInvoiceId = parentInvoiceId;
+    }
+
+    public UUID getChildInvoiceId() {
+        return childInvoiceId;
+    }
+
+    public void setChildInvoiceId(final UUID childInvoiceId) {
+        this.childInvoiceId = childInvoiceId;
+    }
+
+    public UUID getChildAccountId() {
+        return childAccountId;
+    }
+
+    public void setChildAccountId(final UUID childAccountId) {
+        this.childAccountId = childAccountId;
+    }
+
+    @Override
+    public String toString() {
+        return "InvoiceParentChildModelDao{" +
+               "parentInvoiceId=" + parentInvoiceId +
+               ", childInvoiceId=" + childInvoiceId +
+               ", childAccountId=" + childAccountId +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final InvoiceParentChildModelDao that = (InvoiceParentChildModelDao) o;
+
+        if (parentInvoiceId != null ? !parentInvoiceId.equals(that.parentInvoiceId) : that.parentInvoiceId != null) {
+            return false;
+        }
+        if (childInvoiceId != null ? !childInvoiceId.equals(that.childInvoiceId) : that.childInvoiceId != null) {
+            return false;
+        }
+        return childAccountId != null ? childAccountId.equals(that.childAccountId) : that.childAccountId == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (parentInvoiceId != null ? parentInvoiceId.hashCode() : 0);
+        result = 31 * result + (childInvoiceId != null ? childInvoiceId.hashCode() : 0);
+        result = 31 * result + (childAccountId != null ? childAccountId.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public TableName getTableName() {
+        return TableName.INVOICE_PARENT_CHILDREN;
+    }
+
+    @Override
+    public TableName getHistoryTableName() {
+        return null;
+    }
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
index 9d5b6a0..d763bd1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -50,8 +50,8 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDa
                                                     @BindBean final InternalTenantContext context);
 
     @SqlQuery
-    InvoicePaymentModelDao getPaymentsForCookieId(@Bind("paymentCookieId") final String paymentCookieId,
-                                                  @BindBean final InternalTenantContext context);
+    InvoicePaymentModelDao getPaymentForCookieId(@Bind("paymentCookieId") final String paymentCookieId,
+                                                 @BindBean final InternalTenantContext context);
 
     @SqlQuery
     BigDecimal getRemainingAmountPaid(@Bind("invoicePaymentId") final String invoicePaymentId,
@@ -69,7 +69,6 @@ public interface InvoicePaymentSqlDao extends EntitySqlDao<InvoicePaymentModelDa
     List<InvoicePaymentModelDao> getChargebacksByPaymentId(@Bind("paymentId") final String paymentId,
                                                            @BindBean final InternalTenantContext context);
 
-
     @SqlUpdate
     void updateAttempt(@Bind("recordId") Long recordId,
                        @Bind("paymentId") final String paymentId,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
index 40551e9..f4a4b98 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
@@ -19,14 +19,17 @@ package org.killbill.billing.invoice.dao;
 import java.util.List;
 import java.util.UUID;
 
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @EntitySqlDaoStringTemplate
 public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
@@ -38,5 +41,19 @@ public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
     @SqlQuery
     UUID getInvoiceIdByPaymentId(@Bind("paymentId") final String paymentId,
                                  @BindBean final InternalTenantContext context);
+
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    void updateStatus(@Bind("id") String invoiceId,
+                             @Bind("status") String status,
+                             @BindBean final InternalCallContext context);
+
+    @SqlQuery
+    InvoiceModelDao getParentDraftInvoice(@Bind("accountId") final String parentAccountId,
+                               @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    InvoiceModelDao getParentInvoiceByChildInvoiceId(@Bind("childInvoiceId") final String childInvoiceId,
+                                                     @BindBean final InternalTenantContext context);
 }
 
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
index 76843fd..8a75ef0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/BillingIntervalDetail.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -30,12 +32,14 @@ public class BillingIntervalDetail {
     private final int billingCycleDay;
     private final BillingPeriod billingPeriod;
     private final BillingMode billingMode;
+    private final boolean isMonthBased;
     // First date after the startDate aligned with the BCD
     private LocalDate firstBillingCycleDate;
     // Date up to which we should bill
     private LocalDate effectiveEndDate;
     private LocalDate lastBillingCycleDate;
 
+
     public BillingIntervalDetail(final LocalDate startDate,
                                  final LocalDate endDate,
                                  final LocalDate targetDate,
@@ -45,9 +49,14 @@ public class BillingIntervalDetail {
         this.startDate = startDate;
         this.endDate = endDate;
         this.targetDate = targetDate;
-        this.billingCycleDay = billingCycleDay;
+        if (billingPeriod.getPeriod().getMonths() != 0 || billingPeriod.getPeriod().getYears() != 0) {
+            this.billingCycleDay = billingCycleDay;
+        } else {
+            this.billingCycleDay = startDate.getDayOfMonth();
+        }
         this.billingPeriod = billingPeriod;
         this.billingMode = billingMode;
+        this.isMonthBased = (billingPeriod.getPeriod().getMonths() | billingPeriod.getPeriod().getYears()) > 0;
         computeAll();
     }
 
@@ -59,10 +68,9 @@ public class BillingIntervalDetail {
         return effectiveEndDate;
     }
 
-    public LocalDate getFutureBillingDateFor(int nbPeriod) {
-        final int numberOfMonthsPerBillingPeriod = billingPeriod.getNumberOfMonths();
-        LocalDate proposedDate = firstBillingCycleDate.plusMonths((nbPeriod) * numberOfMonthsPerBillingPeriod);
-        return alignProposedBillCycleDate(proposedDate, billingCycleDay);
+    public LocalDate getFutureBillingDateFor(final int nbPeriod) {
+        final LocalDate proposedDate = InvoiceDateUtils.advanceByNPeriods(firstBillingCycleDate, billingPeriod, nbPeriod);
+        return alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
     }
 
     public LocalDate getLastBillingCycleDate() {
@@ -70,9 +78,8 @@ public class BillingIntervalDetail {
     }
 
     public LocalDate getNextBillingCycleDate() {
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
-        final LocalDate proposedDate = lastBillingCycleDate != null ? lastBillingCycleDate.plusMonths(numberOfMonthsInPeriod) : firstBillingCycleDate;
-        final LocalDate nextBillingCycleDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
+        final LocalDate proposedDate = lastBillingCycleDate != null ? lastBillingCycleDate.plus(billingPeriod.getPeriod()) : firstBillingCycleDate;
+        final LocalDate nextBillingCycleDate = alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
         return nextBillingCycleDate;
     }
 
@@ -88,8 +95,7 @@ public class BillingIntervalDetail {
         calculateLastBillingCycleDate();
     }
 
-    @VisibleForTesting
-    void calculateFirstBillingCycleDate() {
+    private void calculateFirstBillingCycleDate() {
 
         final int lastDayOfMonth = startDate.dayOfMonth().getMaximumValue();
         final LocalDate billingCycleDate;
@@ -99,12 +105,11 @@ public class BillingIntervalDetail {
             billingCycleDate = new LocalDate(startDate.getYear(), startDate.getMonthOfYear(), billingCycleDay, startDate.getChronology());
         }
 
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
         LocalDate proposedDate = billingCycleDate;
         while (proposedDate.isBefore(startDate)) {
-            proposedDate = proposedDate.plusMonths(numberOfMonthsInPeriod);
+            proposedDate = proposedDate.plus(billingPeriod.getPeriod());
         }
-        firstBillingCycleDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
+        firstBillingCycleDate = alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
     }
 
     private void calculateEffectiveEndDate() {
@@ -127,18 +132,16 @@ public class BillingIntervalDetail {
             return;
         }
 
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
         int numberOfPeriods = 0;
         LocalDate proposedDate = firstBillingCycleDate;
-        LocalDate nextProposedDate = proposedDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
-
+        LocalDate nextProposedDate = InvoiceDateUtils.advanceByNPeriods(firstBillingCycleDate, billingPeriod, numberOfPeriods);
 
         while (!nextProposedDate.isAfter(targetDate)) {
             proposedDate = nextProposedDate;
-            nextProposedDate = firstBillingCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
+            nextProposedDate = InvoiceDateUtils.advanceByNPeriods(firstBillingCycleDate, billingPeriod, numberOfPeriods);
             numberOfPeriods += 1;
         }
-        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
+        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
 
         // We honor the endDate as long as it does not go beyond our targetDate (by construction this cannot be after the nextProposedDate neither.
         if (endDate != null && !endDate.isAfter(targetDate)) {
@@ -161,15 +164,14 @@ public class BillingIntervalDetail {
             return;
         }
 
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
         int numberOfPeriods = 0;
         LocalDate proposedDate = firstBillingCycleDate;
 
         while (!proposedDate.isAfter(targetDate)) {
-            proposedDate = firstBillingCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
+            proposedDate = InvoiceDateUtils.advanceByNPeriods(firstBillingCycleDate, billingPeriod, numberOfPeriods);
             numberOfPeriods += 1;
         }
-        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
+        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
 
         // The proposedDate is greater to our endDate => return it
         if (endDate != null && endDate.isBefore(proposedDate)) {
@@ -191,13 +193,13 @@ public class BillingIntervalDetail {
         LocalDate proposedDate = firstBillingCycleDate;
         int numberOfPeriods = 0;
         while (!proposedDate.isAfter(effectiveEndDate)) {
-            proposedDate = firstBillingCycleDate.plusMonths(numberOfPeriods * billingPeriod.getNumberOfMonths());
+            proposedDate = InvoiceDateUtils.advanceByNPeriods(firstBillingCycleDate, billingPeriod, numberOfPeriods);
             numberOfPeriods += 1;
         }
 
         // Our proposed date is billingCycleDate prior to the effectiveEndDate
-        proposedDate = proposedDate.plusMonths(-billingPeriod.getNumberOfMonths());
-        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay);
+        proposedDate = proposedDate.minus(billingPeriod.getPeriod());
+        proposedDate = alignProposedBillCycleDate(proposedDate, billingCycleDay, isMonthBased);
 
         if (proposedDate.isBefore(firstBillingCycleDate)) {
             // Make sure not to go too far in the past
@@ -210,7 +212,11 @@ public class BillingIntervalDetail {
     //
     // We start from a billCycleDate
     //
-    public static LocalDate alignProposedBillCycleDate(final LocalDate proposedDate, final int billingCycleDay) {
+    private static LocalDate alignProposedBillCycleDate(final LocalDate proposedDate, final int billingCycleDay, final boolean isMonthBased) {
+        // billingCycleDay alignment only makes sense for month based BillingPeriod (MONTHLY, QUARTERLY, BIANNUAL, ANNUAL)
+        if (!isMonthBased) {
+            return proposedDate;
+        }
         final int lastDayOfMonth = proposedDate.dayOfMonth().getMaximumValue();
         int proposedBillCycleDate = proposedDate.getDayOfMonth();
         if (proposedBillCycleDate < billingCycleDay && billingCycleDay <= lastDayOfMonth) {
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 5da49d2..f35087b 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -30,6 +30,7 @@ import org.joda.time.Months;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -37,9 +38,10 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
 import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.junction.BillingEventSet;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.clock.Clock;
 
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 
@@ -71,10 +73,10 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
             return new InvoiceWithMetadata(null, ImmutableMap.<UUID, SubscriptionFutureNotificationDates>of());
         }
 
-        validateTargetDate(targetDate);
+        validateTargetDate(targetDate, context);
         final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
 
-        final LocalDate invoiceDate = events.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(clock.getUTCNow());
+        final LocalDate invoiceDate = context.toLocalDate(clock.getUTCNow());
         final Invoice invoice = new DefaultInvoice(account.getId(), invoiceDate, adjustedTargetDate, targetCurrency);
         final UUID invoiceId = invoice.getId();
         final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
@@ -89,8 +91,8 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         return new InvoiceWithMetadata(invoice.getInvoiceItems().isEmpty() ? null : invoice, perSubscriptionFutureNotificationDates);
     }
 
-    private void validateTargetDate(final LocalDate targetDate) throws InvoiceApiException {
-        final int maximumNumberOfMonths = config.getNumberOfMonthsInFuture();
+    private void validateTargetDate(final LocalDate targetDate, final InternalTenantContext context) throws InvoiceApiException {
+        final int maximumNumberOfMonths = config.getNumberOfMonthsInFuture(context);
 
         if (Months.monthsBetween(clock.getUTCToday(), targetDate).getMonths() > maximumNumberOfMonths) {
             throw new InvoiceApiException(ErrorCode.INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE, targetDate.toString());
@@ -105,7 +107,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
         LocalDate maxDate = targetDate;
 
         for (final Invoice invoice : existingInvoices) {
-            if (invoice.getTargetDate().isAfter(maxDate)) {
+            if ((invoice.getTargetDate() != null) && invoice.getTargetDate().isAfter(maxDate)) {
                 maxDate = invoice.getTargetDate();
             }
         }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index 9650e8c..55106c2 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -32,8 +32,8 @@ import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.Duration;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
@@ -47,13 +47,11 @@ import org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillin
 import org.killbill.billing.invoice.tree.AccountItemTree;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
 import org.killbill.billing.util.currency.KillBillMoney;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.inject.Inject;
 
 import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateNumberOfWholeBillingPeriods;
 import static org.killbill.billing.invoice.generator.InvoiceDateUtils.calculateProRationAfterLastBillingCycleDate;
@@ -63,13 +61,9 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
 
     private static final Logger log = LoggerFactory.getLogger(FixedAndRecurringInvoiceItemGenerator.class);
 
-    @Inject
-    public FixedAndRecurringInvoiceItemGenerator() {
-    }
-
     public List<InvoiceItem> generateItems(final ImmutableAccountData account, final UUID invoiceId, final BillingEventSet eventSet,
                                            @Nullable final List<Invoice> existingInvoices, final LocalDate targetDate,
-                                           final Currency targetCurrency, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+                                           final Currency targetCurrency, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
                                            final InternalCallContext internalCallContext) throws InvoiceApiException {
         final AccountItemTree accountItemTree = new AccountItemTree(account.getId(), invoiceId);
         if (existingInvoices != null) {
@@ -86,9 +80,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
 
         // Generate list of proposed invoice items based on billing events from junction-- proposed items are ALL items since beginning of time
         final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
-        processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, existingInvoices);
-        processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems);
-
+        processRecurringBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, perSubscriptionFutureNotificationDate, existingInvoices, internalCallContext);
+        processFixedBillingEvents(invoiceId, account.getId(), eventSet, targetDate, targetCurrency, proposedItems, internalCallContext);
         accountItemTree.mergeWithProposedItems(proposedItems);
         return accountItemTree.getResultingItemList();
     }
@@ -96,18 +89,14 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
     private void processRecurringBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events,
                                                final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems,
                                                final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
-                                               @Nullable final List<Invoice> existingInvoices) throws InvoiceApiException {
-
-        if (events.size() == 0) {
+                                               @Nullable final List<Invoice> existingInvoices,
+                                               final InternalCallContext internalCallContext) throws InvoiceApiException {
+        if (events.isEmpty()) {
             return;
         }
 
         // Pretty-print the generated invoice items from the junction events
-        final StringBuilder logStringBuilder = new StringBuilder("Proposed Invoice items for invoiceId='")
-                .append(invoiceId)
-                .append("', accountId='")
-                .append(accountId)
-                .append("'");
+        final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "recurring", log);
 
         final Iterator<BillingEvent> eventIt = events.iterator();
         BillingEvent nextEvent = eventIt.next();
@@ -117,31 +106,35 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             if (!events.getSubscriptionIdsWithAutoInvoiceOff().
                     contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
                 final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
-                final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, events.getAccountDateAndTimeZoneContext());
+                final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
                 proposedItems.addAll(newProposedItems);
             }
         }
-        final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, events.getAccountDateAndTimeZoneContext());
-        proposedItems.addAll(newProposedItems);
+        final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, invoiceItemGeneratorLogger, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, internalCallContext);
 
-        log.info(logStringBuilder.toString());
+        proposedItems.addAll(newProposedItems);
 
-        return;
+        invoiceItemGeneratorLogger.logItems();
     }
 
     @VisibleForTesting
-    void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate, final Currency currency, final List<InvoiceItem> proposedItems) {
-
-        final AccountDateAndTimeZoneContext dateAndTimeZoneContext = events.getAccountDateAndTimeZoneContext();
+    void processFixedBillingEvents(final UUID invoiceId, final UUID accountId, final BillingEventSet events, final LocalDate targetDate,
+                                   final Currency currency, final List<InvoiceItem> proposedItems, final InternalCallContext internalCallContext) throws InvoiceApiException {
+        if (events.isEmpty()) {
+            return;
+        }
 
         InvoiceItem prevItem = null;
 
+        // Pretty-print the generated invoice items from the junction events
+        final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, accountId, "fixed", log);
+
         final Iterator<BillingEvent> eventIt = events.iterator();
         while (eventIt.hasNext()) {
             final BillingEvent thisEvent = eventIt.next();
 
-            final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, dateAndTimeZoneContext);
-            if (!isSameDayAndSameSubscription(prevItem, thisEvent, dateAndTimeZoneContext) && prevItem != null) {
+            final InvoiceItem currentFixedPriceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, invoiceItemGeneratorLogger, internalCallContext);
+            if (!isSameDayAndSameSubscription(prevItem, thisEvent, internalCallContext) && prevItem != null) {
                 proposedItems.add(prevItem);
             }
             prevItem = currentFixedPriceItem;
@@ -150,11 +143,13 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
         if (prevItem != null) {
             proposedItems.add(prevItem);
         }
+
+        invoiceItemGeneratorLogger.logItems();
     }
 
     @VisibleForTesting
-    boolean isSameDayAndSameSubscription(final InvoiceItem prevComputedFixedItem, final BillingEvent currentBillingEvent, final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
-        final LocalDate curLocalEffectiveDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(currentBillingEvent.getEffectiveDate());
+    boolean isSameDayAndSameSubscription(final InvoiceItem prevComputedFixedItem, final BillingEvent currentBillingEvent, final InternalCallContext internalCallContext) {
+        final LocalDate curLocalEffectiveDate = internalCallContext.toLocalDate(currentBillingEvent.getEffectiveDate());
         if (prevComputedFixedItem != null && /* If we have computed a previous item */
             prevComputedFixedItem.getStartDate().compareTo(curLocalEffectiveDate) == 0 && /* The current billing event happens at the same date */
             prevComputedFixedItem.getSubscriptionId().compareTo(currentBillingEvent.getSubscription().getId()) == 0 /* The current billing event happens for the same subscription */) {
@@ -167,78 +162,71 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
     // Turn a set of events into a list of invoice items. Note that the dates on the invoice items will be rounded (granularity of a day)
     private List<InvoiceItem> processRecurringEvent(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent, @Nullable final BillingEvent nextEvent,
                                                     final LocalDate targetDate, final Currency currency,
-                                                    final StringBuilder logStringBuilder, final BillingMode billingMode,
+                                                    final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger, final BillingMode billingMode,
                                                     final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
-                                                    final AccountDateAndTimeZoneContext dateAndTimeZoneContext) throws InvoiceApiException {
-        final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
+                                                    final InternalCallContext internalCallContext) throws InvoiceApiException {
 
-        // For FIXEDTERM phases we need to stop when the specified duration has been reached
-        final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ?
-                                     computeMaxEndDateForFixedTermPlanPhase(thisEvent, dateAndTimeZoneContext) :
-                                     null;
+        try {
+            final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
 
-        // Handle recurring items
-        final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
-        if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
-            final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
+            // For FIXEDTERM phases we need to stop when the specified duration has been reached
+            final LocalDate maxEndDate = thisEvent.getPlanPhase().getPhaseType() == PhaseType.FIXEDTERM ?
+                                         thisEvent.getPlanPhase().getDuration().addToLocalDate(internalCallContext.toLocalDate(thisEvent.getEffectiveDate())) :
+                                         null;
 
-            if (!startDate.isAfter(targetDate)) {
-                final LocalDate endDate = (nextEvent == null) ? null : dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+            // Handle recurring items
+            final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
+            if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
+                final LocalDate startDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
 
-                final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();
+                if (!startDate.isAfter(targetDate)) {
+                    final LocalDate endDate = (nextEvent == null) ? null : internalCallContext.toLocalDate(nextEvent.getEffectiveDate());
 
-                final RecurringInvoiceItemDataWithNextBillingCycleDate itemDataWithNextBillingCycleDate;
-                try {
-                    itemDataWithNextBillingCycleDate = generateInvoiceItemData(startDate, endDate, targetDate, billCycleDayLocal, billingPeriod, billingMode);
-                } catch (InvalidDateSequenceException e) {
-                    throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate, targetDate);
-                }
-
-                for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
+                    final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();
 
-                    // Stop if there a maxEndDate and we have reached it
-                    if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
-                        break;
+                    final RecurringInvoiceItemDataWithNextBillingCycleDate itemDataWithNextBillingCycleDate;
+                    try {
+                        itemDataWithNextBillingCycleDate = generateInvoiceItemData(startDate, endDate, targetDate, billCycleDayLocal, billingPeriod, billingMode);
+                    } catch (final InvalidDateSequenceException e) {
+                        throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_DATE_SEQUENCE, startDate, endDate, targetDate);
                     }
-                    final BigDecimal rate = thisEvent.getRecurringPrice();
-                    if (rate != null) {
-                        final BigDecimal amount = itemDatum.getNumberOfCycles().multiply(rate);
-                        final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
-                                                                                            accountId,
-                                                                                            thisEvent.getSubscription().getBundleId(),
-                                                                                            thisEvent.getSubscription().getId(),
-                                                                                            thisEvent.getPlan().getName(),
-                                                                                            thisEvent.getPlanPhase().getName(),
-                                                                                            itemDatum.getStartDate(),
-                                                                                            itemDatum.getEndDate(),
-                                                                                            amount, rate, currency);
-                        items.add(recurringItem);
+                    for (final RecurringInvoiceItemData itemDatum : itemDataWithNextBillingCycleDate.getItemData()) {
+
+                        // Stop if there a maxEndDate and we have reached it
+                        if (maxEndDate != null && maxEndDate.compareTo(itemDatum.getEndDate()) < 0) {
+                            break;
+                        }
+                        final BigDecimal rate = thisEvent.getRecurringPrice(internalCallContext.toUTCDateTime(itemDatum.getStartDate()));
+                        if (rate != null) {
+                            final BigDecimal amount = KillBillMoney.of(itemDatum.getNumberOfCycles().multiply(rate), currency);
+                            final RecurringInvoiceItem recurringItem = new RecurringInvoiceItem(invoiceId,
+                                                                                                accountId,
+                                                                                                thisEvent.getSubscription().getBundleId(),
+                                                                                                thisEvent.getSubscription().getId(),
+                                                                                                thisEvent.getPlan().getName(),
+                                                                                                thisEvent.getPlanPhase().getName(),
+                                                                                                itemDatum.getStartDate(),
+                                                                                                itemDatum.getEndDate(),
+                                                                                                amount, rate, currency);
+                            items.add(recurringItem);
+                        }
                     }
+                    updatePerSubscriptionNextNotificationDate(thisEvent.getSubscription().getId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, billingMode,
+                                                              perSubscriptionFutureNotificationDate);
                 }
-                updatePerSubscriptionNextNotificationDate(thisEvent.getSubscription().getId(), itemDataWithNextBillingCycleDate.getNextBillingCycleDate(), items, billingMode, perSubscriptionFutureNotificationDate);
             }
-        }
 
-        // For debugging purposes
-        logStringBuilder.append("\n")
-                        .append(thisEvent);
-        for (final InvoiceItem item : items) {
-            logStringBuilder.append("\n\t")
-                            .append(item);
-        }
-        return items;
-    }
+            // For debugging purposes
+            invoiceItemGeneratorLogger.append(thisEvent, items);
 
-    //
-    // Go through invoices in reverse order and for each invoice extract a possible item for that subscription and the phaseName associated with this billing event
-    // Computes the startDate for that first one seen in that uninterrupted sequence and then add the FIXEDTERM duration to compute the max endDate
-    //
-    private LocalDate computeMaxEndDateForFixedTermPlanPhase(final BillingEvent thisEvent, final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
-        final LocalDate eventEffectiveDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
-        return addDurationToLocalDate(eventEffectiveDate, thisEvent.getPlanPhase().getDuration());
+            return items;
+        } catch (final CatalogApiException e) {
+            throw new InvoiceApiException(e);
+        }
     }
 
-    private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
+    private void updatePerSubscriptionNextNotificationDate(final UUID subscriptionId, final LocalDate nextBillingCycleDate, final List<InvoiceItem> newProposedItems, final BillingMode billingMode,
+                                                           final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
 
         LocalDate nextNotificationDate = null;
         switch (billingMode) {
@@ -261,6 +249,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
             default:
                 throw new IllegalStateException("Unrecognized billing mode " + billingMode);
         }
+
         if (nextNotificationDate != null) {
             SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = perSubscriptionFutureNotificationDates.get(subscriptionId);
             if (subscriptionFutureNotificationDates == null) {
@@ -333,7 +322,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
 
         for (int i = 0; i < numberOfWholeBillingPeriods; i++) {
             final LocalDate servicePeriodStartDate;
-            if (results.size() > 0) {
+            if (!results.isEmpty()) {
                 // Make sure the periods align, especially with the pro-ration calculations above
                 servicePeriodStartDate = results.get(results.size() - 1).getEndDate();
             } else if (i == 0) {
@@ -364,43 +353,27 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator 
     }
 
     private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent,
-                                               final LocalDate targetDate, final Currency currency, final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
-        final LocalDate roundedStartDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
+                                               final LocalDate targetDate, final Currency currency,
+                                               final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger, final InternalCallContext internalCallContext) throws InvoiceApiException {
+        final LocalDate roundedStartDate = internalCallContext.toLocalDate(thisEvent.getEffectiveDate());
         if (roundedStartDate.isAfter(targetDate)) {
             return null;
         } else {
             final BigDecimal fixedPrice = thisEvent.getFixedPrice();
 
             if (fixedPrice != null) {
-                return new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(),
-                                                 thisEvent.getSubscription().getId(),
-                                                 thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
-                                                 roundedStartDate, fixedPrice, currency);
+                final FixedPriceInvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, thisEvent.getSubscription().getBundleId(),
+                                                                                              thisEvent.getSubscription().getId(),
+                                                                                              thisEvent.getPlan().getName(), thisEvent.getPlanPhase().getName(),
+                                                                                              roundedStartDate, fixedPrice, currency);
+
+                // For debugging purposes
+                invoiceItemGeneratorLogger.append(thisEvent, fixedPriceInvoiceItem);
+
+                return fixedPriceInvoiceItem;
             } else {
                 return null;
             }
         }
     }
-
-    // That code should belong to Duration/DefaultDuration but requires a change api (not possible for 0.16.3, but will be moreved in 0.17.0)
-    private LocalDate addDurationToLocalDate(@Nullable final LocalDate inputDate, final Duration duration) {
-
-        if (inputDate == null) {
-            return inputDate;
-        }
-
-        switch (duration.getUnit()) {
-            case DAYS:
-                return inputDate.plusDays(duration.getNumber());
-            case MONTHS:
-                return inputDate.plusMonths(duration.getNumber());
-            case YEARS:
-                return inputDate.plusYears(duration.getNumber());
-            case UNLIMITED:
-                return inputDate.plusYears(100);
-            default:
-                throw new IllegalStateException("Unknwon duration " + duration.getUnit());
-        }
-    }
-
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
index 879ef43..fabac45 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceDateUtils.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -21,12 +23,45 @@ import java.math.BigDecimal;
 import org.joda.time.Days;
 import org.joda.time.LocalDate;
 import org.joda.time.Months;
-
+import org.joda.time.Weeks;
+import org.joda.time.Years;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.util.currency.KillBillMoney;
 
 public class InvoiceDateUtils {
 
+    public static int calculateNumberOfWholeBillingPeriods(final LocalDate startDate, final LocalDate endDate, final BillingPeriod billingPeriod) {
+        final int numberBetween;
+        final int numberInPeriod;
+        if (billingPeriod.getPeriod().getDays() != 0) {
+            numberBetween = Days.daysBetween(startDate, endDate).getDays();
+            numberInPeriod = billingPeriod.getPeriod().getDays();
+        } else if (billingPeriod.getPeriod().getWeeks() != 0) {
+            numberBetween = Weeks.weeksBetween(startDate, endDate).getWeeks();
+            numberInPeriod = billingPeriod.getPeriod().getWeeks();
+        } else if (billingPeriod.getPeriod().getMonths() != 0) {
+            numberBetween = Months.monthsBetween(startDate, endDate).getMonths();
+            numberInPeriod = billingPeriod.getPeriod().getMonths();
+        } else {
+            numberBetween = Years.yearsBetween(startDate, endDate).getYears();
+            numberInPeriod = billingPeriod.getPeriod().getYears();
+        }
+        return numberBetween / numberInPeriod;
+    }
+
+    public static BigDecimal calculateProRationBeforeFirstBillingPeriod(final LocalDate startDate, final LocalDate nextBillingCycleDate,
+                                                                        final BillingPeriod billingPeriod) {
+        final LocalDate previousBillingCycleDate = nextBillingCycleDate.minus(billingPeriod.getPeriod());
+        return calculateProrationBetweenDates(startDate, nextBillingCycleDate, previousBillingCycleDate, nextBillingCycleDate);
+    }
+
+    public static BigDecimal calculateProRationAfterLastBillingCycleDate(final LocalDate endDate, final LocalDate previousBillThroughDate,
+                                                                         final BillingPeriod billingPeriod) {
+        // Note: assumption is that previousBillThroughDate is correctly aligned with the billing cycle day
+        final LocalDate nextBillThroughDate = previousBillThroughDate.plus(billingPeriod.getPeriod());
+        return calculateProrationBetweenDates(previousBillThroughDate, endDate, previousBillThroughDate, nextBillThroughDate);
+    }
+
     /**
      * Called internally to calculate proration or when we recalculate approximate repair amount
      *
@@ -34,9 +69,8 @@ public class InvoiceDateUtils {
      * @param endDate                  end date of the prorated interval
      * @param previousBillingCycleDate start date of the period
      * @param nextBillingCycleDate     end date of the period
-     * @return
      */
-    public static BigDecimal calculateProrationBetweenDates(final LocalDate startDate, final LocalDate endDate, final LocalDate previousBillingCycleDate, final LocalDate nextBillingCycleDate) {
+    private static BigDecimal calculateProrationBetweenDates(final LocalDate startDate, final LocalDate endDate, final LocalDate previousBillingCycleDate, final LocalDate nextBillingCycleDate) {
         final int daysBetween = Days.daysBetween(previousBillingCycleDate, nextBillingCycleDate).getDays();
         return calculateProrationBetweenDates(startDate, endDate, daysBetween);
     }
@@ -52,122 +86,18 @@ public class InvoiceDateUtils {
         return days.divide(daysInPeriod, KillBillMoney.MAX_SCALE, KillBillMoney.ROUNDING_METHOD);
     }
 
-    public static BigDecimal calculateProRationBeforeFirstBillingPeriod(final LocalDate startDate, final LocalDate nextBillingCycleDate,
-                                                                        final BillingPeriod billingPeriod) {
-        final LocalDate previousBillingCycleDate = nextBillingCycleDate.plusMonths(-billingPeriod.getNumberOfMonths());
-
-        return calculateProrationBetweenDates(startDate, nextBillingCycleDate, previousBillingCycleDate, nextBillingCycleDate);
-    }
-
-    public static int calculateNumberOfWholeBillingPeriods(final LocalDate startDate, final LocalDate endDate, final BillingPeriod billingPeriod) {
-        final int numberOfMonths = Months.monthsBetween(startDate, endDate).getMonths();
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
-        return numberOfMonths / numberOfMonthsInPeriod;
-    }
-
-    public static LocalDate calculateLastBillingCycleDateBefore(final LocalDate date, final LocalDate previousBillCycleDate,
-                                                                final int billingCycleDay, final BillingPeriod billingPeriod) {
-        LocalDate proposedDate = previousBillCycleDate;
-
-        int numberOfPeriods = 0;
-        while (!proposedDate.isAfter(date)) {
-            proposedDate = previousBillCycleDate.plusMonths(numberOfPeriods * billingPeriod.getNumberOfMonths());
-            numberOfPeriods += 1;
-        }
-
-        proposedDate = proposedDate.plusMonths(-billingPeriod.getNumberOfMonths());
-
-        if (proposedDate.dayOfMonth().get() < billingCycleDay) {
-            final int lastDayOfTheMonth = proposedDate.dayOfMonth().getMaximumValue();
-            if (lastDayOfTheMonth < billingCycleDay) {
-                proposedDate = new LocalDate(proposedDate.getYear(), proposedDate.getMonthOfYear(), lastDayOfTheMonth);
-            } else {
-                proposedDate = new LocalDate(proposedDate.getYear(), proposedDate.getMonthOfYear(), billingCycleDay);
-            }
-        }
-
-        if (proposedDate.isBefore(previousBillCycleDate)) {
-            // Make sure not to go too far in the past
-            return previousBillCycleDate;
-        } else {
-            return proposedDate;
-        }
-    }
-
-    public static LocalDate calculateEffectiveEndDate(final LocalDate billCycleDate, final LocalDate targetDate,
-                                                      final BillingPeriod billingPeriod) {
-        if (targetDate.isBefore(billCycleDate)) {
-            return billCycleDate;
-        }
-
-        final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
-        int numberOfPeriods = 0;
-        LocalDate proposedDate = billCycleDate;
-
-        while (!proposedDate.isAfter(targetDate)) {
-            proposedDate = billCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
-            numberOfPeriods += 1;
-        }
-
-        return proposedDate;
-    }
-
-    public static LocalDate calculateEffectiveEndDate(final LocalDate billCycleDate, final LocalDate targetDate,
-                                                      final LocalDate endDate, final BillingPeriod billingPeriod) {
-        if (targetDate.isBefore(endDate)) {
-            if (targetDate.isBefore(billCycleDate)) {
-                return billCycleDate;
-            }
-
-            final int numberOfMonthsInPeriod = billingPeriod.getNumberOfMonths();
-            int numberOfPeriods = 0;
-            LocalDate proposedDate = billCycleDate;
-
-            while (!proposedDate.isAfter(targetDate)) {
-                proposedDate = billCycleDate.plusMonths(numberOfPeriods * numberOfMonthsInPeriod);
-                numberOfPeriods += 1;
-            }
-
-            // the current period includes the target date
-            // check to see whether the end date truncates the period
-            if (endDate.isBefore(proposedDate)) {
-                return endDate;
-            } else {
-                return proposedDate;
-            }
-        } else {
-            return endDate;
-        }
-    }
-
-    public static BigDecimal calculateProRationAfterLastBillingCycleDate(final LocalDate endDate, final LocalDate previousBillThroughDate,
-                                                                         final BillingPeriod billingPeriod) {
-        // Note: assumption is that previousBillThroughDate is correctly aligned with the billing cycle day
-        final LocalDate nextBillThroughDate = previousBillThroughDate.plusMonths(billingPeriod.getNumberOfMonths());
-        return calculateProrationBetweenDates(previousBillThroughDate, endDate, previousBillThroughDate, nextBillThroughDate);
-    }
-
-    public static LocalDate calculateBillingCycleDateOnOrAfter(final LocalDate date, final int billingCycleDayLocal) {
-        final int lastDayOfMonth = date.dayOfMonth().getMaximumValue();
-
-        final LocalDate fixedDate;
-        if (billingCycleDayLocal > lastDayOfMonth) {
-            fixedDate = new LocalDate(date.getYear(), date.getMonthOfYear(), lastDayOfMonth, date.getChronology());
-        } else {
-            fixedDate = new LocalDate(date.getYear(), date.getMonthOfYear(), billingCycleDayLocal, date.getChronology());
-        }
-
-        LocalDate proposedDate = fixedDate;
-        while (proposedDate.isBefore(date)) {
-            proposedDate = proposedDate.plusMonths(1);
+    public static LocalDate advanceByNPeriods(final LocalDate initialDate, final BillingPeriod billingPeriod, final int nbPeriods) {
+        LocalDate proposedDate = initialDate;
+        for (int i = 0; i < nbPeriods; i++) {
+            proposedDate = proposedDate.plus(billingPeriod.getPeriod());
         }
         return proposedDate;
     }
 
-    public static LocalDate calculateBillingCycleDateAfter(final LocalDate date, final int billingCycleDayLocal) {
-        LocalDate proposedDate = calculateBillingCycleDateOnOrAfter(date, billingCycleDayLocal);
-        if (date.compareTo(proposedDate) == 0) {
-            proposedDate = proposedDate.plusMonths(1);
+    public static LocalDate recedeByNPeriods(final LocalDate initialDate, final BillingPeriod billingPeriod, final int nbPeriods) {
+        LocalDate proposedDate = initialDate;
+        for (int i = 0; i < nbPeriods; i++) {
+            proposedDate = proposedDate.minus(billingPeriod.getPeriod());
         }
         return proposedDate;
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
index d9b3090..eb2ce88 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceItemGenerator.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -17,6 +17,7 @@
 
 package org.killbill.billing.invoice.generator;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -32,6 +33,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
 import org.killbill.billing.junction.BillingEventSet;
+import org.slf4j.Logger;
 
 public abstract class InvoiceItemGenerator {
 
@@ -40,4 +42,60 @@ public abstract class InvoiceItemGenerator {
                                                     final Currency targetCurrency, Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
                                                     final InternalCallContext context) throws InvoiceApiException;
 
+    public static class InvoiceItemGeneratorLogger {
+
+        private final UUID invoiceId;
+        private final UUID accountId;
+        private final String type;
+        private final Logger delegate;
+
+        private StringBuilder logStringBuilder = null;
+
+        public InvoiceItemGeneratorLogger(final UUID invoiceId, final UUID accountId, final String type, final Logger delegate) {
+            this.invoiceId = invoiceId;
+            this.accountId = accountId;
+            this.type = type;
+            this.delegate = delegate;
+        }
+
+        public void append(final Object event, final Collection<InvoiceItem> items) {
+            if (items.isEmpty()) {
+                return;
+            }
+            append(event, items.toArray(new InvoiceItem[items.size()]));
+        }
+
+        public void append(final Object event, final InvoiceItem... items) {
+            if (items.length == 0) {
+                return;
+            }
+
+            getLogStringBuilder().append("\n")
+                                 .append(event);
+
+            for (final InvoiceItem item : items) {
+                getLogStringBuilder().append("\n\t")
+                                     .append(item);
+            }
+        }
+
+        public void logItems() {
+            if (logStringBuilder != null) {
+                delegate.info(getLogStringBuilder().toString());
+            }
+        }
+
+        private StringBuilder getLogStringBuilder() {
+            if (logStringBuilder == null) {
+                logStringBuilder = new StringBuilder("Proposed ").append(type)
+                                                                 .append(" items for invoiceId='")
+                                                                 .append(invoiceId)
+                                                                 .append("', accountId='")
+                                                                 .append(accountId)
+                                                                 .append("'");
+            }
+
+            return logStringBuilder;
+        }
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
index 5051892..850b8a6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -27,7 +27,6 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -69,7 +68,6 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
         this.rawUsageOptimizer = rawUsageOptimizer;
     }
 
-
     @Override
     public List<InvoiceItem> generateItems(final ImmutableAccountData account,
                                            final UUID invoiceId,
@@ -79,11 +77,12 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
                                            final Currency targetCurrency,
                                            final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates,
                                            final InternalCallContext internalCallContext) throws InvoiceApiException {
-
         final Map<UUID, List<InvoiceItem>> perSubscriptionConsumableInArrearUsageItems = extractPerSubscriptionExistingConsumableInArrearUsageItems(eventSet.getUsages(), existingInvoices);
         try {
+            // Pretty-print the generated invoice items from the junction events
+            final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger = new InvoiceItemGeneratorLogger(invoiceId, account.getId(), "usage", log);
 
-            final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, account.getTimeZone());
+            final LocalDate minBillingEventDate = getMinBillingEventDate(eventSet, internalCallContext);
 
             final List<InvoiceItem> items = Lists.newArrayList();
             final Iterator<BillingEvent> events = eventSet.iterator();
@@ -94,7 +93,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
             while (events.hasNext()) {
                 final BillingEvent event = events.next();
                 // Skip events that are posterior to the targetDate
-                final LocalDate eventLocalEffectiveDate = eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(event.getEffectiveDate());
+                final LocalDate eventLocalEffectiveDate = internalCallContext.toLocalDate(event.getEffectiveDate());
                 if (eventLocalEffectiveDate.isAfter(targetDate)) {
                     continue;
                 }
@@ -118,10 +117,10 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
 
                 final UUID subscriptionId = event.getSubscription().getId();
                 if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
-                    final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), eventSet.getAccountDateAndTimeZoneContext());
+                    final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
                     final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
 
-                    final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+                    final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger);
                     final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
                     items.addAll(newInArrearUsageItems);
                     updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
@@ -131,32 +130,31 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
                 curEvents.add(event);
             }
             if (curSubscriptionId != null) {
-                final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), eventSet.getAccountDateAndTimeZoneContext());
+                final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), internalCallContext);
                 final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
 
-                final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
+                final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of(), invoiceItemGeneratorLogger);
                 final List<InvoiceItem> newInArrearUsageItems = subscriptionResult.getInvoiceItems();
                 items.addAll(newInArrearUsageItems);
                 updatePerSubscriptionNextNotificationUsageDate(curSubscriptionId, subscriptionResult.getPerUsageNotificationDates(), BillingMode.IN_ARREAR, perSubscriptionFutureNotificationDates);
             }
-            return items;
 
-        } catch (CatalogApiException e) {
+            invoiceItemGeneratorLogger.logItems();
+
+            return items;
+        } catch (final CatalogApiException e) {
             throw new InvoiceApiException(e);
         }
     }
 
-
-    private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final DateTimeZone accountTimeZone) {
+    private LocalDate getMinBillingEventDate(final BillingEventSet eventSet, final InternalCallContext internalCallContext) {
         DateTime minDate = null;
-        final Iterator<BillingEvent> events = eventSet.iterator();
-        while (events.hasNext()) {
-            final BillingEvent cur = events.next();
+        for (final BillingEvent cur : eventSet) {
             if (minDate == null || minDate.compareTo(cur.getEffectiveDate()) > 0) {
                 minDate = cur.getEffectiveDate();
             }
         }
-        return eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(minDate);
+        return internalCallContext.toLocalDate(minDate);
     }
 
     private void updatePerSubscriptionNextNotificationUsageDate(final UUID subscriptionId, final Map<String, LocalDate> nextBillingCycleDates, final BillingMode usageBillingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
@@ -175,7 +173,6 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
     }
 
     private Map<UUID, List<InvoiceItem>> extractPerSubscriptionExistingConsumableInArrearUsageItems(final Map<String, Usage> knownUsage, @Nullable final List<Invoice> existingInvoices) {
-
         if (existingInvoices == null || existingInvoices.isEmpty()) {
             return ImmutableMap.of();
         }
@@ -198,7 +195,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
             }
         }));
 
-        for (InvoiceItem cur : usageConsumableInArrearItems) {
+        for (final InvoiceItem cur : usageConsumableInArrearItems) {
             List<InvoiceItem> perSubscriptionUsageItems = result.get(cur.getSubscriptionId());
             if (perSubscriptionUsageItems == null) {
                 perSubscriptionUsageItems = new LinkedList<InvoiceItem>();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
index 3080c5a..a9aa2dc 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
@@ -25,7 +25,6 @@ import org.killbill.billing.invoice.InvoiceTagHandler;
 import org.killbill.billing.invoice.api.DefaultInvoiceService;
 import org.killbill.billing.invoice.api.InvoiceApiHelper;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
 import org.killbill.billing.invoice.api.InvoiceNotifier;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
@@ -33,9 +32,9 @@ import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
 import org.killbill.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
-import org.killbill.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
 import org.killbill.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceUserApi;
+import org.killbill.billing.invoice.config.MultiTenantInvoiceConfig;
 import org.killbill.billing.invoice.dao.CBADao;
 import org.killbill.billing.invoice.dao.DefaultInvoiceDao;
 import org.killbill.billing.invoice.dao.InvoiceDao;
@@ -55,16 +54,18 @@ import org.killbill.billing.invoice.template.bundles.DefaultResourceBundleFactor
 import org.killbill.billing.invoice.usage.RawUsageOptimizer;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.killbill.billing.util.template.translation.TranslatorConfig;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 public class DefaultInvoiceModule extends KillBillModule implements InvoiceModule {
 
-    InvoiceConfig config;
+
+    InvoiceConfig staticInvoiceConfig;
 
     public DefaultInvoiceModule(final KillbillConfigSource configSource) {
         super(configSource);
@@ -92,8 +93,9 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
     }
 
     protected void installConfig() {
-        config = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
-        bind(InvoiceConfig.class).toInstance(config);
+        staticInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
+        bind(InvoiceConfig.class).annotatedWith(Names.named(STATIC_CONFIG)).toInstance(staticInvoiceConfig);
+        bind(InvoiceConfig.class).to(MultiTenantInvoiceConfig.class).asEagerSingleton();
     }
 
     protected void installInvoiceService() {
@@ -104,10 +106,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
         bind(ResourceBundleFactory.class).to(DefaultResourceBundleFactory.class).asEagerSingleton();
     }
 
-    @Override
-    public void installInvoiceMigrationApi() {
-        bind(InvoiceMigrationApi.class).to(DefaultInvoiceMigrationApi.class).asEagerSingleton();
-    }
 
     protected void installNotifiers() {
         bind(NextBillingDateNotifier.class).to(DefaultNextBillingDateNotifier.class).asEagerSingleton();
@@ -118,7 +116,7 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
     }
 
     protected void installInvoiceNotifier() {
-        if (config.isEmailNotificationsEnabled()) {
+        if (staticInvoiceConfig.isEmailNotificationsEnabled()) {
             bind(InvoiceNotifier.class).to(EmailInvoiceNotifier.class).asEagerSingleton();
         } else {
             bind(InvoiceNotifier.class).to(NullInvoiceNotifier.class).asEagerSingleton();
@@ -163,7 +161,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
         installInvoiceUserApi();
         installInvoiceInternalApi();
         installInvoicePaymentApi();
-        installInvoiceMigrationApi();
         installResourceBundleFactory();
         bind(RawUsageOptimizer.class).asEagerSingleton();
         bind(InvoiceApiHelper.class).asEagerSingleton();
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 d46a0ed..c29c28a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -21,6 +21,8 @@ package org.killbill.billing.invoice;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -34,6 +36,7 @@ import javax.annotation.Nullable;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
@@ -60,13 +63,17 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoiceNotifier;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceNotificationInternalEvent;
 import org.killbill.billing.invoice.api.user.DefaultNullInvoiceEvent;
+import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
 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.dao.InvoiceModelDaoHelper;
+import org.killbill.billing.invoice.dao.InvoiceParentChildModelDao;
 import org.killbill.billing.invoice.generator.InvoiceGenerator;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
@@ -74,6 +81,8 @@ import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFu
 import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
 import org.killbill.billing.invoice.model.InvoiceItemFactory;
+import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
+import org.killbill.billing.invoice.model.ParentInvoiceItem;
 import org.killbill.billing.invoice.model.RecurringInvoiceItem;
 import org.killbill.billing.invoice.notification.DefaultNextBillingDateNotifier;
 import org.killbill.billing.invoice.notification.NextBillingDateNotificationKey;
@@ -83,11 +92,11 @@ import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
@@ -108,7 +117,9 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 import com.google.inject.Inject;
 
@@ -117,7 +128,7 @@ public class InvoiceDispatcher {
     private static final Logger log = LoggerFactory.getLogger(InvoiceDispatcher.class);
 
     private static final Ordering<DateTime> UPCOMING_NOTIFICATION_DATE_ORDERING = Ordering.natural();
-    private final static Joiner JOINER_COMMA = Joiner.on(",");
+    private static final Joiner JOINER_COMMA = Joiner.on(",");
     private static final TargetDateDryRunArguments TARGET_DATE_DRY_RUN_ARGUMENTS = new TargetDateDryRunArguments();
 
     private final InvoiceGenerator generator;
@@ -166,19 +177,19 @@ public class InvoiceDispatcher {
     public void processSubscriptionForInvoiceGeneration(final EffectiveSubscriptionInternalEvent transition,
                                                         final InternalCallContext context) throws InvoiceApiException {
         final UUID subscriptionId = transition.getSubscriptionId();
-        final DateTime targetDate = transition.getEffectiveTransitionTime();
+        final LocalDate targetDate = context.toLocalDate(transition.getEffectiveTransitionTime());
         processSubscriptionForInvoiceGeneration(subscriptionId, targetDate, context);
     }
 
-    public void processSubscriptionForInvoiceGeneration(final UUID subscriptionId, final DateTime targetDate, final InternalCallContext context) throws InvoiceApiException {
+    public void processSubscriptionForInvoiceGeneration(final UUID subscriptionId, final LocalDate targetDate, final InternalCallContext context) throws InvoiceApiException {
         processSubscriptionInternal(subscriptionId, targetDate, false, context);
     }
 
-    public void processSubscriptionForInvoiceNotification(final UUID subscriptionId, final DateTime targetDate, final InternalCallContext context) throws InvoiceApiException {
+    public void processSubscriptionForInvoiceNotification(final UUID subscriptionId, final LocalDate targetDate, final InternalCallContext context) throws InvoiceApiException {
         final Invoice dryRunInvoice = processSubscriptionInternal(subscriptionId, targetDate, true, context);
         if (dryRunInvoice != null && dryRunInvoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
             final InvoiceNotificationInternalEvent event = new DefaultInvoiceNotificationInternalEvent(dryRunInvoice.getAccountId(), dryRunInvoice.getBalance(), dryRunInvoice.getCurrency(),
-                                                                                                       targetDate, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+                                                                                                       context.toUTCDateTime(targetDate), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
             try {
                 eventBus.post(event);
             } catch (EventBusException e) {
@@ -187,7 +198,7 @@ public class InvoiceDispatcher {
         }
     }
 
-    private Invoice processSubscriptionInternal(final UUID subscriptionId, final DateTime targetDate, final boolean dryRunForNotification, final InternalCallContext context) throws InvoiceApiException {
+    private Invoice processSubscriptionInternal(final UUID subscriptionId, final LocalDate targetDate, final boolean dryRunForNotification, final InternalCallContext context) throws InvoiceApiException {
         try {
             if (subscriptionId == null) {
                 log.warn("Failed handling SubscriptionBase change.", new InvoiceApiException(ErrorCode.INVOICE_INVALID_TRANSITION));
@@ -204,7 +215,7 @@ public class InvoiceDispatcher {
         }
     }
 
-    public Invoice processAccount(final UUID accountId, @Nullable final DateTime targetDate,
+    public Invoice processAccount(final UUID accountId, @Nullable final LocalDate targetDate,
                                   @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
         GlobalLock lock = null;
         try {
@@ -221,13 +232,18 @@ public class InvoiceDispatcher {
         return null;
     }
 
-    private Invoice processAccountWithLock(final UUID accountId, @Nullable final DateTime inputTargetDateTime,
+    private Invoice processAccountWithLock(final UUID accountId, @Nullable final LocalDate inputTargetDateMaybeNull,
                                            @Nullable final DryRunArguments dryRunArguments, final InternalCallContext context) throws InvoiceApiException {
-
         final boolean isDryRun = dryRunArguments != null;
-        // A null inputTargetDateTime is only allowed in dryRun mode to have the system compute it
-        Preconditions.checkArgument(inputTargetDateTime != null ||
-                                    (dryRunArguments != null && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType())), "inputTargetDateTime is required in non dryRun mode");
+        final boolean upcomingInvoiceDryRun = isDryRun && DryRunType.UPCOMING_INVOICE.equals(dryRunArguments.getDryRunType());
+
+        LocalDate inputTargetDate = inputTargetDateMaybeNull;
+        // A null inputTargetDate is only allowed in dryRun mode to have the system compute it
+        if (inputTargetDate == null && !upcomingInvoiceDryRun) {
+            inputTargetDate = clock.getUTCToday();
+        }
+        Preconditions.checkArgument(inputTargetDate != null || upcomingInvoiceDryRun, "inputTargetDate is required in non dryRun mode");
+
         try {
             // Make sure to first set the BCD if needed then get the account object (to have the BCD set)
             final BillingEventSet billingEvents = billingApi.getBillingEventsForAccountAndUpdateAccountBCD(accountId, dryRunArguments, context);
@@ -235,11 +251,11 @@ public class InvoiceDispatcher {
                 return null;
             }
             final Iterable<UUID> filteredSubscriptionIdsForDryRun = getFilteredSubscriptionIdsForDryRun(dryRunArguments, billingEvents);
-            final List<DateTime> candidateDateTimes = (inputTargetDateTime != null) ?
-                                                      ImmutableList.of(inputTargetDateTime) :
-                                                      getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
-            for (final DateTime curTargetDateTime : candidateDateTimes) {
-                final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDateTime, billingEvents, isDryRun, context);
+            final List<LocalDate> candidateTargetDates = (inputTargetDate != null) ?
+                                                         ImmutableList.<LocalDate>of(inputTargetDate) :
+                                                         getUpcomingInvoiceCandidateDates(filteredSubscriptionIdsForDryRun, context);
+            for (final LocalDate curTargetDate : candidateTargetDates) {
+                final Invoice invoice = processAccountWithLockAndInputTargetDate(accountId, curTargetDate, billingEvents, isDryRun, context);
                 if (invoice != null) {
                     filterInvoiceItemsForDryRun(filteredSubscriptionIdsForDryRun, invoice);
                     return invoice;
@@ -247,10 +263,13 @@ public class InvoiceDispatcher {
             }
             return null;
         } catch (final CatalogApiException e) {
-            log.error("Failed handling SubscriptionBase change.", e);
+            log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
             return null;
         } catch (final AccountApiException e) {
-            log.error("Failed handling SubscriptionBase change.", e);
+            log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
+            return null;
+        } catch (final SubscriptionBaseApiException e) {
+            log.warn("Failed to retrieve BillingEvents for accountId='{}', dryRunArguments='{}'", accountId, dryRunArguments, e);
             return null;
         }
     }
@@ -293,7 +312,7 @@ public class InvoiceDispatcher {
         });
     }
 
-    private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final DateTime targetDateTime,
+    private Invoice processAccountWithLockAndInputTargetDate(final UUID accountId, final LocalDate targetDate,
                                                              final BillingEventSet billingEvents, final boolean isDryRun, final InternalCallContext context) throws InvoiceApiException {
         try {
             final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
@@ -309,12 +328,11 @@ public class InvoiceDispatcher {
                                                                                                 }));
 
             final Currency targetCurrency = account.getCurrency();
-            final LocalDate targetDate = billingEvents.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(targetDateTime);
             final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
             final Invoice invoice = invoiceWithMetadata.getInvoice();
 
             // Compute future notifications
-            final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, billingEvents.getAccountDateAndTimeZoneContext(), context);
+            final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, context);
 
             //
 
@@ -322,9 +340,9 @@ public class InvoiceDispatcher {
             //
             if (invoice == null) {
                 if (isDryRun) {
-                    log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}', targetDateTime='{}'", accountId, targetDate, targetDateTime);
+                    log.info("Generated null dryRun invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
                 } else {
-                    log.info("Generated null invoice for accountId='{}', targetDate='{}', targetDateTime='{}'", accountId, targetDate, targetDateTime);
+                    log.info("Generated null invoice for accountId='{}', targetDate='{}'", accountId, targetDate);
 
                     final BusInternalEvent event = new DefaultNullInvoiceEvent(accountId, clock.getUTCToday(),
                                                                                context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
@@ -364,25 +382,26 @@ public class InvoiceDispatcher {
 
                 final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
 
-                setChargedThroughDates(billingEvents.getAccountDateAndTimeZoneContext(), invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
+                setChargedThroughDates(invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
 
-                // TODO we should send bus events when we commit the ionvoice on disk in commitInvoice
-                postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+                if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
+                    // TODO we should send bus events when we commit the invoice on disk in commitInvoice
+                    postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+                    notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
+                }
 
-                notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
             }
             return invoice;
         } catch (final AccountApiException e) {
             log.error("Failed handling SubscriptionBase change.", e);
             return null;
-        } catch (SubscriptionBaseApiException e) {
+        } catch (final SubscriptionBaseApiException e) {
             log.error("Failed handling SubscriptionBase change.", e);
             return null;
         }
     }
 
-    private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) {
-
+    private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final InternalCallContext context) {
         final Map<UUID, List<SubscriptionNotification>> result = new HashMap<UUID, List<SubscriptionNotification>>();
 
         for (final UUID subscriptionId : invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().keySet()) {
@@ -392,13 +411,13 @@ public class InvoiceDispatcher {
             final SubscriptionFutureNotificationDates subscriptionFutureNotificationDates = invoiceWithMetadata.getPerSubscriptionFutureNotificationDates().get(subscriptionId);
             // Add next recurring date if any
             if (subscriptionFutureNotificationDates.getNextRecurringDate() != null) {
-                perSubscriptionNotifications.add(new SubscriptionNotification(dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(subscriptionFutureNotificationDates.getNextRecurringDate()), true));
+                perSubscriptionNotifications.add(new SubscriptionNotification(context.toUTCDateTime(subscriptionFutureNotificationDates.getNextRecurringDate()), true));
             }
             // Add next usage dates if any
             if (subscriptionFutureNotificationDates.getNextUsageDates() != null) {
-                for (UsageDef usageDef : subscriptionFutureNotificationDates.getNextUsageDates().keySet()) {
+                for (final UsageDef usageDef : subscriptionFutureNotificationDates.getNextUsageDates().keySet()) {
                     final LocalDate nextNotificationDateForUsage = subscriptionFutureNotificationDates.getNextUsageDates().get(usageDef);
-                    final DateTime subscriptionUsageCallbackDate = nextNotificationDateForUsage != null ? dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(nextNotificationDateForUsage) : null;
+                    final DateTime subscriptionUsageCallbackDate = nextNotificationDateForUsage != null ? context.toUTCDateTime(nextNotificationDateForUsage) : null;
                     perSubscriptionNotifications.add(new SubscriptionNotification(subscriptionUsageCallbackDate, true));
                 }
             }
@@ -408,10 +427,10 @@ public class InvoiceDispatcher {
         }
 
         // If dryRunNotification is enabled we also need to fetch the upcoming PHASE dates (we add SubscriptionNotification with isForInvoiceNotificationTrigger = false)
-        final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule().getMillis() > 0;
+        final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule(context).getMillis() > 0;
         if (isInvoiceNotificationEnabled) {
             final Map<UUID, DateTime> upcomingPhasesForSubscriptions = subscriptionApi.getNextFutureEventForSubscriptions(SubscriptionBaseTransitionType.PHASE, context);
-            for (UUID cur : upcomingPhasesForSubscriptions.keySet()) {
+            for (final UUID cur : upcomingPhasesForSubscriptions.keySet()) {
                 final DateTime curDate = upcomingPhasesForSubscriptions.get(cur);
                 List<SubscriptionNotification> resultValue = result.get(cur);
                 if (resultValue == null) {
@@ -421,7 +440,7 @@ public class InvoiceDispatcher {
                 result.put(cur, resultValue);
             }
         }
-        return new FutureAccountNotifications(dateAndTimeZoneContext, result);
+        return new FutureAccountNotifications(result);
     }
 
     private Iterable<InvoiceItemModelDao> transformToInvoiceModelDao(final List<InvoiceItem> invoiceItems) {
@@ -449,14 +468,14 @@ public class InvoiceDispatcher {
     private void logInvoiceWithItems(final ImmutableAccountData account, final Invoice invoice, final LocalDate targetDate, final Set<UUID> adjustedUniqueOtherInvoiceId, final boolean isRealInvoiceWithItems) {
         final StringBuilder tmp = new StringBuilder();
         if (isRealInvoiceWithItems) {
-            tmp.append(String.format("Generated invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
+            tmp.append(String.format("Generated invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':", invoice.getId(), invoice.getNumberOfItems(), account.getId(), targetDate));
         } else {
             final String adjustedInvoices = JOINER_COMMA.join(adjustedUniqueOtherInvoiceId.toArray(new UUID[adjustedUniqueOtherInvoiceId.size()]));
             tmp.append(String.format("Adjusting existing invoiceId='%s', numberOfItems='%d', accountId='%s', targetDate='%s':\n",
                                      adjustedInvoices, invoice.getNumberOfItems(), account.getId(), targetDate));
         }
-        for (InvoiceItem item : invoice.getInvoiceItems()) {
-            tmp.append(String.format("\t item = %s\n", item));
+        for (final InvoiceItem item : invoice.getInvoiceItems()) {
+            tmp.append(String.format("\n\t item = %s", item));
         }
         log.info(tmp.toString());
     }
@@ -464,7 +483,7 @@ public class InvoiceDispatcher {
     private boolean commitInvoiceAndSetFutureNotifications(final ImmutableAccountData account, final InvoiceModelDao invoiceModelDao,
                                                            final Iterable<InvoiceItemModelDao> invoiceItemModelDaos,
                                                            final FutureAccountNotifications futureAccountNotifications,
-                                                           boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
+                                                           final boolean isRealInvoiceWithItems, final InternalCallContext context) throws SubscriptionBaseApiException, InvoiceApiException {
         // We filter any zero amount for USAGE items prior we generate the invoice, which may leave us with an invoice with no items;
         // we recompute the isRealInvoiceWithItems flag based on what is left (the call to invoice is still necessary to set the future notifications).
         final Iterable<InvoiceItemModelDao> filteredInvoiceItemModelDaos = Iterables.filter(invoiceItemModelDaos, new Predicate<InvoiceItemModelDao>() {
@@ -536,13 +555,12 @@ public class InvoiceDispatcher {
         return internalCallContextFactory.createCallContext(context);
     }
 
-    private void setChargedThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
-                                        final Collection<InvoiceItem> fixedPriceItems,
+    private void setChargedThroughDates(final Collection<InvoiceItem> fixedPriceItems,
                                         final Collection<InvoiceItem> recurringItems,
                                         final InternalCallContext context) throws SubscriptionBaseApiException {
         final Map<UUID, DateTime> chargeThroughDates = new HashMap<UUID, DateTime>();
-        addInvoiceItemsToChargeThroughDates(dateAndTimeZoneContext, chargeThroughDates, fixedPriceItems);
-        addInvoiceItemsToChargeThroughDates(dateAndTimeZoneContext, chargeThroughDates, recurringItems);
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, fixedPriceItems, context);
+        addInvoiceItemsToChargeThroughDates(chargeThroughDates, recurringItems, context);
 
         for (final UUID subscriptionId : chargeThroughDates.keySet()) {
             if (subscriptionId != null) {
@@ -560,15 +578,15 @@ public class InvoiceDispatcher {
         }
     }
 
-    private void addInvoiceItemsToChargeThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
-                                                     final Map<UUID, DateTime> chargeThroughDates,
-                                                     final Collection<InvoiceItem> items) {
+    private void addInvoiceItemsToChargeThroughDates(final Map<UUID, DateTime> chargeThroughDates,
+                                                     final Collection<InvoiceItem> items,
+                                                     final InternalTenantContext internalTenantContext) {
 
         for (final InvoiceItem item : items) {
             final UUID subscriptionId = item.getSubscriptionId();
             final LocalDate endDate = (item.getEndDate() != null) ? item.getEndDate() : item.getStartDate();
 
-            final DateTime proposedChargedThroughDate = dateAndTimeZoneContext.computeUTCDateTimeFromLocalDate(endDate);
+            final DateTime proposedChargedThroughDate = internalTenantContext.toUTCDateTime(endDate);
             if (chargeThroughDates.containsKey(subscriptionId)) {
                 if (chargeThroughDates.get(subscriptionId).isBefore(proposedChargedThroughDate)) {
                     chargeThroughDates.put(subscriptionId, proposedChargedThroughDate);
@@ -581,18 +599,12 @@ public class InvoiceDispatcher {
 
     public static class FutureAccountNotifications {
 
-        private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
         private final Map<UUID, List<SubscriptionNotification>> notifications;
 
-        public FutureAccountNotifications(final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final Map<UUID, List<SubscriptionNotification>> notifications) {
-            this.dateAndTimeZoneContext = dateAndTimeZoneContext;
+        public FutureAccountNotifications(final Map<UUID, List<SubscriptionNotification>> notifications) {
             this.notifications = notifications;
         }
 
-        public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
-            return dateAndTimeZoneContext;
-        }
-
         public Map<UUID, List<SubscriptionNotification>> getNotifications() {
             return notifications;
         }
@@ -617,11 +629,16 @@ public class InvoiceDispatcher {
         }
     }
 
-    private List<DateTime> getUpcomingInvoiceCandidateDates(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
+    private List<LocalDate> getUpcomingInvoiceCandidateDates(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
         final Iterable<DateTime> nextScheduledInvoiceDates = getNextScheduledInvoiceEffectiveDate(filteredSubscriptionIds, internalCallContext);
         final Iterable<DateTime> nextScheduledSubscriptionsEventDates = subscriptionApi.getFutureNotificationsForAccount(internalCallContext);
-        Iterables.concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates);
-        return UPCOMING_NOTIFICATION_DATE_ORDERING.sortedCopy(Iterables.concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates));
+        return Lists.<DateTime, LocalDate>transform(UPCOMING_NOTIFICATION_DATE_ORDERING.sortedCopy(Iterables.<DateTime>concat(nextScheduledInvoiceDates, nextScheduledSubscriptionsEventDates)),
+                                                    new Function<DateTime, LocalDate>() {
+                                                        @Override
+                                                        public LocalDate apply(final DateTime input) {
+                                                            return internalCallContext.toLocalDate(input);
+                                                        }
+                                                    });
     }
 
     private Iterable<DateTime> getNextScheduledInvoiceEffectiveDate(final Iterable<UUID> filteredSubscriptionIds, final InternalCallContext internalCallContext) {
@@ -653,7 +670,7 @@ public class InvoiceDispatcher {
         }
     }
 
-    private final static class TargetDateDryRunArguments implements DryRunArguments {
+    private static final class TargetDateDryRunArguments implements DryRunArguments {
 
         @Override
         public DryRunType getDryRunType() {
@@ -676,7 +693,7 @@ public class InvoiceDispatcher {
         }
 
         @Override
-        public DateTime getEffectiveDate() {
+        public LocalDate getEffectiveDate() {
             return null;
         }
 
@@ -694,5 +711,145 @@ public class InvoiceDispatcher {
         public List<PlanPhasePriceOverride> getPlanPhasePriceOverrides() {
             return null;
         }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("TargetDateDryRunArguments{");
+            sb.append("dryRunType=").append(DryRunType.TARGET_DATE);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
+
+    public void processParentInvoiceForInvoiceGeneration(final ImmutableAccountData account, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException {
+
+        final InvoiceModelDao childInvoiceModelDao = invoiceDao.getById(childInvoiceId, context);
+        final Invoice childInvoice = new DefaultInvoice(childInvoiceModelDao);
+
+        final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
+        final InternalCallContext parentContext = internalCallContextFactory.createInternalCallContext(parentAccountRecordId, context);
+
+        BigDecimal childInvoiceAmount = InvoiceCalculatorUtils.computeChildInvoiceAmount(childInvoice.getCurrency(), childInvoice.getInvoiceItems());
+        InvoiceModelDao draftParentInvoice = invoiceDao.getParentDraftInvoice(account.getParentAccountId(), parentContext);
+
+        final DateTime today = clock.getNow(account.getTimeZone());
+        final String description = account.getExternalKey().concat(" summary");
+        if (draftParentInvoice != null) {
+
+            for (InvoiceItemModelDao item : draftParentInvoice.getInvoiceItems()) {
+                if ((item.getChildAccountId() != null) && item.getChildAccountId().equals(childInvoice.getAccountId())) {
+                    // update child item amount for existing parent invoice item
+                    BigDecimal newChildInvoiceAmount = childInvoiceAmount.add(item.getAmount());
+                    invoiceDao.updateInvoiceItemAmount(item.getId(), newChildInvoiceAmount, parentContext);
+                    return;
+                }
+            }
+
+            // new item when the parent invoices does not have this child item yet
+            final ParentInvoiceItem newParentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), today, draftParentInvoice.getId(), account.getParentAccountId(), account.getId(), childInvoiceAmount, account.getCurrency(), description);
+            draftParentInvoice.addInvoiceItem(new InvoiceItemModelDao(newParentInvoiceItem));
+
+            List<InvoiceModelDao> invoices = new ArrayList<InvoiceModelDao>();
+            invoices.add(draftParentInvoice);
+            invoiceDao.createInvoices(invoices, parentContext);
+        } else {
+            if (shouldIgnoreChildInvoice(childInvoice, childInvoiceAmount)) {
+                return;
+            }
+
+            draftParentInvoice = new InvoiceModelDao(account.getParentAccountId(), today.toLocalDate(), account.getCurrency(), InvoiceStatus.DRAFT, true);
+            InvoiceItem parentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), today, draftParentInvoice.getId(), account.getParentAccountId(), account.getId(), childInvoiceAmount, account.getCurrency(), description);
+            draftParentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
+
+            // build account date time zone
+            final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
+
+            invoiceDao.createInvoice(draftParentInvoice, draftParentInvoice.getInvoiceItems(), true, futureAccountNotifications, parentContext);
+        }
+
+        // save parent child invoice relation
+        final InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(draftParentInvoice.getId(), childInvoiceId, account.getId());
+        invoiceDao.createParentChildInvoiceRelation(invoiceRelation, parentContext);
+
     }
+
+    private boolean shouldIgnoreChildInvoice(final Invoice childInvoice, final BigDecimal childInvoiceAmount) {
+
+        switch (childInvoiceAmount.compareTo(BigDecimal.ZERO)) {
+            case -1 :
+                // do nothing if child invoice has negative amount because it's a credit and it will be use in next invoice
+                return true;
+            case 1 : return false;
+            case 0 :
+                // only ignore if amount == 0 and any item is not FIXED or RECURRING
+                for (InvoiceItem item : childInvoice.getInvoiceItems()) {
+                    if (item.getInvoiceItemType().equals(InvoiceItemType.FIXED) || item.getInvoiceItemType().equals(InvoiceItemType.RECURRING)) return false;
+                }
+        }
+
+        return true;
+    }
+
+    public void processParentInvoiceForAdjustments(final ImmutableAccountData account, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException {
+
+        final InvoiceModelDao childInvoiceModelDao = invoiceDao.getById(childInvoiceId, context);
+        final InvoiceModelDao parentInvoiceModelDao = childInvoiceModelDao.getParentInvoice();
+
+        if (parentInvoiceModelDao == null) {
+            throw new InvoiceApiException(ErrorCode.INVOICE_MISSING_PARENT_INVOICE, childInvoiceModelDao.getId());
+        } else if (InvoiceModelDaoHelper.getBalance(parentInvoiceModelDao).compareTo(BigDecimal.ZERO) == 0) {
+            // ignore item adjustments for paid invoices.
+            return;
+        }
+
+        final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
+        final InternalCallContext parentContext = internalCallContextFactory.createInternalCallContext(parentAccountRecordId, context);
+        final String description = "Adjustment for account ".concat(account.getExternalKey());
+
+        // find PARENT_SUMMARY invoice item for this child account
+        final InvoiceItemModelDao parentSummaryInvoiceItem = Iterables.find(parentInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
+            @Override
+            public boolean apply(@Nullable final InvoiceItemModelDao input) {
+                return input.getType().equals(InvoiceItemType.PARENT_SUMMARY)
+                       && input.getChildAccountId().equals(childInvoiceModelDao.getAccountId());
+            }
+        });
+
+        final Iterable<InvoiceItemModelDao> childAdjustments = Iterables.filter(childInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
+            @Override
+            public boolean apply(@Nullable final InvoiceItemModelDao input) {
+                return input.getType().equals(InvoiceItemType.ITEM_ADJ);
+            }
+        });
+
+        // find last ITEM_ADJ invoice added in child invoice
+        final InvoiceItemModelDao lastChildInvoiceItemAdjustment = Collections.max(Lists.newArrayList(childAdjustments), new Comparator<InvoiceItemModelDao>() {
+            @Override
+            public int compare(InvoiceItemModelDao o1, InvoiceItemModelDao o2) {
+                return o1.getCreatedDate().compareTo(o2.getCreatedDate());
+            }
+        });
+
+        final BigDecimal childInvoiceAdjustmentAmount = lastChildInvoiceItemAdjustment.getAmount();
+
+        if (parentInvoiceModelDao.getStatus().equals(InvoiceStatus.COMMITTED)) {
+            ItemAdjInvoiceItem adj = new ItemAdjInvoiceItem(UUIDs.randomUUID(),
+                                                            lastChildInvoiceItemAdjustment.getCreatedDate(),
+                                                            parentSummaryInvoiceItem.getInvoiceId(),
+                                                            parentSummaryInvoiceItem.getAccountId(),
+                                                            lastChildInvoiceItemAdjustment.getStartDate(),
+                                                            description,
+                                                            childInvoiceAdjustmentAmount,
+                                                            parentInvoiceModelDao.getCurrency(),
+                                                            parentSummaryInvoiceItem.getId());
+            parentInvoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(adj));
+            invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(parentInvoiceModelDao), parentContext);
+            return;
+        }
+
+        // update item amount
+        final BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentSummaryInvoiceItem.getAmount());
+        invoiceDao.updateInvoiceItemAmount(parentSummaryInvoiceItem.getId(), newParentInvoiceItemAmount, parentContext);
+    }
+
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
index 0a82d37..2e103c4 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceListener.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -21,16 +23,19 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.BlockingTransitionInternalEvent;
-import org.killbill.billing.events.EffectiveEntitlementInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
+import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,29 +51,29 @@ public class InvoiceListener {
     private final InvoiceDispatcher dispatcher;
     private final InternalCallContextFactory internalCallContextFactory;
     private final AccountInternalApi accountApi;
+    private final InvoiceInternalApi invoiceApi;
     private final InvoiceConfig invoiceConfig;
     private final Clock clock;
 
     @Inject
     public InvoiceListener(final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory,
-                           final InvoiceConfig invoiceConfig, final InvoiceDispatcher dispatcher) {
+                           final InvoiceConfig invoiceConfig, final InvoiceDispatcher dispatcher, InvoiceInternalApi invoiceApi) {
         this.accountApi = accountApi;
         this.dispatcher = dispatcher;
         this.invoiceConfig = invoiceConfig;
         this.internalCallContextFactory = internalCallContextFactory;
         this.clock = clock;
+        this.invoiceApi = invoiceApi;
     }
 
     @AllowConcurrentEvents
     @Subscribe
     public void handleSubscriptionTransition(final EffectiveSubscriptionInternalEvent event) {
-
         try {
             //  Skip future uncancel event
             //  Skip events which are marked as not being the last one
             if (event.getTransitionType() == SubscriptionBaseTransitionType.UNCANCEL ||
-                event.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT
-                || event.getRemainingEventsForUserOperation() > 0) {
+                event.getRemainingEventsForUserOperation() > 0) {
                 return;
             }
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
@@ -78,22 +83,10 @@ public class InvoiceListener {
         }
     }
 
-    @AllowConcurrentEvents
-    @Subscribe
-    public void handleEntitlementTransition(final EffectiveEntitlementInternalEvent event) {
-
-        try {
-            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
-            dispatcher.processAccount(event.getAccountId(), event.getEffectiveTransitionTime(), null, context);
-        } catch (InvoiceApiException e) {
-            log.warn("Unable to process event {}", event, e);
-        }
-    }
 
     @AllowConcurrentEvents
     @Subscribe
     public void handleBlockingStateTransition(final BlockingTransitionInternalEvent event) {
-
         // We are only interested in blockBilling or unblockBilling transitions.
         if (!event.isTransitionedToUnblockedBilling() && !event.isTransitionedToBlockedBilling()) {
             return;
@@ -102,7 +95,7 @@ public class InvoiceListener {
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "SubscriptionBaseTransition", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
             final UUID accountId = accountApi.getByRecordId(event.getSearchKey1(), context);
-            dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
+            dispatcher.processAccount(accountId, null, null, context);
         } catch (InvoiceApiException e) {
             log.warn("Unable to process event {}", event, e);
         } catch (AccountApiException e) {
@@ -113,7 +106,7 @@ public class InvoiceListener {
     public void handleNextBillingDateEvent(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
-            dispatcher.processSubscriptionForInvoiceGeneration(subscriptionId, eventDateTime, context);
+            dispatcher.processSubscriptionForInvoiceGeneration(subscriptionId, context.toLocalDate(eventDateTime), context);
         } catch (InvoiceApiException e) {
             log.warn("Unable to process subscriptionId='{}', eventDateTime='{}'", subscriptionId, eventDateTime, e);
         }
@@ -122,9 +115,65 @@ public class InvoiceListener {
     public void handleEventForInvoiceNotification(final UUID subscriptionId, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
         try {
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Next Billing Date", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
-            dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, eventDateTime, context);
+            dispatcher.processSubscriptionForInvoiceNotification(subscriptionId, context.toLocalDate(eventDateTime), context);
         } catch (InvoiceApiException e) {
             log.warn("Unable to process subscriptionId='{}', eventDateTime='{}'", subscriptionId, eventDateTime, e);
         }
     }
+
+    @AllowConcurrentEvents
+    @Subscribe
+    public void handleChildrenInvoiceCreationEvent(final InvoiceCreationInternalEvent event) {
+
+        try {
+            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "CreateParentInvoice", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+            // TODO it may change to Account - #459
+            final ImmutableAccountData account = accountApi.getImmutableAccountDataById(event.getAccountId(), context);
+
+            // catch children invoices and populate the parent summary invoice
+            if (isChildrenAccountAndPaymentDelegated(account)) {
+                dispatcher.processParentInvoiceForInvoiceGeneration(account, event.getInvoiceId(), context);
+            }
+
+        } catch (InvoiceApiException e) {
+            log.error(e.getMessage());
+        } catch (AccountApiException e) {
+            log.error(e.getMessage());
+        }
+    }
+
+    private boolean isChildrenAccountAndPaymentDelegated(final ImmutableAccountData account) {
+        return account.getParentAccountId() != null && account.isPaymentDelegatedToParent();
+    }
+
+    public void handleParentInvoiceCommitmentEvent(final UUID invoiceId, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+        try {
+            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "Commit Invoice", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
+            invoiceApi.commitInvoice(invoiceId, context);
+        } catch (InvoiceApiException e) {
+            log.error(e.getMessage());
+        }
+    }
+
+    @AllowConcurrentEvents
+    @Subscribe
+    public void handleChildrenInvoiceAdjustmentEvent(final DefaultInvoiceAdjustmentEvent event) {
+
+        try {
+            final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "AdjustParentInvoice", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+            // TODO it may change to Account - #459
+            final ImmutableAccountData account = accountApi.getImmutableAccountDataById(event.getAccountId(), context);
+
+            // catch children invoices and populate the parent summary invoice
+            if (isChildrenAccountAndPaymentDelegated(account)) {
+                dispatcher.processParentInvoiceForAdjustments(account, event.getInvoiceId(), context);
+            }
+
+        } catch (InvoiceApiException e) {
+            log.error(e.getMessage());
+        } catch (AccountApiException e) {
+            log.error(e.getMessage());
+        }
+    }
+
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java
index d05b13a..82c62f5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoicePluginDispatcher.java
@@ -45,6 +45,7 @@ public class InvoicePluginDispatcher {
 
     private static final Collection<InvoiceItemType> ALLOWED_INVOICE_ITEM_TYPES = ImmutableList.<InvoiceItemType>of(InvoiceItemType.EXTERNAL_CHARGE,
                                                                                                                     InvoiceItemType.ITEM_ADJ,
+                                                                                                                    InvoiceItemType.CREDIT_ADJ,
                                                                                                                     InvoiceItemType.TAX);
 
     private final OSGIServiceRegistration<InvoicePluginApi> pluginRegistry;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
index facb7ba..3d20a84 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceTagHandler.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -18,18 +20,16 @@ package org.killbill.billing.invoice;
 
 import java.util.UUID;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.events.ControlTagDeletionInternalEvent;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.clock.Clock;
-import org.killbill.billing.events.ControlTagDeletionInternalEvent;
 import org.killbill.billing.util.tag.ControlTagType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.common.eventbus.AllowConcurrentEvents;
 import com.google.common.eventbus.Subscribe;
@@ -39,15 +39,12 @@ public class InvoiceTagHandler {
 
     private static final Logger log = LoggerFactory.getLogger(InvoiceTagHandler.class);
 
-    private final Clock clock;
     private final InvoiceDispatcher dispatcher;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
-    public InvoiceTagHandler(final Clock clock,
-                             final InvoiceDispatcher dispatcher,
+    public InvoiceTagHandler(final InvoiceDispatcher dispatcher,
                              final InternalCallContextFactory internalCallContextFactory) {
-        this.clock = clock;
         this.dispatcher = dispatcher;
         this.internalCallContextFactory = internalCallContextFactory;
     }
@@ -55,7 +52,6 @@ public class InvoiceTagHandler {
     @AllowConcurrentEvents
     @Subscribe
     public void process_AUTO_INVOICING_OFF_removal(final ControlTagDeletionInternalEvent event) {
-
         if (event.getTagDefinition().getName().equals(ControlTagType.AUTO_INVOICING_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
             final UUID accountId = event.getObjectId();
             final InternalCallContext context = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "InvoiceTagHandler", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
@@ -65,8 +61,8 @@ public class InvoiceTagHandler {
 
     private void processUnpaid_AUTO_INVOICING_OFF_invoices(final UUID accountId, final InternalCallContext context) {
         try {
-            dispatcher.processAccount(accountId, clock.getUTCNow(), null, context);
-        } catch (InvoiceApiException e) {
+            dispatcher.processAccount(accountId, null, null, context);
+        } catch (final InvoiceApiException e) {
             log.warn("Failed to process tag removal AUTO_INVOICING_OFF for accountId='{}'", accountId, e);
         }
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
index aa9938a..597c4a1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/CreditAdjInvoiceItem.java
@@ -29,19 +29,15 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.util.UUIDs;
 
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
 
 public class CreditAdjInvoiceItem extends AdjInvoiceItem {
 
     public CreditAdjInvoiceItem(final UUID invoiceId, final UUID accountId, final LocalDate date,
-                                final BigDecimal amount, final Currency currency) {
-        this(UUIDs.randomUUID(), null, invoiceId, accountId, date, amount, currency);
+                                @Nullable final String description, final BigDecimal amount, final Currency currency) {
+        this(UUIDs.randomUUID(), null, invoiceId, accountId, date, description, amount, currency);
     }
 
-    public CreditAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
-                                final BigDecimal amount, final Currency currency) {
-        this(id, createdDate, invoiceId, accountId, date, null, amount, currency);
-    }
 
     public CreditAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate date,
                                 @Nullable final String description, final BigDecimal amount, final Currency currency) {
@@ -55,6 +51,6 @@ public class CreditAdjInvoiceItem extends AdjInvoiceItem {
 
     @Override
     public String getDescription() {
-        return Objects.firstNonNull(description, "Invoice adjustment");
+        return MoreObjects.firstNonNull(description, "Invoice adjustment");
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index 514e368..6db52ff 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -26,12 +26,12 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
 import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -54,17 +54,23 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     private final boolean isWrittenOff;
 
     private final Currency processedCurrency;
-
+    private final InvoiceStatus status;
+    private final boolean isParentInvoice;
+    private final Invoice parentInvoice;
 
 
     // Used to create a new invoice
     public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+        this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED);
+    }
+
+    public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final InvoiceStatus status) {
+        this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, status);
     }
 
     public DefaultInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
-                          final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
-        this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false);
+                          final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice, final InvoiceStatus status) {
+        this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false, status, false, null);
     }
 
 
@@ -72,7 +78,9 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     public DefaultInvoice(final InvoiceModelDao invoiceModelDao) {
         this(invoiceModelDao.getId(), invoiceModelDao.getCreatedDate(), invoiceModelDao.getAccountId(),
              invoiceModelDao.getInvoiceNumber(), invoiceModelDao.getInvoiceDate(), invoiceModelDao.getTargetDate(),
-             invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(), invoiceModelDao.isWrittenOff());
+             invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(),
+             invoiceModelDao.isWrittenOff(), invoiceModelDao.getStatus(), invoiceModelDao.isParentInvoice(),
+             invoiceModelDao.getParentInvoice());
         addInvoiceItems(Collections2.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
             @Override
             public InvoiceItem apply(final InvoiceItemModelDao input) {
@@ -87,10 +95,16 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
         }));
     }
 
+    // Used to create a new parent invoice
+    public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final Currency currency) {
+        this(UUID.randomUUID(), null, accountId, null, invoiceDate, null, currency, currency, false, false, InvoiceStatus.DRAFT, true, null);
+    }
+
     private DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
-                          @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
-                          final LocalDate targetDate, final Currency currency, final Currency processedCurrency,
-                           final boolean isMigrationInvoice, final boolean isWrittenOff) {
+                           @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
+                           @Nullable final LocalDate targetDate, final Currency currency, final Currency processedCurrency,
+                           final boolean isMigrationInvoice, final boolean isWrittenOff,
+                           final InvoiceStatus status, final boolean isParentInvoice, final InvoiceModelDao parentInvoice) {
         super(invoiceId, createdDate, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
@@ -102,13 +116,17 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
         this.isWrittenOff = isWrittenOff;
         this.invoiceItems = new ArrayList<InvoiceItem>();
         this.payments = new ArrayList<InvoicePayment>();
+        this.status = status;
+        this.isParentInvoice = isParentInvoice;
+        this.parentInvoice = (parentInvoice != null) ? new DefaultInvoice(parentInvoice) : null;
     }
 
 
     // Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
     @Override
     public Object clone() {
-        final Invoice clonedInvoice = new DefaultInvoice(getId(),  getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff());
+        InvoiceModelDao parentInvoiceModelDao = (parentInvoice != null) ? new InvoiceModelDao(parentInvoice) : null;
+        final Invoice clonedInvoice = new DefaultInvoice(getId(),  getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff(), getStatus(), isParentInvoice(), parentInvoiceModelDao);
         clonedInvoice.getInvoiceItems().addAll(getInvoiceItems());
         clonedInvoice.getPayments().addAll(getPayments());
         return clonedInvoice;
@@ -231,7 +249,13 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
 
     @Override
     public BigDecimal getBalance() {
-        return isWrittenOff ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+        return getStatus().equals(InvoiceStatus.DRAFT) || hasZeroParentBalance() ?
+               BigDecimal.ZERO :
+               InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments, isWrittenOff() || isMigrationInvoice());
+    }
+
+    private boolean hasZeroParentBalance() {
+        return (parentInvoice != null) && (parentInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
     }
 
     public boolean isWrittenOff() {
@@ -239,8 +263,20 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     }
 
     @Override
+    public InvoiceStatus getStatus() {
+        return status;
+    }
+
+    @Override
+    public boolean isParentInvoice() {
+        return isParentInvoice;
+    }
+
+    @Override
     public String toString() {
-        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + "]";
+        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId
+               + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount()
+               + ", status=" + status + ", isParentInvoice=" + isParentInvoice + "]";
     }
 
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemBase.java
index f143825..3f9b13b 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemBase.java
@@ -36,6 +36,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     /* Common to all items */
     protected final UUID invoiceId;
     protected final UUID accountId;
+    protected final UUID childAccountId;
     protected final LocalDate startDate;
     protected final LocalDate endDate;
     protected final BigDecimal amount;
@@ -80,30 +81,37 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     public InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
                            @Nullable final UUID subscriptionId, @Nullable final String description, @Nullable final String planName, @Nullable final String phaseName, @Nullable final String usageName,
                            final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency) {
-        this(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, null, currency, null);
+        this(id, createdDate, invoiceId, accountId, null, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, null, currency, null);
     }
 
     // With rate but no reversing item
     public InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
                            @Nullable final UUID subscriptionId, @Nullable final String description, @Nullable final String planName, @Nullable final String phaseName, @Nullable final String usageName,
                            final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final BigDecimal rate, final Currency currency) {
-        this(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, rate, currency, null);
+        this(id, createdDate, invoiceId, accountId, null, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, rate, currency, null);
     }
 
     // With  reversing item, no rate
     public InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
                            @Nullable final UUID subscriptionId, @Nullable final String description, @Nullable final String planName, @Nullable final String phaseName, @Nullable final String usageName,
                            final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final Currency currency, final UUID reversedItemId) {
-        this(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, null, currency, reversedItemId);
+        this(id, createdDate, invoiceId, accountId, null, bundleId, subscriptionId, description, planName, phaseName, usageName, startDate, endDate, amount, null, currency, reversedItemId);
     }
 
-    private InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID bundleId,
+    // For parent invoices
+    public InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final UUID childAccountId,
+                             final BigDecimal amount, final Currency currency, final String description) {
+        this(id, createdDate, invoiceId, accountId, childAccountId, null, null, description, null, null, null, null, null, amount, null, currency, null);
+    }
+
+    private InvoiceItemBase(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, @Nullable final UUID childAccountId, @Nullable final UUID bundleId,
                             @Nullable final UUID subscriptionId, @Nullable final String description, @Nullable final String planName, @Nullable final String phaseName, @Nullable final String usageName,
-                            final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final BigDecimal rate, final Currency currency,
+                            @Nullable final LocalDate startDate, final LocalDate endDate, final BigDecimal amount, final BigDecimal rate, final Currency currency,
                             final UUID reversedItemId) {
         super(id, createdDate, createdDate);
         this.invoiceId = invoiceId;
         this.accountId = accountId;
+        this.childAccountId = childAccountId;
         this.subscriptionId = subscriptionId;
         this.bundleId = bundleId;
         this.description = description;
@@ -184,6 +192,11 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
     }
 
     @Override
+    public UUID getChildAccountId() {
+        return childAccountId;
+    }
+
+    @Override
     public boolean equals(final Object o) {
 
         if (!matches(o)) {
@@ -219,6 +232,9 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
         if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
+        if (childAccountId != null ? !childAccountId.equals(that.childAccountId) : that.childAccountId != null) {
+            return false;
+        }
         if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
             return false;
         }
@@ -254,6 +270,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem 
         int result = super.hashCode();
         result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (childAccountId != null ? childAccountId.hashCode() : 0);
         result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
         result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
index 77fd9e3..c4b0f5a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/InvoiceItemFactory.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -41,6 +41,7 @@ public class InvoiceItemFactory {
         final DateTime createdDate = invoiceItemModelDao.getCreatedDate();
         final UUID invoiceId = invoiceItemModelDao.getInvoiceId();
         final UUID accountId = invoiceItemModelDao.getAccountId();
+        final UUID childAccountId = invoiceItemModelDao.getChildAccountId();
         final UUID bundleId = invoiceItemModelDao.getBundleId();
         final UUID subscriptionId = invoiceItemModelDao.getSubscriptionId();
         final String planName = invoiceItemModelDao.getPlanName();
@@ -72,9 +73,6 @@ public class InvoiceItemFactory {
             case CREDIT_ADJ:
                 item = new CreditAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, description, amount, currency);
                 break;
-            case REFUND_ADJ:
-                item = new RefundAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, description, amount, currency);
-                break;
             case REPAIR_ADJ:
                 item = new RepairAdjInvoiceItem(id, createdDate, invoiceId, accountId, startDate, endDate, description, amount, currency, linkedItemId);
                 break;
@@ -87,6 +85,9 @@ public class InvoiceItemFactory {
             case TAX:
                 item = new TaxInvoiceItem(id, createdDate, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, usageName, startDate, description, amount, currency, linkedItemId);
                 break;
+            case PARENT_SUMMARY:
+                item = new ParentInvoiceItem(id, createdDate, invoiceId, accountId, childAccountId, amount, currency, description);
+                break;
             default:
                 throw new RuntimeException("Unexpected type of event item " + type);
         }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
index ff2387a..13656ed 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/ItemAdjInvoiceItem.java
@@ -34,16 +34,12 @@ import com.google.common.base.Objects;
 
 public class ItemAdjInvoiceItem extends AdjInvoiceItem {
 
-    public ItemAdjInvoiceItem(final InvoiceItem invoiceItem, final LocalDate effectiveDate,
+    public ItemAdjInvoiceItem(final InvoiceItem linkedInvoiceItem, final LocalDate effectiveDate,
                               final BigDecimal amount, final Currency currency) {
-        this(UUIDs.randomUUID(), invoiceItem.getInvoiceId(), invoiceItem.getAccountId(), effectiveDate,
-             amount, currency, invoiceItem.getId());
+        this(UUIDs.randomUUID(), null, linkedInvoiceItem.getInvoiceId(), linkedInvoiceItem.getAccountId(), effectiveDate,
+             linkedInvoiceItem.getDescription(), amount, currency, linkedInvoiceItem.getId());
     }
 
-    public ItemAdjInvoiceItem(final UUID id, final UUID invoiceId, final UUID accountId, final LocalDate startDate,
-                              final BigDecimal amount, final Currency currency, final UUID linkedItemId) {
-        this(id, null, invoiceId, accountId, startDate, null, amount, currency, linkedItemId);
-    }
 
     public ItemAdjInvoiceItem(final UUID id, @Nullable final DateTime createdDate, final UUID invoiceId, final UUID accountId, final LocalDate startDate,
                               @Nullable final String description, final BigDecimal amount, final Currency currency, final UUID linkedItemId) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java b/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
index 0763e30..15591d0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/UsageInvoiceItem.java
@@ -28,7 +28,7 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.util.UUIDs;
 
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
 
 public class UsageInvoiceItem extends InvoiceItemBase {
 
@@ -51,6 +51,6 @@ public class UsageInvoiceItem extends InvoiceItemBase {
 
     @Override
     public String getDescription() {
-        return Objects.firstNonNull(description, String.format("%s (usage item)", usageName));
+        return MoreObjects.firstNonNull(description, usageName);
     }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
index 05e661f..644cec3 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDateNotifier.java
@@ -25,7 +25,7 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.notificationq.api.NotificationEvent;
 import org.killbill.notificationq.api.NotificationQueue;
 import org.killbill.notificationq.api.NotificationQueueService;
@@ -44,7 +44,6 @@ public class DefaultNextBillingDateNotifier implements NextBillingDateNotifier {
     public static final String NEXT_BILLING_DATE_NOTIFIER_QUEUE = "next-billing-date-queue";
 
     private final NotificationQueueService notificationQueueService;
-    private final InvoiceConfig config;
     private final SubscriptionBaseInternalApi subscriptionApi;
     private final InvoiceListener listener;
     private final InternalCallContextFactory callContextFactory;
@@ -53,12 +52,10 @@ public class DefaultNextBillingDateNotifier implements NextBillingDateNotifier {
 
     @Inject
     public DefaultNextBillingDateNotifier(final NotificationQueueService notificationQueueService,
-                                          final InvoiceConfig config,
                                           final SubscriptionBaseInternalApi subscriptionApi,
                                           final InvoiceListener listener,
                                           final InternalCallContextFactory callContextFactory) {
         this.notificationQueueService = notificationQueueService;
-        this.config = config;
         this.subscriptionApi = subscriptionApi;
         this.listener = listener;
         this.callContextFactory = callContextFactory;
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 955801c..9014bdb 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
@@ -26,7 +26,6 @@ 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.AccountDateAndTimeZoneContext;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 import org.killbill.notificationq.api.NotificationEventWithMetadata;
 import org.killbill.notificationq.api.NotificationQueue;
@@ -52,18 +51,20 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
 
     @Override
     public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                             final UUID subscriptionId, final DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
-        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, accountDateAndTimeZoneContext, internalCallContext);
+                                                             final UUID subscriptionId, final DateTime futureNotificationTime, final InternalCallContext internalCallContext) {
+        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, internalCallContext);
     }
 
     @Override
     public void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                                   final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
-        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, accountDateAndTimeZoneContext, internalCallContext);
+                                                                   final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext) {
+        insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, internalCallContext);
     }
 
-    private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                          final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+    private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+                                                          final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification,
+                                                          final DateTime futureNotificationTime, final DateTime targetDate,
+                                                          final InternalCallContext internalCallContext) {
         final NotificationQueue nextBillingQueue;
         try {
             nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
@@ -77,8 +78,8 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
                     final boolean isEventDryRunForNotifications = input.getEvent().isDryRunForInvoiceNotification() != null ?
                                                                   input.getEvent().isDryRunForInvoiceNotification() : false;
 
-                    final LocalDate notificationEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(futureNotificationTime);
-                    final LocalDate eventEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(input.getEffectiveDate());
+                    final LocalDate notificationEffectiveLocaleDate = internalCallContext.toLocalDate(futureNotificationTime);
+                    final LocalDate eventEffectiveLocaleDate = internalCallContext.toLocalDate(input.getEffectiveDate());
 
                     return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0 &&
                            ((isDryRunForInvoiceNotification && isEventDryRunForNotifications) ||
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 011dc35..96836bd 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -22,15 +22,13 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
 
 public interface NextBillingDatePoster {
 
     void insertNextBillingNotificationFromTransaction(EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, UUID accountId,
-                                                      UUID subscriptionId, DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, InternalCallContext internalCallContext);
+                                                      UUID subscriptionId, DateTime futureNotificationTime, InternalCallContext internalCallContext);
 
     void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
-                                                            final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext);
-
+                                                            final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final InternalCallContext internalCallContext);
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java
new file mode 100644
index 0000000..a59e51b
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentNotifier.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.invoice.notification;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.invoice.InvoiceListener;
+import org.killbill.billing.invoice.api.DefaultInvoiceService;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
+import org.killbill.notificationq.api.NotificationEvent;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueAlreadyExists;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+
+public class ParentInvoiceCommitmentNotifier implements NextBillingDateNotifier {
+
+    private static final Logger log = LoggerFactory.getLogger(ParentInvoiceCommitmentNotifier.class);
+
+    public static final String PARENT_INVOICE_COMMITMENT_NOTIFIER_QUEUE = "parent-invoice-commitment-queue";
+
+    private final NotificationQueueService notificationQueueService;
+    private final InvoiceListener listener;
+
+    private NotificationQueue commitInvoiceQueue;
+
+    @Inject
+    public ParentInvoiceCommitmentNotifier(final NotificationQueueService notificationQueueService,
+                                           final InvoiceListener listener) {
+        this.notificationQueueService = notificationQueueService;
+        this.listener = listener;
+    }
+
+    @Override
+    public void initialize() throws NotificationQueueAlreadyExists {
+
+        final NotificationQueueHandler notificationQueueHandler = new NotificationQueueHandler() {
+            @Override
+            public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDate, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+                try {
+                    if (!(notificationKey instanceof ParentInvoiceCommitmentNotificationKey)) {
+                        log.error("Invoice service received an unexpected event type {}", notificationKey.getClass().getName());
+                        return;
+                    }
+
+                    final ParentInvoiceCommitmentNotificationKey key = (ParentInvoiceCommitmentNotificationKey) notificationKey;
+
+                    listener.handleParentInvoiceCommitmentEvent(key.getUuidKey(), userToken, accountRecordId, tenantRecordId);
+
+                } catch (IllegalArgumentException e) {
+                    log.error("The key returned from the ParentInvoiceCommitmentQueue is not a valid UUID", e);
+                }
+            }
+        };
+
+        commitInvoiceQueue = notificationQueueService.createNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
+                                                                              PARENT_INVOICE_COMMITMENT_NOTIFIER_QUEUE,
+                                                                              notificationQueueHandler);
+    }
+
+    @Override
+    public void start() {
+        commitInvoiceQueue.startQueue();
+    }
+
+    @Override
+    public void stop() throws NoSuchNotificationQueue {
+        if (commitInvoiceQueue != null) {
+            commitInvoiceQueue.stopQueue();
+            notificationQueueService.deleteNotificationQueue(commitInvoiceQueue.getServiceName(), commitInvoiceQueue.getQueueName());
+        }
+    }
+
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java
new file mode 100644
index 0000000..afd8816
--- /dev/null
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/ParentInvoiceCommitmentPoster.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 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.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 ParentInvoiceCommitmentPoster {
+
+    private static final Logger log = LoggerFactory.getLogger(ParentInvoiceCommitmentPoster.class);
+
+    private final NotificationQueueService notificationQueueService;
+
+    @Inject
+    public ParentInvoiceCommitmentPoster(final NotificationQueueService notificationQueueService) {
+        this.notificationQueueService = notificationQueueService;
+    }
+
+    public void insertParentInvoiceFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory,
+                                                           final UUID invoiceId,
+                                                           final DateTime futureNotificationTime,
+                                                           final InternalCallContext internalCallContext) {
+        final NotificationQueue commitInvoiceQueue;
+        try {
+            commitInvoiceQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
+                                                                               ParentInvoiceCommitmentNotifier.PARENT_INVOICE_COMMITMENT_NOTIFIER_QUEUE);
+
+            // If we see existing notification for the same date we don't insert a new notification
+            final List<NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey>> futureNotifications = commitInvoiceQueue.getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection());
+            final NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey> existingFutureNotificationWithSameDate = Iterables.tryFind(futureNotifications, new Predicate<NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey>>() {
+                @Override
+                public boolean apply(final NotificationEventWithMetadata<ParentInvoiceCommitmentNotificationKey> input) {
+
+                    final LocalDate notificationEffectiveLocaleDate = internalCallContext.toLocalDate(futureNotificationTime);
+                    final LocalDate eventEffectiveLocaleDate = internalCallContext.toLocalDate(input.getEffectiveDate());
+
+                    return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0;
+                }
+            }).orNull();
+
+            if (existingFutureNotificationWithSameDate == null) {
+                log.info("Queuing parent invoice commitment notification at {} for invoiceId {}", futureNotificationTime.toString(), invoiceId.toString());
+
+                commitInvoiceQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getHandle().getConnection(), futureNotificationTime,
+                                                                         new ParentInvoiceCommitmentNotificationKey(invoiceId), internalCallContext.getUserToken(),
+                                                                         internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
+            } else if (log.isDebugEnabled()) {
+                log.debug("*********************   SKIPPING Queuing parent invoice commitment notification at {} for invoiceId {} *******************", futureNotificationTime.toString(), invoiceId.toString());
+            }
+
+        } catch (final NoSuchNotificationQueue e) {
+            log.error("Attempting to put items on a non-existent queue (ParentInvoiceCommitmentNotifier).", e);
+        } catch (final IOException e) {
+            log.error("Failed to serialize notificationKey for invoiceId {}", invoiceId);
+        }
+    }
+
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 6f38c8f..44f2a30 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -26,7 +26,6 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.UUID;
 
 import org.joda.money.CurrencyUnit;
@@ -44,6 +43,7 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
 import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
@@ -71,11 +71,10 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
     private final CurrencyConversionApi currencyConversionApi;
     private final InternalTenantContext context;
     private final ResourceBundleFactory bundleFactory;
-    private final Map<java.util.Currency, Locale> currencyLocaleMap;
 
     public DefaultInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale,
                                    final CurrencyConversionApi currencyConversionApi, final ResourceBundleFactory bundleFactory,
-                                   final InternalTenantContext context, final Map<java.util.Currency, Locale> currencyLocaleMap) {
+                                   final InternalTenantContext context) {
         this.config = config;
         this.invoice = invoice;
         this.dateFormatter = DateTimeFormat.mediumDate().withLocale(locale);
@@ -83,7 +82,6 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         this.currencyConversionApi = currencyConversionApi;
         this.bundleFactory = bundleFactory;
         this.context = context;
-        this.currencyLocaleMap = currencyLocaleMap;
     }
 
     @Override
@@ -102,10 +100,8 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
                 // Merge CBA items to avoid confusing the customer, since these are internal
                 // adjustments (auto generated)
                 mergedCBAItem = mergeCBAItem(invoiceItems, mergedCBAItem, item);
-            } else if (InvoiceItemType.REFUND_ADJ.equals(item.getInvoiceItemType()) ||
-                       InvoiceItemType.CREDIT_ADJ.equals(item.getInvoiceItemType())) {
-                // Merge refund adjustments and credit adjustments, as these are both
-                // the same for the customer (invoice adjustment)
+            } else if (InvoiceItemType.CREDIT_ADJ.equals(item.getInvoiceItemType())) {
+                // Merge credit adjustments, as these are both the same for the customer (invoice adjustment)
                 mergedInvoiceAdjustment = mergeInvoiceAdjustmentItem(invoiceItems, mergedInvoiceAdjustment, item);
             } else {
                 invoiceItems.add(item);
@@ -149,7 +145,7 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
             if (!mergedInvoiceAdjustment.getCurrency().equals(item.getCurrency())) {
                 invoiceItems.add(item);
             } else {
-                mergedInvoiceAdjustment = new CreditAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), invoice.getInvoiceDate(),
+                mergedInvoiceAdjustment = new CreditAdjInvoiceItem(invoice.getId(), invoice.getAccountId(), invoice.getInvoiceDate(), mergedInvoiceAdjustment.getDescription(),
                                                                    mergedInvoiceAdjustment.getAmount().add(item.getAmount()), mergedInvoiceAdjustment.getCurrency());
             }
         }
@@ -241,8 +237,8 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         dfs.setInternationalCurrencySymbol(currencyUnit.getCurrencyCode());
 
         try {
-            final java.util.Currency currency = java.util.Currency.getInstance(invoiceCurrencyCode);
-            dfs.setCurrencySymbol(currency.getSymbol(currencyLocaleMap.get(currency)));
+            Currency currency = Currency.fromCode(invoiceCurrencyCode);
+            dfs.setCurrencySymbol(currency.getSymbol());
         } catch (final IllegalArgumentException e) {
             dfs.setCurrencySymbol(currencyUnit.getSymbol(locale));
         }
@@ -341,6 +337,16 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
         return invoice.getUpdatedDate();
     }
 
+    @Override
+    public InvoiceStatus getStatus() {
+        return invoice.getStatus();
+    }
+
+    @Override
+    public boolean isParentInvoice() {
+        return invoice.isParentInvoice();
+    }
+
     // Expose the fields for children classes. This is useful for further customization of the invoices
 
     @SuppressWarnings("UnusedDeclaration")
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
index b9e405b..0ed4bfc 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatterFactory.java
@@ -16,10 +16,7 @@
 
 package org.killbill.billing.invoice.template.formatters;
 
-import java.util.Currency;
-import java.util.HashMap;
 import java.util.Locale;
-import java.util.Map;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.currency.api.CurrencyConversionApi;
@@ -29,31 +26,15 @@ import org.killbill.billing.invoice.api.formatters.InvoiceFormatterFactory;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
 import org.killbill.billing.util.template.translation.TranslatorConfig;
 
-import com.google.common.annotations.VisibleForTesting;
-
 public class DefaultInvoiceFormatterFactory implements InvoiceFormatterFactory {
 
-    private final Map<Currency, Locale> currencyLocaleMap = new HashMap<Currency, Locale>();
-
     public DefaultInvoiceFormatterFactory() {
-        // This initialization relies on System.currentTimeMillis() instead of the Kill Bill clock (it won't be accurate when moving the clock)
-        for (final Locale localeItem : Locale.getAvailableLocales()) {
-            try {
-                final java.util.Currency currency = java.util.Currency.getInstance(localeItem);
-                currencyLocaleMap.put(currency, localeItem);
-            } catch (final Exception ignored) {
-            }
-        }
     }
 
     @Override
     public InvoiceFormatter createInvoiceFormatter(final TranslatorConfig config, final Invoice invoice, final Locale locale, final CurrencyConversionApi currencyConversionApi,
                                                    final ResourceBundleFactory bundleFactory, final InternalTenantContext context) {
-        return new DefaultInvoiceFormatter(config, invoice, locale, currencyConversionApi, bundleFactory, context, currencyLocaleMap);
+        return new DefaultInvoiceFormatter(config, invoice, locale, currencyConversionApi, bundleFactory, context);
     }
 
-    @VisibleForTesting
-    Map<Currency, Locale> getCurrencyLocaleMap() {
-        return currencyLocaleMap;
-    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index c6185a9..cbcc8c6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -123,6 +123,11 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
     }
 
     @Override
+    public UUID getChildAccountId() {
+        return item.getChildAccountId();
+    }
+
+    @Override
     public UUID getBundleId() {
         return item.getBundleId();
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/AccountItemTree.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/AccountItemTree.java
index 1f6ec8d..def85b1 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/AccountItemTree.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/AccountItemTree.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2014 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -104,7 +104,6 @@ public class AccountItemTree {
             case TAX:
             case CBA_ADJ:
             case CREDIT_ADJ:
-            case REFUND_ADJ:
             case USAGE:
                 return;
 
@@ -113,6 +112,8 @@ public class AccountItemTree {
             case FIXED:
             case ITEM_ADJ:
                 break;
+            case PARENT_SUMMARY:
+                break;
 
             default:
                 Preconditions.checkState(false, "Unknown invoice item type " + existingItem.getInvoiceItemType());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
index 48e946a..e3f9af0 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/tree/ItemsNodeInterval.java
@@ -21,6 +21,7 @@ package org.killbill.billing.invoice.tree;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -282,18 +283,19 @@ public class ItemsNodeInterval extends NodeInterval {
     //   it has no more leaves and no more items.
     // Case B - This is a bit more involved: We look for full repair that happened in pieces; this will translate to an ADD element of a NodeInterval,
     // whose children completely map the interval (isPartitionedByChildren) and where each child will have a CANCEL item pointing to the ADD.
-    // When we detect such nodes, we delete both the ADD in the parent interval and the CANCEL in the children
+    // When we detect such nodes, we delete both the ADD in the parent interval and the CANCEL in the children (and cleanup the interval if it does not have items)
     //
     private void pruneTree() {
         walkTree(new WalkCallback() {
             @Override
             public void onCurrentNode(final int depth, final NodeInterval curNode, final NodeInterval parent) {
 
-                if(curNode.isRoot()) {
+                if (curNode.isRoot()) {
                     return;
                 }
 
                 final ItemsInterval curNodeItems = ((ItemsNodeInterval) curNode).getItemsInterval();
+
                 // Case A:
                 final boolean isEmpty = curNodeItems.mergeCancellingPairs();
                 if (isEmpty && curNode.getLeftChild() == null) {
@@ -305,14 +307,22 @@ public class ItemsNodeInterval extends NodeInterval {
                 }
 
                 // Case B -- look for such case, and if found (foundFullRepairByParts) we fix them below.
-                final Iterator<Item> it =  curNodeItems.get_ADD_items().iterator();
+                List<Item> curNodeItemsToBeRemoved = null;
+                final Iterator<Item> it = curNodeItems.get_ADD_items().iterator();
+                // For each item on this curNode interval we check if there is a matching set of CANCEL items on the children (resulting in completely cancelling that item).
                 while (it.hasNext()) {
 
                     final Item curAddItem = it.next();
 
+                    //
+                    // We already know the children partition fully the 'curNode' interval, we just need to see if for each piece
+                    // we find a matching CANCEL item pointing to this 'curAddItem'
+                    //
                     NodeInterval curChild = curNode.getLeftChild();
-                    Map<ItemsInterval, Item> toBeRemoved = new HashMap<ItemsInterval, Item>();
-                    boolean foundFullRepairByParts = true;
+                    Map<ItemsInterval, Item> childrenCancellingToBeRemoved = new HashMap<ItemsInterval, Item>();
+
+                    // Note that because of previous iterations, curChild could now be null so we need to initialize the foundFullRepairByParts based on that new state.
+                    boolean foundFullRepairByParts = curChild != null;
                     while (curChild != null) {
                         final ItemsInterval curChildItems = ((ItemsNodeInterval) curChild).getItemsInterval();
                         Item cancellingItem = curChildItems.getCancelledItemIfExists(curAddItem.getId());
@@ -320,20 +330,27 @@ public class ItemsNodeInterval extends NodeInterval {
                             foundFullRepairByParts = false;
                             break;
                         }
-                        toBeRemoved.put(curChildItems, cancellingItem);
+                        childrenCancellingToBeRemoved.put(curChildItems, cancellingItem);
                         curChild = curChild.getRightSibling();
                     }
 
                     if (foundFullRepairByParts) {
-                        for (ItemsInterval curItemsInterval : toBeRemoved.keySet()) {
-                            curItemsInterval.remove(toBeRemoved.get(curItemsInterval));
+                        for (ItemsInterval curItemsInterval : childrenCancellingToBeRemoved.keySet()) {
+                            curItemsInterval.remove(childrenCancellingToBeRemoved.get(curItemsInterval));
                             if (curItemsInterval.size() == 0) {
                                 curNode.removeChild(curItemsInterval.getNodeInterval());
                             }
                         }
-                        curNodeItems.remove(curAddItem);
+                        if (curNodeItemsToBeRemoved == null) {
+                            curNodeItemsToBeRemoved = new ArrayList<Item>();
+                        }
+                        curNodeItemsToBeRemoved.add(curAddItem);
                     }
                 }
+                // Finally Execute the removal of the curNodeItems outside of the upper while loop so as to not trigger ConcurrentModificationException (see #641)
+                for (Item curNodeItemsRemoval : curNodeItemsToBeRemoved) {
+                    curNodeItems.remove(curNodeItemsRemoval);
+                }
             }
         });
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
index 592f05e..65f5449 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -28,6 +29,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
@@ -41,7 +43,6 @@ import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
 import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -74,7 +75,7 @@ public class ContiguousIntervalConsumableInArrear {
     private final UUID invoiceId;
     private final AtomicBoolean isBuilt;
     private final LocalDate rawUsageStartDate;
-    private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+    private final InternalTenantContext internalTenantContext;
 
     public ContiguousIntervalConsumableInArrear(final Usage usage,
                                                 final UUID accountId,
@@ -82,7 +83,7 @@ public class ContiguousIntervalConsumableInArrear {
                                                 final List<RawUsage> rawSubscriptionUsage,
                                                 final LocalDate targetDate,
                                                 final LocalDate rawUsageStartDate,
-                                                final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
+                                                final InternalTenantContext internalTenantContext) {
         this.usage = usage;
         this.accountId = accountId;
         this.invoiceId = invoiceId;
@@ -90,7 +91,7 @@ public class ContiguousIntervalConsumableInArrear {
         this.rawSubscriptionUsage = rawSubscriptionUsage;
         this.targetDate = targetDate;
         this.rawUsageStartDate = rawUsageStartDate;
-        this.dateAndTimeZoneContext = dateAndTimeZoneContext;
+        this.internalTenantContext = internalTenantContext;
         this.billingEvents = Lists.newLinkedList();
         this.transitionTimes = Lists.newLinkedList();
         this.isBuilt = new AtomicBoolean(false);
@@ -104,7 +105,6 @@ public class ContiguousIntervalConsumableInArrear {
      *
      * @param closedInterval whether there was a last billing event referencing the usage section or whether this is ongoing and
      *                       then targetDate will define the endDate.
-     * @return
      */
     public ContiguousIntervalConsumableInArrear build(final boolean closedInterval) {
 
@@ -112,11 +112,11 @@ public class ContiguousIntervalConsumableInArrear {
         Preconditions.checkState((!closedInterval && billingEvents.size() >= 1) ||
                                  (closedInterval && billingEvents.size() >= 2));
 
-        final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(0).getEffectiveDate());
+        final LocalDate startDate = internalTenantContext.toLocalDate(billingEvents.get(0).getEffectiveDate());
         if (targetDate.isBefore(startDate)) {
             return this;
         }
-        final LocalDate endDate = closedInterval ? dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(billingEvents.size() - 1).getEffectiveDate()) : targetDate;
+        final LocalDate endDate = closedInterval ? internalTenantContext.toLocalDate(billingEvents.get(billingEvents.size() - 1).getEffectiveDate()) : targetDate;
 
         final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, getBCD(), usage.getBillingPeriod(), usage.getBillingMode());
 
@@ -142,30 +142,10 @@ public class ContiguousIntervalConsumableInArrear {
         return this;
     }
 
-    public class ConsumableInArrearItemsAndNextNotificationDate {
-        private final List<InvoiceItem> invoiceItems;
-        private final LocalDate nextNotificationDate;
-
-        public ConsumableInArrearItemsAndNextNotificationDate(final List<InvoiceItem> invoiceItems, final LocalDate nextNotificationDate) {
-            this.invoiceItems = invoiceItems;
-            this.nextNotificationDate = nextNotificationDate;
-        }
-
-        public List<InvoiceItem> getInvoiceItems() {
-            return invoiceItems;
-        }
-
-        public LocalDate getNextNotificationDate() {
-            return nextNotificationDate;
-        }
-    }
-
-
     /**
      * Compute the missing usage invoice items based on what should be billed and what has been billed ($ amount comparison).
      *
      * @param existingUsage existing on disk usage items for the subscription
-     * @return
      * @throws CatalogApiException
      */
     public ConsumableInArrearItemsAndNextNotificationDate computeMissingItemsAndNextNotificationDate(final List<InvoiceItem> existingUsage) throws CatalogApiException {
@@ -182,10 +162,10 @@ public class ContiguousIntervalConsumableInArrear {
         // We start by generating 'marker' USAGE items with $0 that will allow to correctly insert the next notification for when there is no USAGE to bill.
         // Those will be removed by the invoicing code later so as to not end up with superfluous $0 items
         LocalDate prevDate = null;
-        for (LocalDate curDate : transitionTimes) {
+        for (final LocalDate curDate : transitionTimes) {
             if (prevDate != null) {
-                InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
-                                                        getPhaseName(), usage.getName(), prevDate, curDate, BigDecimal.ZERO, getCurrency());
+                final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
+                                                              getPhaseName(), usage.getName(), prevDate, curDate, BigDecimal.ZERO, getCurrency());
                 result.add(item);
             }
             prevDate = curDate;
@@ -212,8 +192,8 @@ public class ContiguousIntervalConsumableInArrear {
             if (!billedItems.iterator().hasNext() || billedUsage.compareTo(toBeBilledUsage) < 0) {
                 final BigDecimal amountToBill = toBeBilledUsage.subtract(billedUsage);
                 if (amountToBill.compareTo(BigDecimal.ZERO) > 0) {
-                    InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
-                                                            getPhaseName(), usage.getName(), ru.getStart(), ru.getEnd(), amountToBill, getCurrency());
+                    final InvoiceItem item = new UsageInvoiceItem(invoiceId, accountId, getBundleId(), getSubscriptionId(), getPlanName(),
+                                                                  getPhaseName(), usage.getName(), ru.getStart(), ru.getEnd(), amountToBill, getCurrency());
                     result.add(item);
                 }
             }
@@ -230,25 +210,23 @@ public class ContiguousIntervalConsumableInArrear {
         while (eventIt.hasNext()) {
             final BillingEvent thisEvent = nextEvent;
             nextEvent = eventIt.next();
-            final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
-            final LocalDate endDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+            final LocalDate startDate = internalTenantContext.toLocalDate(thisEvent.getEffectiveDate());
+            final LocalDate endDate = internalTenantContext.toLocalDate(nextEvent.getEffectiveDate());
 
             final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, thisEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR);
             final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate();
             result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result;
         }
 
-        final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
+        final LocalDate startDate = internalTenantContext.toLocalDate(nextEvent.getEffectiveDate());
         final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, null, targetDate, nextEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR);
         final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate();
         result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result;
         return result;
     }
 
-
     @VisibleForTesting
     List<RolledUpUsage> getRolledUpUsage() {
-
         final Iterator<RawUsage> rawUsageIterator = rawSubscriptionUsage.iterator();
         if (!rawUsageIterator.hasNext()) {
             return ImmutableList.of();
@@ -280,7 +258,7 @@ public class ContiguousIntervalConsumableInArrear {
         // matching RolledUpUsage for that interval, and we'll detect that in the 'computeMissingItems' logic
         //
         LocalDate prevDate = null;
-        for (LocalDate curDate : transitionTimes) {
+        for (final LocalDate curDate : transitionTimes) {
 
             if (prevDate != null) {
 
@@ -290,8 +268,8 @@ public class ContiguousIntervalConsumableInArrear {
                 // Start consuming prevRawUsage element if it exists and falls into the range
                 if (prevRawUsage != null) {
                     if (prevRawUsage.getDate().compareTo(prevDate) >= 0 && prevRawUsage.getDate().compareTo(curDate) < 0) {
-                        Long currentAmount = perRangeUnitToAmount.get(prevRawUsage.getUnitType());
-                        Long updatedAmount = (currentAmount != null) ? currentAmount + prevRawUsage.getAmount() : prevRawUsage.getAmount();
+                        final Long currentAmount = perRangeUnitToAmount.get(prevRawUsage.getUnitType());
+                        final Long updatedAmount = (currentAmount != null) ? currentAmount + prevRawUsage.getAmount() : prevRawUsage.getAmount();
                         perRangeUnitToAmount.put(prevRawUsage.getUnitType(), updatedAmount);
                         prevRawUsage = null;
                     }
@@ -311,8 +289,8 @@ public class ContiguousIntervalConsumableInArrear {
                             break;
                         }
 
-                        Long currentAmount = perRangeUnitToAmount.get(curRawUsage.getUnitType());
-                        Long updatedAmount = (currentAmount != null) ? currentAmount + curRawUsage.getAmount() : curRawUsage.getAmount();
+                        final Long currentAmount = perRangeUnitToAmount.get(curRawUsage.getUnitType());
+                        final Long updatedAmount = (currentAmount != null) ? currentAmount + curRawUsage.getAmount() : curRawUsage.getAmount();
                         perRangeUnitToAmount.put(curRawUsage.getUnitType(), updatedAmount);
                     }
                 }
@@ -345,7 +323,7 @@ public class ContiguousIntervalConsumableInArrear {
         BigDecimal result = BigDecimal.ZERO;
         final List<TieredBlock> tieredBlocks = getConsumableInArrearTieredBlocks(usage, unitType);
         int remainingUnits = nbUnits.intValue();
-        for (TieredBlock tieredBlock : tieredBlocks) {
+        for (final TieredBlock tieredBlock : tieredBlocks) {
 
             final int blockTierSize = tieredBlock.getSize().intValue();
             final int tmp = remainingUnits / blockTierSize + (remainingUnits % blockTierSize == 0 ? 0 : 1);
@@ -371,7 +349,7 @@ public class ContiguousIntervalConsumableInArrear {
 
         Preconditions.checkState(isBuilt.get());
         BigDecimal billedAmount = BigDecimal.ZERO;
-        for (InvoiceItem ii : filteredUsageForInterval) {
+        for (final InvoiceItem ii : filteredUsageForInterval) {
             billedAmount = billedAmount.add(ii.getAmount());
         }
         // Return the billed $ amount (not the # of units)
@@ -414,7 +392,6 @@ public class ContiguousIntervalConsumableInArrear {
         return billingEvents.get(0).getBillCycleDayLocal();
     }
 
-
     public UUID getBundleId() {
         return billingEvents.get(0).getSubscription().getBundleId();
     }
@@ -436,8 +413,33 @@ public class ContiguousIntervalConsumableInArrear {
         return billingEvents.get(0).getCurrency();
     }
 
-    public DateTimeZone getAccountTimeZone() {
-        return billingEvents.get(0).getTimeZone();
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ContiguousIntervalConsumableInArrear{");
+        sb.append("transitionTimes=").append(transitionTimes);
+        sb.append(", billingEvents=").append(billingEvents);
+        sb.append(", rawSubscriptionUsage=").append(rawSubscriptionUsage);
+        sb.append(", rawUsageStartDate=").append(rawUsageStartDate);
+        sb.append('}');
+        return sb.toString();
     }
 
+    public class ConsumableInArrearItemsAndNextNotificationDate {
+
+        private final List<InvoiceItem> invoiceItems;
+        private final LocalDate nextNotificationDate;
+
+        public ConsumableInArrearItemsAndNextNotificationDate(final List<InvoiceItem> invoiceItems, final LocalDate nextNotificationDate) {
+            this.invoiceItems = invoiceItems;
+            this.nextNotificationDate = nextNotificationDate;
+        }
+
+        public List<InvoiceItem> getInvoiceItems() {
+            return invoiceItems;
+        }
+
+        public LocalDate getNextNotificationDate() {
+            return nextNotificationDate;
+        }
+    }
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
index b78fcbc..1d3efeb 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -30,10 +30,11 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.generator.InvoiceDateUtils;
 import org.killbill.billing.invoice.model.UsageInvoiceItem;
 import org.killbill.billing.usage.InternalUserApi;
 import org.killbill.billing.usage.RawUsage;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,17 +67,15 @@ public class RawUsageOptimizer {
     }
 
     public RawUsageOptimizerResult getConsumableInArrearUsage(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
-        final LocalDate targetStartDate = config.getMaxRawUsagePreviousPeriod() > 0 ? getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, existingUsageItems, knownUsage) : firstEventStartDate;
-        log.info("ConsumableInArrear accountRecordId='{}', rawUsageStartDate='{}', firstEventStartDate='{}'",
-                 internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate);
-
+        final LocalDate targetStartDate = config.getMaxRawUsagePreviousPeriod(internalCallContext) > 0 ? getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, existingUsageItems, knownUsage, internalCallContext) : firstEventStartDate;
+        log.debug("ConsumableInArrear accountRecordId='{}', rawUsageStartDate='{}', firstEventStartDate='{}'",
+                  internalCallContext.getAccountRecordId(), targetStartDate, firstEventStartDate);
         final List<RawUsage> rawUsageData = usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext);
-        return new RawUsageOptimizerResult(firstEventStartDate, targetStartDate, rawUsageData);
+        return new RawUsageOptimizerResult(targetStartDate, rawUsageData);
     }
 
     @VisibleForTesting
-    LocalDate getOptimizedRawUsageStartDate(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage) {
-
+    LocalDate getOptimizedRawUsageStartDate(final LocalDate firstEventStartDate, final LocalDate targetDate, final Iterable<InvoiceItem> existingUsageItems, final Map<String, Usage> knownUsage, final InternalCallContext internalCallContext) {
         if (!existingUsageItems.iterator().hasNext()) {
             return firstEventStartDate;
 
@@ -99,9 +98,9 @@ public class RawUsageOptimizer {
         //
         final LocalDate[] perBillingPeriodMostRecentConsumableInArrearItemEndDate = new LocalDate[BillingPeriod.values().length - 1]; // Exclude the NO_BILLING_PERIOD
         int idx = 0;
-        for (BillingPeriod bp : BillingPeriod.values()) {
+        for (final BillingPeriod bp : BillingPeriod.values()) {
             if (bp != BillingPeriod.NO_BILLING_PERIOD) {
-                final LocalDate makerDateThanCannotBeChosenAsTheMinOfAllDates = targetDate.plusMonths(config.getMaxRawUsagePreviousPeriod() * bp.getNumberOfMonths());
+                final LocalDate makerDateThanCannotBeChosenAsTheMinOfAllDates = InvoiceDateUtils.advanceByNPeriods(targetDate, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext));
                 perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx++] = (knownUsageBillingPeriod.contains(bp)) ? null : makerDateThanCannotBeChosenAsTheMinOfAllDates;
             }
         }
@@ -124,10 +123,10 @@ public class RawUsageOptimizer {
         // Extract the min from all the dates
         LocalDate targetStartDate = null;
         idx = 0;
-        for (BillingPeriod bp : BillingPeriod.values()) {
+        for (final BillingPeriod bp : BillingPeriod.values()) {
             if (bp != BillingPeriod.NO_BILLING_PERIOD) {
                 final LocalDate tmp = perBillingPeriodMostRecentConsumableInArrearItemEndDate[idx];
-                final LocalDate targetBillingPeriodDate = tmp != null ? tmp.minusMonths(config.getMaxRawUsagePreviousPeriod() * bp.getNumberOfMonths()) : null;
+                final LocalDate targetBillingPeriodDate = tmp != null ? InvoiceDateUtils.recedeByNPeriods(tmp, bp, config.getMaxRawUsagePreviousPeriod(internalCallContext)) : null;
                 if (targetStartDate == null || (targetBillingPeriodDate != null && targetBillingPeriodDate.compareTo(targetStartDate) < 0)) {
                     targetStartDate = targetBillingPeriodDate;
                 }
@@ -152,20 +151,14 @@ public class RawUsageOptimizer {
 
     public static class RawUsageOptimizerResult {
 
-        private final LocalDate firstEventStartDate;
         private final LocalDate rawUsageStartDate;
         private final List<RawUsage> rawUsage;
 
-        public RawUsageOptimizerResult(final LocalDate firstEventStartDate, final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage) {
-            this.firstEventStartDate = firstEventStartDate;
+        public RawUsageOptimizerResult(final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage) {
             this.rawUsageStartDate = rawUsageStartDate;
             this.rawUsage = rawUsage;
         }
 
-        public LocalDate getFirstEventStartDate() {
-            return firstEventStartDate;
-        }
-
         public LocalDate getRawUsageStartDate() {
             return rawUsageStartDate;
         }
@@ -174,5 +167,4 @@ public class RawUsageOptimizer {
             return rawUsage;
         }
     }
-
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
index 6db0640..2f93a43 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -27,15 +28,16 @@ import java.util.Set;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.catalog.api.UsageType;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.generator.InvoiceItemGenerator.InvoiceItemGeneratorLogger;
 import org.killbill.billing.invoice.usage.ContiguousIntervalConsumableInArrear.ConsumableInArrearItemsAndNextNotificationDate;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
@@ -75,7 +77,7 @@ public class SubscriptionConsumableInArrear {
     private final LocalDate targetDate;
     private final List<RawUsage> rawSubscriptionUsage;
     private final LocalDate rawUsageStartDate;
-    private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+    private final InternalTenantContext internalTenantContext;
 
     public SubscriptionConsumableInArrear(final UUID accountId,
                                           final UUID invoiceId,
@@ -83,14 +85,14 @@ public class SubscriptionConsumableInArrear {
                                           final List<RawUsage> rawUsage,
                                           final LocalDate targetDate,
                                           final LocalDate rawUsageStartDate,
-                                          final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
+                                          final InternalTenantContext internalTenantContext) {
 
         this.accountId = accountId;
         this.invoiceId = invoiceId;
         this.subscriptionBillingEvents = subscriptionBillingEvents;
         this.targetDate = targetDate;
         this.rawUsageStartDate = rawUsageStartDate;
-        this.dateAndTimeZoneContext= dateAndTimeZoneContext;
+        this.internalTenantContext = internalTenantContext;
         // Extract raw usage for that subscription and sort it by date
         this.rawSubscriptionUsage = Ordering.<RawUsage>from(RAW_USAGE_DATE_COMPARATOR).sortedCopy(Iterables.filter(rawUsage, new Predicate<RawUsage>() {
             @Override
@@ -100,36 +102,35 @@ public class SubscriptionConsumableInArrear {
         }));
     }
 
-
     /**
      * Based on billing events, (@code existingUsage} and targetDate, figure out what remains to be billed.
      *
      * @param existingUsage the existing on disk usage items.
-     * @return
      * @throws CatalogApiException
      */
-    public SubscriptionConsumableInArrearItemsAndNextNotificationDate computeMissingUsageInvoiceItems(final List<InvoiceItem> existingUsage) throws CatalogApiException {
-
+    public SubscriptionConsumableInArrearItemsAndNextNotificationDate computeMissingUsageInvoiceItems(final List<InvoiceItem> existingUsage, final InvoiceItemGeneratorLogger invoiceItemGeneratorLogger) throws CatalogApiException {
         final SubscriptionConsumableInArrearItemsAndNextNotificationDate result = new SubscriptionConsumableInArrearItemsAndNextNotificationDate();
         final List<ContiguousIntervalConsumableInArrear> billingEventTransitionTimePeriods = computeInArrearUsageInterval();
-        for (ContiguousIntervalConsumableInArrear usageInterval : billingEventTransitionTimePeriods) {
-            result.addConsumableInArrearItemsAndNextNotificationDate(usageInterval.getUsage().getName(), usageInterval.computeMissingItemsAndNextNotificationDate(existingUsage));
+        for (final ContiguousIntervalConsumableInArrear usageInterval : billingEventTransitionTimePeriods) {
+            final ConsumableInArrearItemsAndNextNotificationDate newItemsAndDate = usageInterval.computeMissingItemsAndNextNotificationDate(existingUsage);
+
+            // For debugging purposes
+            invoiceItemGeneratorLogger.append(usageInterval, newItemsAndDate.getInvoiceItems());
+
+            result.addConsumableInArrearItemsAndNextNotificationDate(usageInterval.getUsage().getName(), newItemsAndDate);
         }
         return result;
     }
 
-
-
     @VisibleForTesting
     List<ContiguousIntervalConsumableInArrear> computeInArrearUsageInterval() {
-
         final List<ContiguousIntervalConsumableInArrear> usageIntervals = Lists.newLinkedList();
 
         final Map<String, ContiguousIntervalConsumableInArrear> inFlightInArrearUsageIntervals = new HashMap<String, ContiguousIntervalConsumableInArrear>();
 
         final Set<String> allSeenUsage = new HashSet<String>();
 
-        for (BillingEvent event : subscriptionBillingEvents) {
+        for (final BillingEvent event : subscriptionBillingEvents) {
 
             // Extract all in arrear /consumable usage section for that billing event.
             final List<Usage> usages = findConsumableInArrearUsages(event);
@@ -143,12 +144,12 @@ public class SubscriptionConsumableInArrear {
             // All inflight usage interval are candidates to be closed unless we see that current billing event referencing the same usage section.
             final Set<String> toBeClosed = new HashSet<String>(allSeenUsage);
 
-            for (Usage usage : usages) {
+            for (final Usage usage : usages) {
 
                 // Add inflight usage interval if non existent
                 ContiguousIntervalConsumableInArrear existingInterval = inFlightInArrearUsageIntervals.get(usage.getName());
                 if (existingInterval == null) {
-                    existingInterval = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, targetDate, rawUsageStartDate, dateAndTimeZoneContext);
+                    existingInterval = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, targetDate, rawUsageStartDate, internalTenantContext);
                     inFlightInArrearUsageIntervals.put(usage.getName(), existingInterval);
                 }
                 // Add billing event for that usage interval
@@ -158,7 +159,7 @@ public class SubscriptionConsumableInArrear {
             }
 
             // Build the usage interval that are no longer referenced
-            for (String usageName : toBeClosed) {
+            for (final String usageName : toBeClosed) {
                 final ContiguousIntervalConsumableInArrear interval = inFlightInArrearUsageIntervals.remove(usageName);
                 if (interval != null) {
                     interval.addBillingEvent(event);
@@ -166,20 +167,20 @@ public class SubscriptionConsumableInArrear {
                 }
             }
         }
-        for (String usageName : inFlightInArrearUsageIntervals.keySet()) {
+        for (final String usageName : inFlightInArrearUsageIntervals.keySet()) {
             usageIntervals.add(inFlightInArrearUsageIntervals.get(usageName).build(false));
         }
         inFlightInArrearUsageIntervals.clear();
         return usageIntervals;
     }
 
-    List<Usage> findConsumableInArrearUsages(final BillingEvent event) {
-        if (event.getUsages().size() == 0) {
+    private List<Usage> findConsumableInArrearUsages(final BillingEvent event) {
+        if (event.getUsages().isEmpty()) {
             return Collections.emptyList();
         }
 
         final List<Usage> result = Lists.newArrayList();
-        for (Usage usage : event.getUsages()) {
+        for (final Usage usage : event.getUsages()) {
             if (usage.getUsageType() != UsageType.CONSUMABLE ||
                 usage.getBillingMode() != BillingMode.IN_ARREAR) {
                 continue;
@@ -190,6 +191,7 @@ public class SubscriptionConsumableInArrear {
     }
 
     public class SubscriptionConsumableInArrearItemsAndNextNotificationDate {
+
         private List<InvoiceItem> invoiceItems;
         private Map<String, LocalDate> perUsageNotificationDates;
 
@@ -223,5 +225,4 @@ public class SubscriptionConsumableInArrear {
             return perUsageNotificationDates != null ? perUsageNotificationDates : ImmutableMap.<String, LocalDate>of();
         }
     }
-
 }
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 8470a77..ae19a63 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -6,6 +6,7 @@ tableFields(prefix) ::= <<
   <prefix>type
 , <prefix>invoice_id
 , <prefix>account_id
+, <prefix>child_account_id
 , <prefix>bundle_id
 , <prefix>subscription_id
 , <prefix>description
@@ -26,6 +27,7 @@ tableValues() ::= <<
   :type
 , :invoiceId
 , :accountId
+, :childAccountId
 , :bundleId
 , :subscriptionId
 , :description
@@ -66,4 +68,21 @@ getAdjustedOrRepairedInvoiceItemsByLinkedId() ::= <<
   AND type IN ('ITEM_ADJ', 'REPAIR_ADJ')
   <AND_CHECK_TENANT()>
   ;
+>>
+
+updateAmount() ::= <<
+    UPDATE <tableName()>
+    SET amount = :amount
+    WHERE id = :id
+    <AND_CHECK_TENANT()>;
+>>
+
+getInvoiceItemsByParentInvoice() ::= <<
+  SELECT <allTableFields(("items."))>
+  FROM <tableName()> items
+  INNER JOIN invoice_parent_children invRel ON invRel.child_invoice_id = items.invoice_id
+  WHERE invRel.parent_invoice_id = :parentInvoiceId
+  <AND_CHECK_TENANT("items.")>
+  <defaultOrderBy()>
+  ;
 >>
\ No newline at end of file
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceParentChildrenSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceParentChildrenSqlDao.sql.stg
new file mode 100644
index 0000000..e4f9726
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceParentChildrenSqlDao.sql.stg
@@ -0,0 +1,37 @@
+group InvoiceParentChildrenSqlDao: EntitySqlDao;
+
+tableName() ::= "invoice_parent_children"
+
+tableFields(prefix) ::= <<
+  <prefix>parent_invoice_id
+, <prefix>child_invoice_id
+, <prefix>child_account_id
+, <prefix>created_by
+, <prefix>created_date
+>>
+
+allTableFields(prefix) ::= <<
+  <prefix>record_id
+, <tableFields(prefix)>
+>>
+
+tableValues() ::= <<
+  :parentInvoiceId
+, :childInvoiceId
+, :childAccountId
+, :createdBy
+, :createdDate
+>>
+
+allTableValues() ::= <<
+  :recordId
+, <tableValues()>
+>>
+
+getChildInvoicesByParentInvoiceId() ::= <<
+   SELECT <allTableFields()>
+     FROM <tableName()>
+    WHERE parent_invoice_id = :parentInvoiceId
+    <AND_CHECK_TENANT()>
+    <defaultOrderBy()>
+ >>
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
index e1ab469..7fb6bb6 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoicePaymentSqlDao.sql.stg
@@ -53,12 +53,13 @@ getByPaymentId() ::= <<
   ;
 >>
 
-getPaymentsForCookieId() ::= <<
+getPaymentForCookieId() ::= <<
   SELECT <allTableFields()>
   FROM <tableName()>
   WHERE payment_cookie_id = :paymentCookieId
   <AND_CHECK_TENANT()>
   <defaultOrderBy()>
+  LIMIT 1
   ;
 >>
 
@@ -78,7 +79,7 @@ getInvoicePayments() ::= <<
     FROM <tableName()>
     WHERE payment_id = :paymentId
     <AND_CHECK_TENANT()>
-   <defaultOrderBy()>
+    <defaultOrderBy()>
     ;
 >>
 
@@ -106,6 +107,7 @@ getChargeBacksByAccountId() ::= <<
     FROM <tableName()> ip
     INNER JOIN invoices i ON i.id = ip.invoice_id
     WHERE ip.type = 'CHARGED_BACK' AND i.account_id = :accountId
+    AND success
     <AND_CHECK_TENANT("i.")>
     <AND_CHECK_TENANT("ip.")>
     ;
@@ -116,11 +118,11 @@ getChargebacksByPaymentId() ::= <<
     FROM <tableName()>
     WHERE type = 'CHARGED_BACK'
     AND linked_invoice_payment_id IN (SELECT id FROM invoice_payments WHERE payment_id = :paymentId)
+    AND success
     <AND_CHECK_TENANT()>
     ;
 >>
 
-
 updateAttempt() ::= <<
     UPDATE <tableName()>
     SET payment_id := :paymentId,
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 8daa8b1..02de5dc 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -7,7 +7,9 @@ tableFields(prefix) ::= <<
 , <prefix>invoice_date
 , <prefix>target_date
 , <prefix>currency
+, <prefix>status
 , <prefix>migrated
+, <prefix>parent_invoice
 , <prefix>created_by
 , <prefix>created_date
 >>
@@ -17,7 +19,9 @@ tableValues() ::= <<
 , :invoiceDate
 , :targetDate
 , :currency
+, :status
 , :migrated
+, :parentInvoice
 , :createdBy
 , :createdDate
 >>
@@ -50,3 +54,28 @@ getInvoiceIdByPaymentId() ::= <<
    <AND_CHECK_TENANT("i.")>
    <AND_CHECK_TENANT("ip.")>
 >>
+
+updateStatus() ::= <<
+    UPDATE <tableName()>
+    SET status = :status
+    WHERE id = :id
+    <AND_CHECK_TENANT()>;
+>>
+
+getParentDraftInvoice() ::= <<
+  SELECT <allTableFields()>
+    FROM <tableName()>
+   WHERE account_id = :accountId
+     AND status = 'DRAFT'
+   <AND_CHECK_TENANT()>
+   <defaultOrderBy()>
+>>
+
+getParentInvoiceByChildInvoiceId() ::= <<
+   SELECT <allTableFields("i.")>
+     FROM <tableName()> i
+     INNER JOIN invoice_parent_children ipc ON i.id = ipc.parent_invoice_id
+    WHERE ipc.child_invoice_id = :childInvoiceId
+   <AND_CHECK_TENANT("i.")>
+   <AND_CHECK_TENANT("ipc.")>
+ >>
\ No newline at end of file
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index f7cda2b..89602db 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -7,13 +7,14 @@ CREATE TABLE invoice_items (
     type varchar(24) NOT NULL,
     invoice_id varchar(36) NOT NULL,
     account_id varchar(36) NOT NULL,
+    child_account_id varchar(36),
     bundle_id varchar(36),
     subscription_id varchar(36),
     description varchar(255),
-    plan_name varchar(50),
-    phase_name varchar(50),
-    usage_name varchar(50),
-    start_date date NOT NULL,
+    plan_name varchar(255),
+    phase_name varchar(255),
+    usage_name varchar(255),
+    start_date date,
     end_date date,
     amount numeric(15,9) NOT NULL,
     rate numeric(15,9) NULL,
@@ -38,9 +39,11 @@ CREATE TABLE invoices (
     id varchar(36) NOT NULL,
     account_id varchar(36) NOT NULL,
     invoice_date date NOT NULL,
-    target_date date NOT NULL,
+    target_date date,
     currency varchar(3) NOT NULL,
+    status varchar(15) NOT NULL DEFAULT 'COMMITTED',
     migrated bool NOT NULL,
+    parent_invoice bool NOT NULL DEFAULT FALSE,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     account_record_id bigint /*! unsigned */ not null,
@@ -48,7 +51,7 @@ CREATE TABLE invoices (
     PRIMARY KEY(record_id)
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
 CREATE UNIQUE INDEX invoices_id ON invoices(id);
-CREATE INDEX invoices_account_target ON invoices(account_id ASC, target_date);
+CREATE INDEX invoices_account ON invoices(account_id ASC);
 CREATE INDEX invoices_tenant_account_record_id ON invoices(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS invoice_payments;
@@ -75,3 +78,20 @@ CREATE UNIQUE INDEX invoice_payments_id ON invoice_payments(id);
 CREATE INDEX invoice_payments_invoice_id ON invoice_payments(invoice_id);
 CREATE INDEX invoice_payments_reversals ON invoice_payments(linked_invoice_payment_id);
 CREATE INDEX invoice_payments_tenant_account_record_id ON invoice_payments(tenant_record_id, account_record_id);
+
+DROP TABLE IF EXISTS invoice_parent_children;
+CREATE TABLE invoice_parent_children (
+    record_id serial unique,
+    id varchar(36) NOT NULL,
+    parent_invoice_id varchar(36) NOT NULL,
+    child_invoice_id varchar(36) NOT NULL,
+    child_account_id varchar(36) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    account_record_id bigint /*! unsigned */ not null,
+    tenant_record_id bigint /*! unsigned */ not null default 0,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX invoice_parent_children_id ON invoice_parent_children(id);
+CREATE INDEX invoice_parent_children_invoice_id ON invoice_parent_children(parent_invoice_id);
+CREATE INDEX invoice_parent_children_tenant_account_record_id ON invoice_parent_children(tenant_record_id, account_record_id);
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20160908172551__multiple_refunds_254.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20160908172551__multiple_refunds_254.sql
new file mode 100644
index 0000000..e5628d6
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20160908172551__multiple_refunds_254.sql
@@ -0,0 +1 @@
+drop index idx_invoice_payments on invoice_payments;
\ No newline at end of file
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20161103111120__plan_length_NA.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20161103111120__plan_length_NA.sql
new file mode 100644
index 0000000..4a5e995
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20161103111120__plan_length_NA.sql
@@ -0,0 +1,3 @@
+alter table invoice_items modify plan_name varchar(255) COLLATE utf8_bin DEFAULT NULL;
+alter table invoice_items modify phase_name varchar(255) COLLATE utf8_bin DEFAULT NULL;
+alter table invoice_items modify usage_name varchar(255) COLLATE utf8_bin DEFAULT NULL;
\ No newline at end of file
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20162029212038__invoice_refund_adj_30.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20162029212038__invoice_refund_adj_30.sql
new file mode 100644
index 0000000..a314a09
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20162029212038__invoice_refund_adj_30.sql
@@ -0,0 +1 @@
+update invoice_items set type = 'CREDIT_ADJ' where type = 'REFUND_ADJ';
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163502123517__invoice_ha_459.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163502123517__invoice_ha_459.sql
new file mode 100644
index 0000000..efe7a94
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163502123517__invoice_ha_459.sql
@@ -0,0 +1,25 @@
+CREATE TABLE invoice_parent_children (
+    record_id serial unique,
+    id varchar(36) NOT NULL,
+    parent_invoice_id varchar(36) NOT NULL,
+    child_invoice_id varchar(36) NOT NULL,
+    child_account_id varchar(36) NOT NULL,
+    created_by varchar(50) NOT NULL,
+    created_date datetime NOT NULL,
+    account_record_id bigint /*! unsigned */ not null,
+    tenant_record_id bigint /*! unsigned */ not null default 0,
+    PRIMARY KEY(record_id)
+) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX invoice_parent_children_id ON invoice_parent_children(id);
+CREATE INDEX invoice_parent_children_invoice_id ON invoice_parent_children(parent_invoice_id);
+CREATE INDEX invoice_parent_children_tenant_account_record_id ON invoice_parent_children(tenant_record_id, account_record_id);
+
+
+alter table invoice_items add column child_account_id varchar(36) after account_id;
+alter table invoice_items modify start_date date;
+
+alter table invoices add column  parent_invoice bool NOT NULL DEFAULT FALSE after migrated;
+alter table invoices modify target_date date;
+
+drop index invoices_account_target on invoices;
+create index invoices_account on invoices(account_id asc);
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163917173937__invoice_status_459.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163917173937__invoice_status_459.sql
new file mode 100644
index 0000000..4f9a09b
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20163917173937__invoice_status_459.sql
@@ -0,0 +1,2 @@
+alter table invoices add column status varchar(15) NOT NULL after currency;
+update invoices set status = 'COMMITTED';
\ No newline at end of file
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
index 8b7bbfb..9d791e1 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/invoice/TestDefaultInvoicePaymentApi.java
@@ -51,15 +51,6 @@ public class TestDefaultInvoicePaymentApi extends InvoiceTestSuiteWithEmbeddedDB
         verifyRefund(THIRTY, BigDecimal.TEN, BigDecimal.TEN, false, ImmutableMap.<UUID, BigDecimal>of());
     }
 
-    @Test(groups = "slow")
-    public void testFullRefundWithInvoiceAdjustment() throws Exception {
-        verifyRefund(THIRTY, THIRTY, BigDecimal.ZERO, true, ImmutableMap.<UUID, BigDecimal>of());
-    }
-
-    @Test(groups = "slow")
-    public void testPartialRefundWithInvoiceAdjustment() throws Exception {
-        verifyRefund(THIRTY, BigDecimal.TEN, BigDecimal.ZERO, true, ImmutableMap.<UUID, BigDecimal>of());
-    }
 
     @Test(groups = "slow")
     public void testFullRefundWithBothInvoiceItemAdjustments() throws Exception {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
index fd4b071..f5849cd 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/migration/TestDefaultInvoiceMigrationApi.java
@@ -23,27 +23,27 @@ import java.util.Collection;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDaoHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.killbill.billing.invoice.model.RecurringInvoiceItem;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbeddedDB {
+import com.google.common.collect.ImmutableList;
 
-    private final Logger log = LoggerFactory.getLogger(TestDefaultInvoiceMigrationApi.class);
+public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbeddedDB {
 
     private LocalDate date_migrated;
-    private DateTime date_regular;
+    private LocalDate date_regular;
 
     private UUID accountId;
     private UUID migrationInvoiceId;
@@ -57,7 +57,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
     public void beforeMethod() throws Exception {
         super.beforeMethod();
         date_migrated = clock.getUTCToday().minusYears(1);
-        date_regular = clock.getUTCNow();
+        date_regular = clock.getUTCToday();
 
         final Account account = invoiceUtil.createAccount(callContext);
         accountId = account.getId();
@@ -66,8 +66,9 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
     }
 
     private UUID createAndCheckMigrationInvoice(final UUID accountId) throws InvoiceApiException {
-        final UUID migrationInvoiceId = migrationApi.createMigrationInvoice(accountId, date_migrated, MIGRATION_INVOICE_AMOUNT,
-                                                                            MIGRATION_INVOICE_CURRENCY, callContext);
+
+        final InvoiceItem recurringItem = new RecurringInvoiceItem(null, accountId, null, null, "planName", "phaseName", new LocalDate(2016, 2, 1), new LocalDate(2016, 3, 1), MIGRATION_INVOICE_AMOUNT, MIGRATION_INVOICE_AMOUNT, MIGRATION_INVOICE_CURRENCY);
+        final UUID migrationInvoiceId = invoiceUserApi.createMigrationInvoice(accountId, date_migrated, ImmutableList.of(recurringItem), callContext);
         Assert.assertNotNull(migrationInvoiceId);
         //Double check it was created and values are correct
 
@@ -79,7 +80,8 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
         //		Assert.assertEquals(invoice.getTargetDate(),now);
         Assert.assertEquals(invoice.getInvoiceItems().size(), 1);
         Assert.assertEquals(invoice.getInvoiceItems().get(0).getAmount().compareTo(MIGRATION_INVOICE_AMOUNT), 0);
-        Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).compareTo(MIGRATION_INVOICE_AMOUNT), 0);
+        Assert.assertEquals(invoice.getInvoiceItems().get(0).getType(), InvoiceItemType.RECURRING);
+        Assert.assertEquals(InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO), 0);
         Assert.assertEquals(invoice.getCurrency(), MIGRATION_INVOICE_CURRENCY);
         Assert.assertTrue(invoice.isMigrated());
 
@@ -88,7 +90,7 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
 
     @Test(groups = "slow")
     public void testUserApiAccess() {
-        final List<Invoice> byAccount = invoiceUserApi.getInvoicesByAccount(accountId, callContext);
+        final List<Invoice> byAccount = invoiceUserApi.getInvoicesByAccount(accountId, false, callContext);
         Assert.assertEquals(byAccount.size(), 1);
         Assert.assertEquals(byAccount.get(0).getId(), regularInvoiceId);
 
@@ -97,7 +99,8 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
         Assert.assertEquals(byAccountAndDate.get(0).getId(), regularInvoiceId);
 
         final Collection<Invoice> unpaid = invoiceUserApi.getUnpaidInvoicesByAccountId(accountId, new LocalDate(date_regular.plusDays(1)), callContext);
-        Assert.assertEquals(unpaid.size(), 2);
+        Assert.assertEquals(unpaid.size(), 1);
+        Assert.assertEquals(unpaid.iterator().next().getId(), regularInvoiceId);
     }
 
     // ACCOUNT balance should reflect total of migration and non-migration invoices
@@ -108,7 +111,6 @@ public class TestDefaultInvoiceMigrationApi extends InvoiceTestSuiteWithEmbedded
         final BigDecimal balanceOfAllInvoices = InvoiceModelDaoHelper.getBalance(migrationInvoice).add(InvoiceModelDaoHelper.getBalance(regularInvoice));
 
         final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
-        log.info("ACCOUNT balance: " + accountBalance + " should equal the Balance Of All Invoices: " + balanceOfAllInvoices);
         Assert.assertEquals(accountBalance.compareTo(balanceOfAllInvoices), 0);
     }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 35ea6c6..98fc4b3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -32,6 +34,7 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -58,11 +61,9 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         super.beforeMethod();
         final Account account = invoiceUtil.createAccount(callContext);
         accountId = account.getId();
-        invoiceId = invoiceUtil.generateRegularInvoice(account, clock.getUTCNow(), callContext);
+        invoiceId = invoiceUtil.generateRegularInvoice(account, null, callContext);
     }
 
-
-
     @Test(groups = "slow")
     public void testPostExternalChargeOnNewInvoice() throws Exception {
         // Initial account balance
@@ -71,7 +72,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         // Post an external charge
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, null, "description", clock.getUTCToday(), externalChargeAmount, accountCurrency);
-        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         verifyExternalChargeOnNewInvoice(accountBalance, null, externalChargeAmount, externalChargeInvoiceItem);
 
         assertEquals(externalChargeInvoiceItem.getDescription(), "description");
@@ -86,7 +87,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
         final UUID bundleId = UUID.randomUUID();
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, bundleId, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
-        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         verifyExternalChargeOnNewInvoice(accountBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem);
     }
 
@@ -122,7 +123,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         // Post an external charge
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
-        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         verifyExternalChargeOnExistingInvoice(invoiceBalance, null, externalChargeAmount, externalChargeInvoiceItem);
     }
 
@@ -141,7 +142,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         // Post an external charge
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
-        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), newCallContextLater).get(0);
+        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, newCallContextLater).get(0);
 
         final Invoice newInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
         final BigDecimal newOriginalAmountCharged = newInvoice.getOriginalChargedAmount();
@@ -166,7 +167,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         final BigDecimal externalChargeAmount = BigDecimal.TEN;
         final UUID bundleId = UUID.randomUUID();
         final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, bundleId, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
-        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+        final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
         verifyExternalChargeOnExistingInvoice(invoiceBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem);
     }
 
@@ -189,8 +190,8 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
     }
 
-    @Test(groups = "slow")
-    public void testAdjustFullInvoice() throws Exception {
+    @Test(groups = "slow", expectedExceptions = InvoiceApiException.class, expectedExceptionsMessageRegExp = ".*it is already in COMMITTED status")
+    public void testAdjustCommittedInvoice() throws Exception {
         // Verify the initial invoice balance
         final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
         Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
@@ -201,12 +202,13 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Adjust the invoice for the full amount
         final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, invoiceBalance,
-                                                                                    clock.getUTCToday(), accountCurrency, callContext);
+                                                                                    clock.getUTCToday(), accountCurrency, "some description", callContext);
         Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
         Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
         Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
         Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(invoiceBalance.negate()), 0);
         Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
+        Assert.assertEquals(creditInvoiceItem.getDescription(), "some description");
         Assert.assertNull(creditInvoiceItem.getLinkedItemId());
 
         // Verify the adjusted invoice balance
@@ -219,39 +221,10 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testAdjustPartialInvoice() throws Exception {
-        // Verify the initial invoice balance
-        final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
-        Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
-
-        // Verify the initial account balance
-        final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
-        Assert.assertEquals(accountBalance, invoiceBalance);
-
-        // Adjust the invoice for a fraction of the balance
-        final BigDecimal creditAmount = invoiceBalance.divide(BigDecimal.TEN, BigDecimal.ROUND_HALF_UP);
-        final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, creditAmount,
-                                                                                    clock.getUTCToday(), accountCurrency, callContext);
-        Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
-        Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
-        Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
-        Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(creditAmount.negate()), 0);
-        Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
-        Assert.assertNull(creditInvoiceItem.getLinkedItemId());
-
-        // Verify the adjusted invoice balance
-        final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
-        verifyAdjustedInvoiceBalance(invoiceBalance, creditAmount, accountCurrency, adjustedInvoiceBalance);
-
-        // Verify the adjusted account balance
-        final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
-        Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
-    }
-
-    @Test(groups = "slow")
     public void testCantAdjustInvoiceWithNegativeAmount() throws Exception {
         try {
-            invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, BigDecimal.TEN.negate(), clock.getUTCToday(), accountCurrency, callContext);
+            invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, BigDecimal.TEN.negate(), clock.getUTCToday(), accountCurrency,
+                                                  null, callContext);
             Assert.fail("Should not have been able to adjust an invoice with a negative amount");
         } catch (InvoiceApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.CREDIT_AMOUNT_INVALID.getCode());
@@ -274,7 +247,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
 
         // Adjust the invoice for the full amount
         final InvoiceItem adjInvoiceItem = invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(),
-                                                                                      clock.getUTCToday(), callContext);
+                                                                                      clock.getUTCToday(), null, callContext);
         Assert.assertEquals(adjInvoiceItem.getInvoiceId(), invoiceId);
         Assert.assertEquals(adjInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
         Assert.assertEquals(adjInvoiceItem.getAccountId(), accountId);
@@ -309,7 +282,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         final BigDecimal adjAmount = invoiceItem.getAmount().subtract(new BigDecimal("0.01"));
         final InvoiceItem adjInvoiceItem = invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(),
                                                                                       clock.getUTCToday(), adjAmount, accountCurrency,
-                                                                                      callContext);
+                                                                                      null, callContext);
         Assert.assertEquals(adjInvoiceItem.getInvoiceId(), invoiceId);
         Assert.assertEquals(adjInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
         Assert.assertEquals(adjInvoiceItem.getAccountId(), accountId);
@@ -332,7 +305,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
 
         try {
             invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(), clock.getUTCToday(),
-                                                       BigDecimal.TEN.negate(), accountCurrency, callContext);
+                                                       BigDecimal.TEN.negate(), accountCurrency, null, callContext);
             Assert.fail("Should not have been able to adjust an item with a negative amount");
         } catch (InvoiceApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE.getCode());
@@ -367,4 +340,31 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         final Invoice invoiceAfterTagRemoval = invoiceUserApi.getInvoice(invoiceId, callContext);
         assertEquals(invoiceAfterTagRemoval.getBalance().compareTo(BigDecimal.ZERO), 1);
     }
+
+    @Test(groups = "slow")
+    public void testCommitInvoice() throws Exception {
+        // Verify the initial invoice balance
+        final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
+        Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
+
+        // Verify the initial account balance
+        final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
+        Assert.assertEquals(accountBalance, invoiceBalance);
+
+        // Adjust the invoice for the full amount
+        final BigDecimal creditAmount = BigDecimal.TEN;
+        final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, null, creditAmount,
+                                                                                    clock.getUTCToday(), accountCurrency, null, callContext);
+
+        final UUID invoiceId = creditInvoiceItem.getInvoiceId();
+        Invoice creditInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
+        Assert.assertEquals(creditInvoice.getStatus(), InvoiceStatus.DRAFT);
+        Assert.assertEquals(creditInvoiceItem.getInvoiceId(), creditInvoice.getId());
+
+        // move invoice from DRAFT to COMMITTED
+        invoiceUserApi.commitInvoice(creditInvoice.getId(), callContext);
+        creditInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
+        Assert.assertEquals(creditInvoice.getStatus(), InvoiceStatus.COMMITTED);
+
+    }
 }
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 2164386..8ba0cc8 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -28,12 +28,14 @@ import java.util.Map;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
 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.InvoiceStatus;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
@@ -294,7 +296,12 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
     }
 
     @Override
-    public InvoicePaymentModelDao postChargeback(final UUID invoicePaymentId, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
+    public InvoicePaymentModelDao postChargeback(final UUID invoicePaymentId, final String chargebackTransactionExternalKey, final BigDecimal amount, final Currency currency, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoicePaymentModelDao postChargebackReversal(final UUID paymentId, final String chargebackTransactionExternalKey, final InternalCallContext context) throws InvoiceApiException {
         throw new UnsupportedOperationException();
     }
 
@@ -367,5 +374,40 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, 
         synchronized (monitor) {
             payments.put(invoicePayment.getId(), invoicePayment);
         }
+
+    }
+
+    @Override
+    public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newState, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void createParentChildInvoiceRelation(final InvoiceParentChildModelDao invoiceRelation, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public InvoiceModelDao getParentDraftInvoice(final UUID parentAccountId, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InvoiceParentChildModelDao> getChildInvoicesByParentInvoiceId(final UUID parentInvoiceId, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void updateInvoiceItemAmount(final UUID invoiceItemId, final BigDecimal amount, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void transferChildCreditToParent(final Account childAccount, final InternalCallContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(final UUID parentInvoiceId, final InternalTenantContext context) throws InvoiceApiException {
+        throw new UnsupportedOperationException();
     }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 6bbfbe3..b26dbbb 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -45,6 +45,8 @@ 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.entity.EntityPersistenceException;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
+import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
 import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
 import org.killbill.billing.invoice.MockBillingEventSet;
 import org.killbill.billing.invoice.api.Invoice;
@@ -53,6 +55,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
 import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
 import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
@@ -60,6 +63,8 @@ import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.DefaultInvoicePayment;
 import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
+import org.killbill.billing.invoice.model.ItemAdjInvoiceItem;
+import org.killbill.billing.invoice.model.ParentInvoiceItem;
 import org.killbill.billing.invoice.model.RecurringInvoiceItem;
 import org.killbill.billing.invoice.model.RepairAdjInvoiceItem;
 import org.killbill.billing.junction.BillingEvent;
@@ -545,7 +550,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
                                                                     endDate, rate1, rate1, Currency.USD);
         invoiceUtil.createInvoiceItem(item1, context);
 
-        final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), rate1.negate(), Currency.USD);
+        final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice1.getId(), accountId, new LocalDate(), null, rate1.negate(), Currency.USD);
         invoiceUtil.createInvoiceItem(creditItem, context);
 
         final BigDecimal balance = invoiceDao.getAccountBalance(accountId, context);
@@ -598,11 +603,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         testAccountBalanceWithRefundInternal(false);
     }
 
-    @Test(groups = "slow")
-    public void testAccountBalanceWithRefundAndAdj() throws InvoiceApiException, EntityPersistenceException {
-        testAccountBalanceWithRefundInternal(true);
-    }
-
     private void testAccountBalanceWithRefundInternal(final boolean withAdjustment) throws InvoiceApiException, EntityPersistenceException {
 
         final UUID accountId = account.getId();
@@ -742,13 +742,6 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         testAccountBalanceWithRefundAndCBAInternal(false, refundAmount, expectedBalance);
     }
 
-    @Test(groups = "slow")
-    public void testAccountBalanceWithLargeRefundAndCBAWithAdj() throws InvoiceApiException, EntityPersistenceException {
-        final BigDecimal refundAmount = new BigDecimal("20.00");
-        final BigDecimal expectedBalance = new BigDecimal("-10.00");
-        testAccountBalanceWithRefundAndCBAInternal(true, refundAmount, expectedBalance);
-    }
-
     private void testAccountBalanceWithRefundAndCBAInternal(final boolean withAdjustment, final BigDecimal refundAmount, final BigDecimal expectedFinalBalance) throws InvoiceApiException, EntityPersistenceException {
         final UUID accountId = account.getId();
         final UUID bundleId = UUID.randomUUID();
@@ -804,7 +797,11 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         assertEquals(cba.compareTo(new BigDecimal("10.00")), 0);
 
         // PARTIAL REFUND on the payment
-        invoiceDao.createRefund(paymentId, refundAmount, withAdjustment, ImmutableMap.<UUID, BigDecimal>of(), UUID.randomUUID().toString(), context);
+        final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts = new HashMap<UUID, BigDecimal>();
+        if (withAdjustment) {
+            invoiceItemIdsWithAmounts.put(item2Replace.getId(), refundAmount);
+        }
+        invoiceDao.createRefund(paymentId, refundAmount, withAdjustment, invoiceItemIdsWithAmounts, UUID.randomUUID().toString(), context);
 
         balance = invoiceDao.getAccountBalance(accountId, context);
         assertEquals(balance.compareTo(expectedFinalBalance), 0);
@@ -920,7 +917,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         assertEquals(cba.compareTo(new BigDecimal("0.00")), 0);
 
         // FINALLY ISSUE A CREDIT ADJ
-        final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice2.getId(), accountId, new LocalDate(), rate2.negate(), Currency.USD);
+        final CreditAdjInvoiceItem creditItem = new CreditAdjInvoiceItem(invoice2.getId(), accountId, new LocalDate(), null, rate2.negate(), Currency.USD);
         invoiceUtil.createInvoiceItem(creditItem, context);
         balance = invoiceDao.getAccountBalance(accountId, context);
         assertEquals(balance.compareTo(new BigDecimal("0.00")), 0);
@@ -1082,6 +1079,54 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         assertEquals(invoices.size(), 2);
     }
 
+    @Test(groups = "slow")
+    public void testGetUnpaidInvoicesByAccountIdWithDraftInvoice() throws EntityPersistenceException {
+        final UUID accountId = account.getId();
+        final UUID bundleId = UUID.randomUUID();
+        final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
+        final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
+        invoiceUtil.createInvoice(invoice1, true, context);
+
+        final LocalDate startDate = new LocalDate(2011, 3, 1);
+        final LocalDate endDate = startDate.plusMonths(1);
+
+        final BigDecimal rate1 = new BigDecimal("17.0");
+        final BigDecimal rate2 = new BigDecimal("42.0");
+
+        final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
+                                                                    rate1, rate1, Currency.USD);
+        invoiceUtil.createInvoiceItem(item1, context);
+
+        final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
+                                                                    rate2, rate2, Currency.USD);
+        invoiceUtil.createInvoiceItem(item2, context);
+
+        LocalDate upToDate;
+        Collection<InvoiceModelDao> invoices;
+
+        upToDate = new LocalDate(2011, 1, 1);
+        invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+        assertEquals(invoices.size(), 0);
+
+        upToDate = new LocalDate(2012, 1, 1);
+        invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+        assertEquals(invoices.size(), 1);
+
+        List<InvoiceModelDao> allInvoicesByAccount = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
+        assertEquals(allInvoicesByAccount.size(), 1);
+
+        // insert DRAFT invoice
+        createCredit(accountId, new LocalDate(2011, 12, 31), BigDecimal.TEN);
+
+        allInvoicesByAccount = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
+        assertEquals(allInvoicesByAccount.size(), 2);
+        assertEquals(allInvoicesByAccount.get(0).getStatus(), InvoiceStatus.COMMITTED);
+        assertEquals(allInvoicesByAccount.get(1).getStatus(), InvoiceStatus.DRAFT);
+
+        upToDate = new LocalDate(2012, 1, 1);
+        invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+        assertEquals(invoices.size(), 1);
+    }
 
     /*
      *
@@ -1108,7 +1153,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
                                                                        recurringPrice.getPrice(currency), currency, BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE,
                                                                        "testEvent1", 1L, SubscriptionBaseTransitionType.CREATE);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, invoiceList, targetDate, Currency.USD, context);
@@ -1160,7 +1205,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         final BillingEvent event = invoiceUtil.createMockBillingEvent(null, subscription, effectiveDate, plan, phase,
                                                                       fixedPrice.getPrice(currency), null, currency, BillingPeriod.MONTHLY, 15, BillingMode.IN_ADVANCE,
                                                                       "testEvent", 1L, SubscriptionBaseTransitionType.CREATE);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(event);
 
         final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 15);
@@ -1202,7 +1247,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         final BillingEvent event1 = invoiceUtil.createMockBillingEvent(null, subscription, effectiveDate1, plan, phase1, fixedPrice.getPrice(currency),
                                                                        null, currency, BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE,
                                                                        "testEvent1", 1L, SubscriptionBaseTransitionType.CREATE);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         final UUID accountId = account.getId();
@@ -1245,7 +1290,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testInvoiceForEmptyEventSet() throws InvoiceApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, new LocalDate(), Currency.USD, context);
         final Invoice invoice = invoiceWithMetadata.getInvoice();
         assertNull(invoice);
@@ -1272,7 +1317,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
                                                                        fixedPrice.getPrice(currency), null, currency,
                                                                        BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE,
                                                                        "testEvent1", 1L, SubscriptionBaseTransitionType.CREATE);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(event1);
 
         final DateTime effectiveDate2 = effectiveDate1.plusDays(30);
@@ -1339,7 +1384,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         invoices.add(new DefaultInvoice(savedInvoice));
 
         // NOW COMPUTE A DIFFERENT ITEM TO TRIGGER REPAIR
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final SubscriptionBase subscription = getZombieSubscription(subscriptionId);
 
         final Plan plan = Mockito.mock(Plan.class);
@@ -1380,7 +1425,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
         final PlanPhase phase2 = Mockito.mock(PlanPhase.class);
         Mockito.when(phase2.getName()).thenReturn("plan-phase2");
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final List<Invoice> invoices = new ArrayList<Invoice>();
 
         final BillingEvent event1 = invoiceUtil.createMockBillingEvent(null, subscription, targetDate1, plan, phase1, null,
@@ -1658,7 +1703,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
     private void createCredit(final UUID accountId, @Nullable final UUID invoiceId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
         final InvoiceModelDao invoiceModelDao;
         if (invoiceId == null) {
-            invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD);
+            invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD, false, InvoiceStatus.DRAFT);
 
         } else {
             invoiceModelDao = invoiceDao.getById(invoiceId, context);
@@ -1668,10 +1713,82 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
                                                                           invoiceModelDao.getId(),
                                                                           accountId,
                                                                           effectiveDate,
+                                                                          null,
                                                                           // Note! The amount is negated here!
                                                                           creditAmount.negate(),
                                                                           invoiceModelDao.getCurrency());
         invoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(invoiceItem));
         invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), context);
     }
+
+    @Test(groups = "slow")
+    public void testCreateParentChildInvoiceRelation() throws InvoiceApiException {
+
+        final UUID parentInvoiceId = UUID.randomUUID();
+        final UUID childInvoiceId = UUID.randomUUID();
+        final UUID childAccountId = UUID.randomUUID();
+        InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(parentInvoiceId, childInvoiceId, childAccountId);
+        invoiceDao.createParentChildInvoiceRelation(invoiceRelation, context);
+
+        final List<InvoiceParentChildModelDao> relations = invoiceDao.getChildInvoicesByParentInvoiceId(parentInvoiceId, context);
+        assertEquals(relations.size(), 1);
+        final InvoiceParentChildModelDao parentChildRelation = relations.get(0);
+        assertEquals(parentChildRelation.getChildAccountId(), childAccountId);
+        assertEquals(parentChildRelation.getChildInvoiceId(), childInvoiceId);
+        assertEquals(parentChildRelation.getParentInvoiceId(), parentInvoiceId);
+
+    }
+
+    @Test(groups = "slow")
+    public void testCreateParentInvoice() throws InvoiceApiException {
+
+        final UUID parentAccountId = UUID.randomUUID();
+        final UUID childAccountId = UUID.randomUUID();
+        final DateTime today = clock.getNow(account.getTimeZone());
+
+        InvoiceModelDao parentInvoice = new InvoiceModelDao(parentAccountId, today.toLocalDate(), account.getCurrency(), InvoiceStatus.DRAFT, true);
+        InvoiceItem parentInvoiceItem = new ParentInvoiceItem(UUID.randomUUID(), today, parentInvoice.getId(), parentAccountId, childAccountId, BigDecimal.TEN, account.getCurrency(), "");
+        parentInvoice.addInvoiceItem(new InvoiceItemModelDao(parentInvoiceItem));
+
+        // build account date time zone
+        final FutureAccountNotifications futureAccountNotifications = new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of());
+        invoiceDao.createInvoice(parentInvoice, parentInvoice.getInvoiceItems(), true, futureAccountNotifications, context);
+
+        final InvoiceModelDao parentDraftInvoice = invoiceDao.getParentDraftInvoice(parentAccountId, context);
+
+        assertNotNull(parentDraftInvoice);
+        assertEquals(parentDraftInvoice.getStatus(), InvoiceStatus.DRAFT);
+        assertEquals(parentDraftInvoice.getInvoiceItems().size(), 1);
+
+    }
+
+    @Test(groups = "slow")
+    public void testRetrieveInvoiceItemsByParentInvoice() throws InvoiceApiException {
+        final UUID childAccountId = account.getId();
+        final Invoice childInvoice = new DefaultInvoice(childAccountId, clock.getUTCToday(), clock.getUTCToday(), Currency.USD);
+        final UUID invoiceId = childInvoice.getId();
+        final UUID subscriptionId = UUID.randomUUID();
+        final UUID bundleId = UUID.randomUUID();
+        final LocalDate startDate = new LocalDate(2010, 1, 1);
+        final LocalDate endDate = new LocalDate(2010, 4, 1);
+        final InvoiceItem invoiceItem = new RecurringInvoiceItem(invoiceId, childAccountId, bundleId, subscriptionId, "test plan", "test phase", startDate, endDate,
+                                                                 new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
+        final InvoiceItem invoiceAdj = new ItemAdjInvoiceItem(invoiceItem, startDate, new BigDecimal("-5.00"), Currency.USD);
+
+        childInvoice.addInvoiceItem(invoiceItem);
+        childInvoice.addInvoiceItem(invoiceAdj);
+        invoiceUtil.createInvoice(childInvoice, true, context);
+
+        final UUID parentInvoiceId = UUID.randomUUID();
+
+        InvoiceParentChildModelDao invoiceRelation = new InvoiceParentChildModelDao(parentInvoiceId, childInvoice.getId(), childAccountId);
+        invoiceDao.createParentChildInvoiceRelation(invoiceRelation, context);
+
+        final List<InvoiceItemModelDao> invoiceItems = invoiceDao.getInvoiceItemsByParentInvoice(parentInvoiceId, context);
+        assertEquals(invoiceItems.size(), 2);
+        assertEquals(invoiceItems.get(0).getType(), InvoiceItemType.RECURRING);
+        assertEquals(invoiceItems.get(1).getType(), InvoiceItemType.ITEM_ADJ);
+
+    }
+
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 5a6690f..8c9c8c5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -33,7 +33,7 @@ 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.account.api.DefaultAccount;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPrice;
 import org.killbill.billing.catalog.MockInternationalPrice;
 import org.killbill.billing.catalog.MockPlan;
@@ -53,6 +53,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.DefaultInvoicePayment;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
@@ -63,7 +64,7 @@ import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.currency.KillBillMoney;
 import org.killbill.clock.Clock;
 import org.killbill.clock.DefaultClock;
@@ -109,12 +110,18 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         super.beforeClass();
         final Clock clock = new DefaultClock();
         final InvoiceConfig invoiceConfig = new InvoiceConfig() {
+
             @Override
             public int getNumberOfMonthsInFuture() {
                 return 36;
             }
 
             @Override
+            public int getNumberOfMonthsInFuture(final InternalTenantContext context) {
+                return getNumberOfMonthsInFuture();
+            }
+
+            @Override
             public boolean isEmailNotificationsEnabled() {
                 return false;
             }
@@ -125,11 +132,21 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
             }
 
             @Override
+            public TimeSpan getDryRunNotificationSchedule(final InternalTenantContext context) {
+                return getDryRunNotificationSchedule();
+            }
+
+            @Override
             public int getMaxRawUsagePreviousPeriod() {
                 return -1;
             }
 
             @Override
+            public int getMaxRawUsagePreviousPeriod(final InternalTenantContext context) {
+                return getMaxRawUsagePreviousPeriod();
+            }
+
+            @Override
             public int getMaxGlobalLockRetries() {
                 return 10;
             }
@@ -157,14 +174,14 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testWithEmptyEventSet() throws InvoiceApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, clock.getUTCToday(), Currency.USD, internalCallContext);
         assertNull(invoiceWithMetadata.getInvoice());
     }
 
     @Test(groups = "fast")
     public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final SubscriptionBase sub = createSubscription();
         final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
@@ -183,6 +200,45 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         assertEquals(invoice.getNumberOfItems(), 2);
         assertEquals(invoice.getBalance(), KillBillMoney.of(TWENTY, invoice.getCurrency()));
         assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
+
+        assertEquals(invoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2011, 9, 1));
+        assertEquals(invoice.getInvoiceItems().get(0).getEndDate(), new LocalDate(2011, 10, 1));
+
+        assertEquals(invoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(invoice.getInvoiceItems().get(1).getStartDate(), new LocalDate(2011, 10, 1));
+        assertEquals(invoice.getInvoiceItems().get(1).getEndDate(), new LocalDate(2011, 11, 1));
+    }
+
+    @Test(groups = "fast")
+    public void testWithSingleThirtyDaysEvent() throws InvoiceApiException, CatalogApiException {
+        final BillingEventSet events = new MockBillingEventSet();
+
+        final SubscriptionBase sub = createSubscription();
+        final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
+
+        final Plan plan = new MockPlan();
+        final BigDecimal rate1 = TEN;
+        final PlanPhase phase = createMockThirtyDaysPlanPhase(rate1);
+
+        final BillingEvent event = createBillingEvent(sub.getId(), sub.getBundleId(), startDate, plan, phase, 1);
+        events.add(event);
+
+        final LocalDate targetDate = invoiceUtil.buildDate(2011, 10, 3);
+        final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
+        final Invoice invoice = invoiceWithMetadata.getInvoice();
+        assertNotNull(invoice);
+        assertEquals(invoice.getNumberOfItems(), 2);
+        assertEquals(invoice.getBalance(), KillBillMoney.of(TWENTY, invoice.getCurrency()));
+        assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
+
+        assertEquals(invoice.getInvoiceItems().get(0).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(invoice.getInvoiceItems().get(0).getStartDate(), new LocalDate(2011, 9, 1));
+        assertEquals(invoice.getInvoiceItems().get(0).getEndDate(), new LocalDate(2011, 10, 1));
+
+        assertEquals(invoice.getInvoiceItems().get(1).getInvoiceItemType(), InvoiceItemType.RECURRING);
+        assertEquals(invoice.getInvoiceItems().get(1).getStartDate(), new LocalDate(2011, 10, 1));
+        assertEquals(invoice.getInvoiceItems().get(1).getEndDate(), new LocalDate(2011, 10, 31));
     }
 
     private SubscriptionBase createSubscription() {
@@ -208,7 +264,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final int bcdLocal = 16;
         final LocalDate startDate = invoiceUtil.buildDate(2012, 7, bcdLocal);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final BillingEvent event = createBillingEvent(sub.getId(), sub.getBundleId(), startDate, plan, phase, bcdLocal);
         events.add(event);
 
@@ -233,7 +289,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final int bcdLocal = 16;
         final LocalDate startDate = invoiceUtil.buildDate(2012, 7, 16);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(createBillingEvent(sub.getId(), sub.getBundleId(), startDate, plan, phaseEvergreen, bcdLocal));
 
         // Set a target date of today (start date)
@@ -248,7 +304,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final SubscriptionBase sub = createSubscription();
         final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
@@ -273,7 +329,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testTwoMonthlySubscriptionsWithAlignedBillingDates() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final Plan plan1 = new MockPlan();
         final BigDecimal rate1 = FIVE;
@@ -301,7 +357,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final Plan plan1 = new MockPlan();
         final BigDecimal rate1 = FIVE;
@@ -338,7 +394,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testOnePlan_ThreeMonthlyPhases_ChangeEOT() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final Plan plan1 = new MockPlan();
         final BigDecimal rate1 = FIVE;
@@ -368,7 +424,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final SubscriptionBase sub = createSubscription();
         final LocalDate startDate = invoiceUtil.buildDate(2011, 9, 1);
@@ -446,7 +502,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
         BigDecimal expectedAmount;
         final List<Invoice> invoices = new ArrayList<Invoice>();
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         // on 1/5/2011, create SubscriptionBase 1 (trial)
         events.add(createBillingEvent(subscriptionId1, bundleId, plan1StartDate, plan1, plan1Phase1, 5));
@@ -556,7 +612,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
     public void testZeroDollarEvents() throws InvoiceApiException, CatalogApiException {
         final Plan plan = new MockPlan();
         final PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final LocalDate targetDate = invoiceUtil.buildDate(2011, 1, 1);
         events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), targetDate, plan, planPhase, 1));
 
@@ -569,7 +625,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
     public void testEndDateIsCorrect() throws InvoiceApiException, CatalogApiException {
         final Plan plan = new MockPlan();
         final PlanPhase planPhase = createMockMonthlyPlanPhase(ONE);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final LocalDate startDate = clock.getUTCToday().minusDays(1);
         final LocalDate targetDate = startDate.plusDays(1);
 
@@ -597,7 +653,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
         final DateTime changeDate = new DateTime("2012-04-1");
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final BillingEvent event1 = invoiceUtil.createMockBillingEvent(null, subscription, new DateTime("2012-01-1"),
                                                                        plan, phase1,
@@ -636,7 +692,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final BigDecimal fixedCost = TEN;
         final PlanPhase phase1 = createMockMonthlyPlanPhase(monthlyRate, fixedCost, PhaseType.TRIAL);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final UUID subscriptionId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
 
@@ -674,7 +730,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final PlanPhase phase1 = createMockMonthlyPlanPhase(null, fixedCost1, PhaseType.TRIAL);
         final PlanPhase phase2 = createMockMonthlyPlanPhase(null, fixedCost2, PhaseType.EVERGREEN);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final UUID subscriptionId = UUID.randomUUID();
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
@@ -706,7 +762,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testInvoiceGenerationFailureScenario() throws InvoiceApiException, CatalogApiException {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final UUID subscriptionId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
         final int BILL_CYCLE_DAY = 15;
@@ -766,13 +822,18 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
     @Test(groups = "fast", expectedExceptions = {InvoiceApiException.class})
     public void testTargetDateRestrictionFailure() throws InvoiceApiException, CatalogApiException {
         final LocalDate targetDate = clock.getUTCToday().plusMonths(60);
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final Plan plan1 = new MockPlan();
         final PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL);
         events.add(createBillingEvent(UUID.randomUUID(), UUID.randomUUID(), clock.getUTCToday(), plan1, phase1, 1));
         generator.generateInvoice(account, events, null, targetDate, Currency.USD, internalCallContext);
     }
 
+    private MockPlanPhase createMockThirtyDaysPlanPhase(@Nullable final BigDecimal recurringRate) {
+        return new MockPlanPhase(new MockInternationalPrice(new DefaultPrice(recurringRate, Currency.USD)),
+                                 null, BillingPeriod.THIRTY_DAYS);
+    }
+
     private MockPlanPhase createMockMonthlyPlanPhase() {
         return new MockPlanPhase(null, null, BillingPeriod.MONTHLY);
     }
@@ -840,7 +901,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final MockInternationalPrice price20 = new MockInternationalPrice(new DefaultPrice(TWENTY, Currency.USD));
         final PlanPhase basePlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(createBillingEvent(baseSubscription.getId(), baseSubscription.getBundleId(), april25, basePlan, basePlanEvergreen, 25));
 
         // generate invoice
@@ -876,7 +937,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
         // perform a repair (change base plan; remove one add-on)
         // event stream should include just two plans
-        final MockBillingEventSet newEvents = new MockBillingEventSet(internalCallContext);
+        final MockBillingEventSet newEvents = new MockBillingEventSet();
         final Plan basePlan2 = new MockPlan("base plan 2");
         final MockInternationalPrice price13 = new MockInternationalPrice(new DefaultPrice(THIRTEEN, Currency.USD));
         final PlanPhase basePlan2Phase = new MockPlanPhase(price13, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
@@ -905,7 +966,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         final MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
         final PlanPhase originalPlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         events.add(createBillingEvent(originalSubscription.getId(), originalSubscription.getBundleId(), april25, originalPlan, originalPlanEvergreen, 25));
 
         final InvoiceWithMetadata invoiceWithMetadata1 = generator.generateInvoice(account, events, null, april25, Currency.USD, internalCallContext);
@@ -960,7 +1021,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
         final LocalDate targetDate = new LocalDate(2013, 10, 30);
 
-        final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), targetDate, currency, false);
+        final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), targetDate, currency, false, InvoiceStatus.COMMITTED);
 
         // Set the existing recurring invoice item 2013/06/15 - 2013/07/15
         final LocalDate startDate = new LocalDate(2013, 06, 15);
@@ -983,7 +1044,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
         //
         // Note : this is the interesting part of the test; it does not provide the blocking billing events, which force invoice
         // to un repair what was previously repaired.
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final BillingEvent event = invoiceUtil.createMockBillingEvent(null, subscription, new DateTime("2013-06-15", DateTimeZone.UTC),
                                                                       plan, phase,
                                                                       null, recurringPrice.getPrice(currency), currency,
@@ -1064,7 +1125,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testAutoInvoiceOffAccount() throws Exception {
-        final MockBillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final MockBillingEventSet events = new MockBillingEventSet();
         events.setAccountInvoiceOff(true);
 
         final SubscriptionBase sub = createSubscription();
@@ -1087,7 +1148,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
     public void testAutoInvoiceOffWithCredits() throws CatalogApiException, InvoiceApiException {
         final Currency currency = Currency.USD;
         final List<Invoice> invoices = new ArrayList<Invoice>();
-        final MockBillingEventSet eventSet = new MockBillingEventSet(internalCallContext);
+        final MockBillingEventSet eventSet = new MockBillingEventSet();
         final UUID accountId = UUID.randomUUID();
         final UUID bundleId = UUID.randomUUID();
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
index a42f7de..3cd5432 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestFixedAndRecurringInvoiceItemGenerator.java
@@ -37,6 +37,7 @@ import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
 import org.killbill.billing.invoice.MockBillingEventSet;
+import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
@@ -44,8 +45,6 @@ import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -58,7 +57,6 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
 
     private Account account;
     private SubscriptionBase subscription;
-    private AccountDateAndTimeZoneContext dateAndTimeZoneContext;
 
     @Override
     @BeforeMethod(groups = "fast")
@@ -68,7 +66,6 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
         try {
             account = invoiceUtil.createAccount(callContext);
             subscription = invoiceUtil.createSubscription();
-            dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(new DateTime("2016-01-08T03:01:01.000Z"), internalCallContext);
         } catch (final Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -91,7 +88,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
                                                                       BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
                                                                       SubscriptionBaseTransitionType.CREATE);
 
-        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
     }
 
     @Test(groups = "fast")
@@ -114,7 +111,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
                                                                       BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
                                                                       SubscriptionBaseTransitionType.CREATE);
 
-        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
     }
 
     @Test(groups = "fast")
@@ -137,7 +134,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
                                                                       BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
                                                                       SubscriptionBaseTransitionType.CREATE);
 
-        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+        assertFalse(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
     }
 
     @Test(groups = "fast")
@@ -160,16 +157,16 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
                                                                       BillingMode.IN_ADVANCE, "Billing Event Desc", 1L,
                                                                       SubscriptionBaseTransitionType.CREATE);
 
-        assertTrue(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, dateAndTimeZoneContext));
+        assertTrue(fixedAndRecurringInvoiceItemGenerator.isSameDayAndSameSubscription(prevInvoiceItem, event, internalCallContext));
     }
 
     @Test(groups = "fast")
-    public void testProcessFixedBillingEventsWithCancellationOnSameDay() {
+    public void testProcessFixedBillingEventsWithCancellationOnSameDay() throws InvoiceApiException {
 
         final LocalDate targetDate = new LocalDate("2016-01-08");
 
         final UUID invoiceId = UUID.randomUUID();
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final BigDecimal fixedPriceAmount = BigDecimal.TEN;
         final MockInternationalPrice fixedPrice = new MockInternationalPrice(new DefaultPrice(fixedPriceAmount, Currency.USD));
@@ -192,17 +189,17 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
         events.add(event2);
 
         final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
-        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
         assertTrue(proposedItems.isEmpty());
     }
 
     @Test(groups = "fast")
-    public void testProcessFixedBillingEventsWithCancellationOnNextDay() {
+    public void testProcessFixedBillingEventsWithCancellationOnNextDay() throws InvoiceApiException {
 
         final LocalDate targetDate = new LocalDate("2016-01-08");
 
         final UUID invoiceId = UUID.randomUUID();
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final BigDecimal fixedPriceAmount = BigDecimal.TEN;
         final MockInternationalPrice fixedPrice = new MockInternationalPrice(new DefaultPrice(fixedPriceAmount, Currency.USD));
@@ -225,7 +222,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
         events.add(event2);
 
         final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
-        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
         assertEquals(proposedItems.size(), 1);
         assertEquals(proposedItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
         assertEquals(proposedItems.get(0).getAmount().compareTo(fixedPriceAmount), 0);
@@ -233,12 +230,12 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
 
 
     @Test(groups = "fast")
-    public void testProcessFixedBillingEventsWithMultipleChangeOnSameDay() {
+    public void testProcessFixedBillingEventsWithMultipleChangeOnSameDay() throws InvoiceApiException {
 
         final LocalDate targetDate = new LocalDate("2016-01-08");
 
         final UUID invoiceId = UUID.randomUUID();
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         final BigDecimal fixedPriceAmount1 = BigDecimal.TEN;
         final MockInternationalPrice fixedPrice1 = new MockInternationalPrice(new DefaultPrice(fixedPriceAmount1, Currency.USD));
@@ -280,7 +277,7 @@ public class TestFixedAndRecurringInvoiceItemGenerator extends InvoiceTestSuiteN
         events.add(event3);
 
         final List<InvoiceItem> proposedItems = new ArrayList<InvoiceItem>();
-        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems);
+        fixedAndRecurringInvoiceItemGenerator.processFixedBillingEvents(invoiceId, account.getId(), events, targetDate, Currency.USD, proposedItems, internalCallContext);
         assertEquals(proposedItems.size(), 1);
         assertEquals(proposedItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
         assertEquals(proposedItems.get(0).getAmount().compareTo(fixedPriceAmount3), 0);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInAdvanceBillingIntervalDetail.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInAdvanceBillingIntervalDetail.java
index 2328f56..121ff05 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInAdvanceBillingIntervalDetail.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInAdvanceBillingIntervalDetail.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -18,11 +20,10 @@ package org.killbill.billing.invoice.generator;
 
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.BillingMode;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
 
@@ -35,10 +36,18 @@ public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
     @Test(groups = "fast")
     public void testCalculateFirstBillingCycleDate1() throws Exception {
         final LocalDate from = new LocalDate("2012-01-16");
+        final LocalDate to = null;
+        final LocalDate targetDate = new LocalDate();
         final int bcd = 17;
-        final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(from, null, new LocalDate(), bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
-        billingIntervalDetail.calculateFirstBillingCycleDate();
-        Assert.assertEquals(billingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-01-17"));
+
+        final BillingIntervalDetail annualBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(annualBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-01-17"));
+
+        final BillingIntervalDetail monthlyBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.MONTHLY, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(monthlyBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-01-17"));
+
+        final BillingIntervalDetail thirtyDaysBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.THIRTY_DAYS, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(thirtyDaysBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-01-16"));
     }
 
     /*
@@ -50,16 +59,22 @@ public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
     @Test(groups = "fast")
     public void testCalculateFirstBillingCycleDate2() throws Exception {
         final LocalDate from = new LocalDate("2012-02-16");
+        final LocalDate to = null;
+        final LocalDate targetDate = new LocalDate();
         final int bcd = 30;
-        final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(from, null, new LocalDate(), bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
-        billingIntervalDetail.calculateFirstBillingCycleDate();
-        Assert.assertEquals(billingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-29"));
+
+        final BillingIntervalDetail annualBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(annualBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-29"));
+
+        final BillingIntervalDetail monthlyBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.MONTHLY, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(monthlyBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-29"));
+
+        final BillingIntervalDetail thirtyDaysBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.THIRTY_DAYS, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(thirtyDaysBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-16"));
     }
 
     /*
-     * Here the interesting part is that BCD is prior start and
-     *  i) we use MONTHLY billing period
-     * ii) on the next month, there is no such date (2012-02-30 does not exist)
+     * Here the interesting part is that BCD is prior start
      *
      *                                      Start
      *                              BCD     END_MONTH
@@ -67,12 +82,20 @@ public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
      *
      */
     @Test(groups = "fast")
-    public void testCalculateFirstBillingCycleDate4() throws Exception {
+    public void testCalculateFirstBillingCycleDate3() throws Exception {
         final LocalDate from = new LocalDate("2012-01-31");
+        final LocalDate to = null;
+        final LocalDate targetDate = new LocalDate();
         final int bcd = 30;
-        final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(from, null, new LocalDate(), bcd, BillingPeriod.MONTHLY, BillingMode.IN_ADVANCE);
-        billingIntervalDetail.calculateFirstBillingCycleDate();
-        Assert.assertEquals(billingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-29"));
+
+        final BillingIntervalDetail annualBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(annualBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2013-01-30"));
+
+        final BillingIntervalDetail monthlyBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.MONTHLY, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(monthlyBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-29"));
+
+        final BillingIntervalDetail thirtyDaysBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.THIRTY_DAYS, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(thirtyDaysBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-01-31"));
     }
 
     /*
@@ -82,12 +105,20 @@ public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
      *
      */
     @Test(groups = "fast")
-    public void testCalculateFirstBillingCycleDate3() throws Exception {
+    public void testCalculateFirstBillingCycleDate4() throws Exception {
         final LocalDate from = new LocalDate("2012-02-16");
+        final LocalDate to = null;
+        final LocalDate targetDate = new LocalDate();
         final int bcd = 14;
-        final BillingIntervalDetail billingIntervalDetail = new BillingIntervalDetail(from, null, new LocalDate(), bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
-        billingIntervalDetail.calculateFirstBillingCycleDate();
-        Assert.assertEquals(billingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2013-02-14"));
+
+        final BillingIntervalDetail annualBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.ANNUAL, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(annualBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2013-02-14"));
+
+        final BillingIntervalDetail monthlyBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.MONTHLY, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(monthlyBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-03-14"));
+
+        final BillingIntervalDetail thirtyDaysBillingIntervalDetail = new BillingIntervalDetail(from, to, targetDate, bcd, BillingPeriod.THIRTY_DAYS, BillingMode.IN_ADVANCE);
+        Assert.assertEquals(thirtyDaysBillingIntervalDetail.getFirstBillingCycleDate(), new LocalDate("2012-02-16"));
     }
 
     @Test(groups = "fast")
@@ -165,5 +196,4 @@ public class TestInAdvanceBillingIntervalDetail extends InvoiceTestSuiteNoDB {
         final LocalDate effectiveEndDate = billingIntervalDetail.getEffectiveEndDate();
         Assert.assertEquals(effectiveEndDate, new LocalDate("2012-05-31"));
     }
-
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceDateUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceDateUtils.java
index 485329b..67b1f42 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceDateUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestInvoiceDateUtils.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -18,33 +20,15 @@ package org.killbill.billing.invoice.generator;
 
 import java.math.BigDecimal;
 
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 public class TestInvoiceDateUtils extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
-    public void testLastBCDShouldNotBeBeforePreviousBCD() throws Exception {
-        final LocalDate from = new LocalDate("2012-07-16");
-        final LocalDate previousBCD = new LocalDate("2012-08-15");
-        final int bcdLocal = 15;
-        final LocalDate lastBCD = InvoiceDateUtils.calculateLastBillingCycleDateBefore(from, previousBCD, bcdLocal, BillingPeriod.MONTHLY);
-        Assert.assertEquals(lastBCD, new LocalDate("2012-08-15"));
-    }
-
-    @Test(groups = "fast")
-    public void testNextBCDShouldNotBeInThePast() throws Exception {
-        final LocalDate from = new LocalDate("2012-07-16");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 15);
-        Assert.assertEquals(to, new LocalDate("2012-08-15"));
-    }
-
-    @Test(groups = "fast")
     public void testProRationAfterLastBillingCycleDate() throws Exception {
         final LocalDate endDate = new LocalDate("2012-06-02");
         final LocalDate previousBillThroughDate = new LocalDate("2012-03-02");
@@ -53,72 +37,77 @@ public class TestInvoiceDateUtils extends InvoiceTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBeforeBCDWithAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-02");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-03-03"));
-    }
-
-    @Test(groups = "fast")
-    public void testEqualBCDWithAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-03");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-04-03"));
-    }
-
-    @Test(groups = "fast")
-    public void testAfterBCDWithAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-04");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-04-03"));
-    }
-
-    @Test(groups = "fast")
-    public void testBeforeBCDWithOnOrAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-02");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-03-03"));
-    }
-
-    @Test(groups = "fast")
-    public void testEqualBCDWithOnOrAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-03");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-03-03"));
-    }
+    public void testCalculateNbOfBillingPeriods() throws Exception {
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 15), BillingPeriod.MONTHLY), 1);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 16), BillingPeriod.MONTHLY), 2);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 17), BillingPeriod.MONTHLY), 2);
 
-    @Test(groups = "fast")
-    public void testAfterBCDWithOnOrAfter() throws Exception {
-        final LocalDate from = new LocalDate("2012-03-04");
-        final LocalDate to = InvoiceDateUtils.calculateBillingCycleDateOnOrAfter(from, 3);
-        Assert.assertEquals(to, new LocalDate("2012-04-03"));
-    }
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 13), BillingPeriod.THIRTY_DAYS), 1);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 14), BillingPeriod.THIRTY_DAYS), 2);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 9, 15), BillingPeriod.THIRTY_DAYS), 2);
 
-    @Test(groups = "fast")
-    public void testEffectiveEndDate() throws Exception {
-        final LocalDate firstBCD = new LocalDate(2012, 7, 16);
-        final LocalDate targetDate = new LocalDate(2012, 8, 16);
-        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-        final LocalDate effectiveEndDate = InvoiceDateUtils.calculateEffectiveEndDate(firstBCD, targetDate, billingPeriod);
-        // TODO should that be 2012-09-15?
-        Assert.assertEquals(effectiveEndDate, new LocalDate(2012, 9, 16));
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 7, 29), BillingPeriod.WEEKLY), 1);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 7, 30), BillingPeriod.WEEKLY), 2);
+        Assert.assertEquals(InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(new LocalDate(2012, 7, 16), new LocalDate(2012, 7, 31), BillingPeriod.WEEKLY), 2);
     }
 
     @Test(groups = "fast")
-    public void testLastBCD() throws Exception {
-        final LocalDate firstBCD = new LocalDate(2012, 7, 16);
-        final LocalDate effectiveEndDate = new LocalDate(2012, 9, 15);
-        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-        final LocalDate lastBCD = InvoiceDateUtils.calculateLastBillingCycleDateBefore(effectiveEndDate, firstBCD, 16, billingPeriod);
-        Assert.assertEquals(lastBCD, new LocalDate(2012, 8, 16));
+    public void testAdvanceByNPeriods() throws Exception {
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 0), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 1), new LocalDate(2016, 5, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 2), new LocalDate(2016, 6, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 3), new LocalDate(2016, 7, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 4), new LocalDate(2016, 8, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 5), new LocalDate(2016, 9, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 6), new LocalDate(2016, 10, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.MONTHLY, 7), new LocalDate(2016, 11, 8));
+
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 0), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 1), new LocalDate(2016, 5, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 2), new LocalDate(2016, 6, 7));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 3), new LocalDate(2016, 7, 7));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 4), new LocalDate(2016, 8, 6));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 5), new LocalDate(2016, 9, 5));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 6), new LocalDate(2016, 10, 5));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.THIRTY_DAYS, 7), new LocalDate(2016, 11, 4));
+
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 0), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 1), new LocalDate(2016, 4, 15));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 2), new LocalDate(2016, 4, 22));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 3), new LocalDate(2016, 4, 29));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 4), new LocalDate(2016, 5, 6));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 5), new LocalDate(2016, 5, 13));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 6), new LocalDate(2016, 5, 20));
+        Assert.assertEquals(InvoiceDateUtils.advanceByNPeriods(new LocalDate(2016, 4, 8), BillingPeriod.WEEKLY, 7), new LocalDate(2016, 5, 27));
     }
 
     @Test(groups = "fast")
-    public void testCalculateNbOfBillingPeriods() throws Exception {
-        final LocalDate firstBCD = new LocalDate(2012, 7, 16);
-        final LocalDate lastBCD = new LocalDate(2012, 9, 16);
-        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-        final int numberOfWholeBillingPeriods = InvoiceDateUtils.calculateNumberOfWholeBillingPeriods(firstBCD, lastBCD, billingPeriod);
-        Assert.assertEquals(numberOfWholeBillingPeriods, 2);
+    public void testRecedeByNPeriods() throws Exception {
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 7), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 6), new LocalDate(2016, 5, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 5), new LocalDate(2016, 6, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 4), new LocalDate(2016, 7, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 3), new LocalDate(2016, 8, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 2), new LocalDate(2016, 9, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 1), new LocalDate(2016, 10, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 8), BillingPeriod.MONTHLY, 0), new LocalDate(2016, 11, 8));
+
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 7), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 6), new LocalDate(2016, 5, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 5), new LocalDate(2016, 6, 7));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 4), new LocalDate(2016, 7, 7));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 3), new LocalDate(2016, 8, 6));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 2), new LocalDate(2016, 9, 5));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 1), new LocalDate(2016, 10, 5));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 11, 4), BillingPeriod.THIRTY_DAYS, 0), new LocalDate(2016, 11, 4));
+
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 7), new LocalDate(2016, 4, 8));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 6), new LocalDate(2016, 4, 15));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 5), new LocalDate(2016, 4, 22));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 4), new LocalDate(2016, 4, 29));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 3), new LocalDate(2016, 5, 6));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 2), new LocalDate(2016, 5, 13));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 1), new LocalDate(2016, 5, 20));
+        Assert.assertEquals(InvoiceDateUtils.recedeByNPeriods(new LocalDate(2016, 5, 27), BillingPeriod.WEEKLY, 0), new LocalDate(2016, 5, 27));
     }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModule.java b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModule.java
index a596d64..049446e 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModule.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/glue/TestInvoiceModule.java
@@ -29,6 +29,7 @@ import org.killbill.billing.util.email.EmailModule;
 import org.killbill.billing.util.email.templates.TemplateModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.MemoryGlobalLockerModule;
 import org.killbill.billing.util.glue.TagStoreModule;
@@ -53,6 +54,7 @@ public class TestInvoiceModule extends DefaultInvoiceModule {
 
         install(new CatalogModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new TemplateModule(configSource));
         install(new EmailModule(configSource));
         install(new MockTenantModule(configSource));
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
index 6945f02..dcd4a44 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -22,7 +22,6 @@ import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.currency.api.CurrencyConversionApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
@@ -66,8 +65,6 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected InvoicePaymentApi invoicePaymentApi;
     @Inject
-    protected InvoiceMigrationApi migrationApi;
-    @Inject
     protected InvoiceGenerator generator;
     @Inject
     protected BillingInternalApi billingApi;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index 346fa22..2f6bf60 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -24,25 +24,21 @@ import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.DefaultInvoiceService;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceService;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
-import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
 import org.killbill.billing.invoice.dao.InvoiceDao;
 import org.killbill.billing.invoice.generator.InvoiceGenerator;
 import org.killbill.billing.invoice.glue.TestInvoiceModuleWithEmbeddedDb;
 import org.killbill.billing.invoice.notification.NextBillingDateNotifier;
-import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
 import org.killbill.billing.junction.BillingInternalApi;
 import org.killbill.billing.lifecycle.api.BusService;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
@@ -75,8 +71,6 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     @Inject
     protected InvoicePaymentApi invoicePaymentApi;
     @Inject
-    protected InvoiceMigrationApi migrationApi;
-    @Inject
     protected InvoiceGenerator generator;
     @Inject
     protected BillingInternalApi billingApi;
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
index 566e80b..431a8dd 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -17,60 +19,34 @@
 package org.killbill.billing.invoice;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 import java.util.UUID;
 
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 
 public class MockBillingEventSet extends TreeSet<BillingEvent> implements BillingEventSet {
 
     private static final long serialVersionUID = 1L;
 
-    private final InternalTenantContext internalTenantContext;
-
     private boolean isAccountInvoiceOff;
     private List<UUID> subscriptionIdsWithAutoInvoiceOff;
-    private AccountDateAndTimeZoneContext accountDateAndTimeZoneContext;
 
-    public MockBillingEventSet(final InternalTenantContext internalTenantContext) {
+    public MockBillingEventSet() {
         super();
-        this.internalTenantContext = internalTenantContext;
         this.isAccountInvoiceOff = false;
         this.subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
     }
 
-    @Override
-    public boolean add(final BillingEvent e) {
-        if (accountDateAndTimeZoneContext == null) {
-            this.accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(e.getEffectiveDate(), internalTenantContext);
-        }
-        return super.add(e);
-    }
-
-    @Override
-    public boolean addAll(final Collection<? extends BillingEvent> all) {
-        if (accountDateAndTimeZoneContext == null) {
-            this.accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(all.iterator().next().getEffectiveDate(), internalTenantContext);
-        }
-        return super.addAll(all);
-    }
-
-
     public void addSubscriptionWithAutoInvoiceOff(final UUID subscriptionId) {
         subscriptionIdsWithAutoInvoiceOff.add(subscriptionId);
     }
 
-
     @Override
     public boolean isAccountAutoInvoiceOff() {
         return isAccountInvoiceOff;
@@ -87,11 +63,6 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
     }
 
     @Override
-    public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
-        return accountDateAndTimeZoneContext;
-    }
-
-    @Override
     public Map<String, Usage> getUsages() {
         return Collections.emptyMap();
     }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java b/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
index eccdb7f..a8f7d68 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/model/TestItemAdjInvoiceItem.java
@@ -32,8 +32,8 @@ public class TestItemAdjInvoiceItem extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testType() throws Exception {
-        final InvoiceItem invoiceItem = new ItemAdjInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(),
-                                                               new LocalDate(2010, 1, 1), new BigDecimal("7.00"), Currency.USD,
+        final InvoiceItem invoiceItem = new ItemAdjInvoiceItem(UUID.randomUUID(), null, UUID.randomUUID(), UUID.randomUUID(),
+                                                               new LocalDate(2010, 1, 1), null, new BigDecimal("7.00"),  Currency.USD,
                                                                UUID.randomUUID());
         Assert.assertEquals(invoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ);
     }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/GenericProRationTestBase.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/GenericProRationTestBase.java
index c05b8fe..51647a5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/GenericProRationTestBase.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/inAdvance/GenericProRationTestBase.java
@@ -1,7 +1,9 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -53,7 +55,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_OnePeriodLessADayAfterStart() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 2, 15);
-        final LocalDate targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths()).plusDays(-1);
+        final LocalDate targetDate = startDate.plus(getBillingPeriod().getPeriod()).plusDays(-1);
 
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, ONE);
     }
@@ -61,7 +63,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_ExactlyOnePeriodAfterStart() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 2, 15);
-        final LocalDate targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
+        final LocalDate targetDate = startDate.plus(getBillingPeriod().getPeriod());
 
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
@@ -69,7 +71,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_SlightlyMoreThanOnePeriodAfterStart() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 2, 15);
-        final LocalDate targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths()).plusDays(1);
+        final LocalDate targetDate = startDate.plus(getBillingPeriod().getPeriod()).plusDays(1);
 
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
@@ -77,7 +79,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_CrossingYearBoundary() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 12, 15);
-        final LocalDate oneCycleLater = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
+        final LocalDate oneCycleLater = startDate.plus(getBillingPeriod().getPeriod());
 
         // test just before the billing cycle day
         testCalculateNumberOfBillingCycles(startDate, oneCycleLater.plusDays(-1), 15, ONE);
@@ -92,7 +94,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_StartingMidFebruary() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2011, 2, 15);
-        final LocalDate targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
+        final LocalDate targetDate = startDate.plus(getBillingPeriod().getPeriod());
 
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
@@ -100,7 +102,7 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
     @Test(groups = "fast")
     public void testSinglePlan_StartingMidFebruaryOfLeapYear() throws InvalidDateSequenceException {
         final LocalDate startDate = invoiceUtil.buildDate(2012, 2, 15);
-        final LocalDate targetDate = startDate.plusMonths(getBillingPeriod().getNumberOfMonths());
+        final LocalDate targetDate = startDate.plus(getBillingPeriod().getPeriod());
 
         testCalculateNumberOfBillingCycles(startDate, targetDate, 15, TWO);
     }
@@ -111,7 +113,10 @@ public abstract class GenericProRationTestBase extends ProRationInAdvanceTestBas
         BigDecimal expectedValue = ONE;
 
         for (int i = 1; i <= 12; i++) {
-            final LocalDate oneCycleLater = startDate.plusMonths(i * getBillingPeriod().getNumberOfMonths());
+            LocalDate oneCycleLater = startDate;
+            for (int j = 0; j < i; j++) {
+                oneCycleLater = oneCycleLater.plus(getBillingPeriod().getPeriod());
+            }
             // test just before the billing cycle day
             testCalculateNumberOfBillingCycles(startDate, oneCycleLater.plusDays(-1), 31, expectedValue);
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
index f64b7ea..d526635 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -37,11 +37,11 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 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.DefaultAccountDateAndTimeZoneContext;
 import org.killbill.clock.Clock;
 import org.mockito.Mockito;
 import org.testng.Assert;
@@ -95,6 +95,7 @@ public class InvoiceTestUtils {
         Mockito.when(invoice.getTargetDate()).thenReturn(clock.getUTCToday());
         Mockito.when(invoice.getCurrency()).thenReturn(currency);
         Mockito.when(invoice.isMigrationInvoice()).thenReturn(false);
+        Mockito.when(invoice.getStatus()).thenReturn(InvoiceStatus.COMMITTED);
 
         final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
         final List<InvoiceItemModelDao> invoiceModelItems = new ArrayList<InvoiceItemModelDao>();
@@ -105,9 +106,7 @@ public class InvoiceTestUtils {
         }
         Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
 
-        final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), internalCallContext);
-
-        invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+        invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
 
         return invoice;
     }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index d55669e..34b3004 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -33,6 +33,7 @@ import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory.ResourceBundleType;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -41,7 +42,6 @@ import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import org.killbill.billing.invoice.model.DefaultInvoice;
 import org.killbill.billing.invoice.model.DefaultInvoicePayment;
 import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
-import org.killbill.billing.invoice.model.RefundAdjInvoiceItem;
 import org.killbill.billing.invoice.model.RepairAdjInvoiceItem;
 import org.killbill.billing.invoice.template.translator.DefaultInvoiceTranslator;
 import org.killbill.billing.util.email.templates.MustacheTemplateEngine;
@@ -55,14 +55,12 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     private TranslatorConfig config;
     private MustacheTemplateEngine templateEngine;
-    private DefaultInvoiceFormatterFactory defaultInvoiceFormatterFactory;
 
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {
         super.beforeClass();
         config = new ConfigurationObjectFactory(skifeConfigSource).build(TranslatorConfig.class);
         templateEngine = new MustacheTemplateEngine();
-        defaultInvoiceFormatterFactory = new DefaultInvoiceFormatterFactory();
     }
 
     @Test(groups = "fast")
@@ -81,7 +79,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                                                                                                          fixedItem.getStartDate(), fixedItem.getAmount().negate(),
                                                                                                          fixedItem.getCurrency());
         final Invoice invoice = new DefaultInvoice(fixedItem.getInvoiceId(), fixedItem.getAccountId(), null,
-                                                   new LocalDate(), new LocalDate(), Currency.USD, false);
+                                                   new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
         invoice.addInvoiceItem(fixedItem);
         invoice.addInvoiceItem(creditBalanceAdjInvoiceItem);
         invoice.addInvoiceItem(creditBalanceAdjInvoiceItem2);
@@ -91,7 +89,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         Assert.assertEquals(invoice.getCreditedAmount().doubleValue(), 0.00);
 
         // Verify the merge
-        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext, defaultInvoiceFormatterFactory.getCurrencyLocaleMap());
+        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext);
         final List<InvoiceItem> invoiceItems = formatter.getInvoiceItems();
         Assert.assertEquals(invoiceItems.size(), 1);
         Assert.assertEquals(invoiceItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
@@ -108,8 +106,6 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         // Then, the invoice is adjusted for $1:
         // * $-1 credit adjustment
         // * $1 generated CBA due to the credit adjustment
-        // Then, we refund $1 with invoice level adjustment:
-        // * $-1 refund adjustment
         final FixedPriceInvoiceItem fixedItem = new FixedPriceInvoiceItem(UUID.randomUUID(), UUID.randomUUID(), null, null,
                                                                           UUID.randomUUID().toString(), UUID.randomUUID().toString(),
                                                                           new LocalDate(), BigDecimal.TEN, Currency.USD);
@@ -121,31 +117,28 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                                                                                                         fixedItem.getStartDate(), fixedItem.getAmount(),
                                                                                                         fixedItem.getCurrency());
         final CreditAdjInvoiceItem creditAdjInvoiceItem = new CreditAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
-                                                                                   fixedItem.getStartDate(), BigDecimal.ONE.negate(), fixedItem.getCurrency());
+                                                                                   fixedItem.getStartDate(), null, BigDecimal.ONE.negate(), fixedItem.getCurrency());
         final CreditBalanceAdjInvoiceItem creditBalanceAdjInvoiceItem2 = new CreditBalanceAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
                                                                                                          fixedItem.getStartDate(), creditAdjInvoiceItem.getAmount().negate(),
                                                                                                          fixedItem.getCurrency());
-        final RefundAdjInvoiceItem refundAdjInvoiceItem = new RefundAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
-                                                                                   fixedItem.getStartDate(), BigDecimal.ONE.negate(), fixedItem.getCurrency());
         final DefaultInvoice invoice = new DefaultInvoice(fixedItem.getInvoiceId(), fixedItem.getAccountId(), null,
-                                                          new LocalDate(), new LocalDate(), Currency.USD, false);
+                                                          new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
         invoice.addInvoiceItem(fixedItem);
         invoice.addInvoiceItem(repairAdjInvoiceItem);
         invoice.addInvoiceItem(creditBalanceAdjInvoiceItem);
         invoice.addInvoiceItem(creditAdjInvoiceItem);
         invoice.addInvoiceItem(creditBalanceAdjInvoiceItem2);
-        invoice.addInvoiceItem(refundAdjInvoiceItem);
         invoice.addPayment(new DefaultInvoicePayment(InvoicePaymentType.ATTEMPT, UUID.randomUUID(), invoice.getId(), clock.getUTCNow(), BigDecimal.TEN,
                                                      Currency.USD, Currency.USD, null, true));
         invoice.addPayment(new DefaultInvoicePayment(InvoicePaymentType.REFUND, UUID.randomUUID(), invoice.getId(), clock.getUTCNow(), BigDecimal.ONE.negate(),
                                                      Currency.USD, Currency.USD, null, true));
         // Check the scenario
-        Assert.assertEquals(invoice.getBalance().doubleValue(), 0.00);
+        Assert.assertEquals(invoice.getBalance().doubleValue(), 1.00);
         Assert.assertEquals(invoice.getCreditedAmount().doubleValue(), 11.00);
         Assert.assertEquals(invoice.getRefundedAmount().doubleValue(), -1.00);
 
         // Verify the merge
-        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext, defaultInvoiceFormatterFactory.getCurrencyLocaleMap());
+        final InvoiceFormatter formatter = new DefaultInvoiceFormatter(config, invoice, Locale.US, null, resourceBundleFactory, internalCallContext);
         final List<InvoiceItem> invoiceItems = formatter.getInvoiceItems();
         Assert.assertEquals(invoiceItems.size(), 4);
         Assert.assertEquals(invoiceItems.get(0).getInvoiceItemType(), InvoiceItemType.FIXED);
@@ -155,7 +148,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
         Assert.assertEquals(invoiceItems.get(2).getInvoiceItemType(), InvoiceItemType.CBA_ADJ);
         Assert.assertEquals(invoiceItems.get(2).getAmount().doubleValue(), 11.00);
         Assert.assertEquals(invoiceItems.get(3).getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
-        Assert.assertEquals(invoiceItems.get(3).getAmount().doubleValue(), -2.00);
+        Assert.assertEquals(invoiceItems.get(3).getAmount().doubleValue(), -1.00);
     }
 
     @Test(groups = "fast")
@@ -207,13 +200,13 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                     "    <td class=\"amount\"><strong>{{invoice.formattedBalance}}</strong></td>\n" +
                     "</tr>",
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 499,958 ر.ع.\u200F</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 499,958 ر.ع</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>0,000 ر.ع.\u200F</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>0,000 ر.ع</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 499,958 ر.ع.\u200F</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 499,958 ر.ع</strong></td>\n" +
                     "</tr>",
                     Locale.FRANCE);
     }
@@ -237,13 +230,13 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                     "    <td class=\"amount\"><strong>{{invoice.formattedBalance}}</strong></td>\n" +
                     "</tr>",
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>0 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>0 ¥</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>1 500 ¥</strong></td>\n" +
                     "</tr>",
                     Locale.FRANCE);
     }
@@ -267,13 +260,13 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                     "    <td class=\"amount\"><strong>{{invoice.formattedBalance}}</strong></td>\n" +
                     "</tr>",
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>BTC1,105.28843439</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>Ƀ1,105.28843439</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>BTC0.00000000</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>Ƀ0.00000000</strong></td>\n" +
                     "</tr>\n" +
                     "<tr>\n" +
-                    "    <td class=\"amount\"><strong>BTC1,105.28843439</strong></td>\n" +
+                    "    <td class=\"amount\"><strong>Ƀ1,105.28843439</strong></td>\n" +
                     "</tr>",
                     Locale.US);
     }
@@ -392,7 +385,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testProcessedCurrencyDoesNotExist() throws Exception {
-        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.USD, false);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
 
         checkOutput(invoice,
                     "{{#invoice.processedCurrency}}" +
@@ -412,7 +405,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
                                                                              UUID.randomUUID().toString(), UUID.randomUUID().toString(),
                                                                              new LocalDate(), new BigDecimal("1499.95"), Currency.BRL);
 
-        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.BRL,  false);
+        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.BRL,  false, InvoiceStatus.COMMITTED);
         invoice.addInvoiceItem(fixedItemBRL);
 
         final String template = "<html>\n" +
@@ -518,7 +511,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
         data.put("text", translator);
 
-        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, Locale.US, currencyConversionApi, resourceBundleFactory, internalCallContext, defaultInvoiceFormatterFactory.getCurrencyLocaleMap()));
+        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, Locale.US, currencyConversionApi, resourceBundleFactory, internalCallContext));
 
         final String formattedText = templateEngine.executeTemplateText(template, data);
 
@@ -527,7 +520,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     private void checkOutput(final Invoice invoice, final String template, final String expected, final Locale locale) {
         final Map<String, Object> data = new HashMap<String, Object>();
-        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, locale, null, resourceBundleFactory, internalCallContext, defaultInvoiceFormatterFactory.getCurrencyLocaleMap()));
+        data.put("invoice", new DefaultInvoiceFormatter(config, invoice, locale, null, resourceBundleFactory, internalCallContext));
 
         final String formattedText = templateEngine.executeTemplateText(template, data);
         Assert.assertEquals(formattedText, expected);
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 f5eeeaa..cfedd3c 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceDispatcher.java
@@ -48,6 +48,7 @@ import org.killbill.billing.invoice.notification.NullInvoiceNotifier;
 import org.killbill.billing.junction.BillingEventSet;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
@@ -69,10 +70,10 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testDryRunInvoice() throws InvoiceApiException, AccountApiException, CatalogApiException {
+    public void testDryRunInvoice() throws InvoiceApiException, AccountApiException, CatalogApiException, SubscriptionBaseApiException {
         final UUID accountId = account.getId();
 
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
         final PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
         final DateTime effectiveDate = clock.getUTCNow().minusDays(1);
@@ -84,7 +85,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
 
         Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
 
-        final DateTime target = effectiveDate;
+        final LocalDate target = internalCallContext.toLocalDate(effectiveDate);
 
         final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
         final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
@@ -114,7 +115,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testWithOverdueEvents() throws Exception {
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
 
         // Initial trial
         final MockPlan bicycleTrialEvergreen1USD = MockPlan.createBicycleTrialEvergreen1USD();
@@ -144,7 +145,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
                                                                    internalCallContextFactory, invoiceNotifier, invoicePluginDispatcher, locker, busService.getBus(),
                                                                    null, invoiceConfig, clock);
 
-        final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), null, context);
+        final Invoice invoice = dispatcher.processAccount(account.getId(), new LocalDate("2012-07-30"), null, context);
         Assert.assertNotNull(invoice);
 
         final List<InvoiceItem> invoiceItems = invoice.getInvoiceItems();
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 215d650..8e22146 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -29,12 +29,14 @@ import javax.inject.Inject;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.killbill.billing.ObjectType;
+import org.killbill.billing.GuicyKillbillTestSuite;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
@@ -78,14 +80,12 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.InvoiceConfig;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 import org.killbill.billing.util.currency.KillBillMoney;
 import org.killbill.billing.util.dao.NonEntityDao;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.mockito.Mockito;
@@ -153,6 +153,7 @@ public class TestInvoiceHelper {
     private final InvoiceGenerator generator;
     private final BillingInternalApi billingApi;
     private final AccountInternalApi accountApi;
+    private final ImmutableAccountInternalApi immutableAccountApi;
     private final InvoicePluginDispatcher invoicePluginDispatcher;
     private final AccountUserApi accountUserApi;
     private final SubscriptionBaseInternalApi subscriptionApi;
@@ -161,7 +162,6 @@ public class TestInvoiceHelper {
     private final GlobalLocker locker;
     private final Clock clock;
     private final NonEntityDao nonEntityDao;
-    private final CacheControllerDispatcher cacheControllerDispatcher;
     private final MutableInternalCallContext internalCallContext;
     private final InternalCallContextFactory internalCallContextFactory;
     private final InvoiceConfig invoiceConfig;
@@ -172,12 +172,13 @@ public class TestInvoiceHelper {
 
     @Inject
     public TestInvoiceHelper(final InvoiceGenerator generator, final IDBI dbi,
-                             final BillingInternalApi billingApi, final AccountInternalApi accountApi, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
+                             final BillingInternalApi billingApi, final AccountInternalApi accountApi, final ImmutableAccountInternalApi immutableAccountApi, final InvoicePluginDispatcher invoicePluginDispatcher, final AccountUserApi accountUserApi, final SubscriptionBaseInternalApi subscriptionApi, final BusService busService,
                              final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher, final MutableInternalCallContext internalCallContext, final InvoiceConfig invoiceConfig,
                              final InternalCallContextFactory internalCallContextFactory) {
         this.generator = generator;
         this.billingApi = billingApi;
         this.accountApi = accountApi;
+        this.immutableAccountApi = immutableAccountApi;
         this.invoicePluginDispatcher = invoicePluginDispatcher;
         this.accountUserApi = accountUserApi;
         this.subscriptionApi = subscriptionApi;
@@ -186,7 +187,6 @@ public class TestInvoiceHelper {
         this.locker = locker;
         this.clock = clock;
         this.nonEntityDao = nonEntityDao;
-        this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.internalCallContext = internalCallContext;
         this.internalCallContextFactory = internalCallContextFactory;
         this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
@@ -194,11 +194,11 @@ public class TestInvoiceHelper {
         this.invoiceConfig = invoiceConfig;
     }
 
-    public UUID generateRegularInvoice(final Account account, final DateTime targetDate, final CallContext callContext) throws Exception {
+    public UUID generateRegularInvoice(final Account account, final LocalDate targetDate, final CallContext callContext) throws Exception {
         final SubscriptionBase subscription = Mockito.mock(SubscriptionBase.class);
         Mockito.when(subscription.getId()).thenReturn(UUID.randomUUID());
         Mockito.when(subscription.getBundleId()).thenReturn(new UUID(0L, 0L));
-        final BillingEventSet events = new MockBillingEventSet(internalCallContext);
+        final BillingEventSet events = new MockBillingEventSet();
         final Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
         final PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
         final DateTime effectiveDate = new DateTime().minusDays(1);
@@ -243,23 +243,28 @@ public class TestInvoiceHelper {
     }
 
     public Account createAccount(final CallContext callContext) throws AccountApiException {
-        final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
-                                                                .firstNameLength(6)
-                                                                .email(UUID.randomUUID().toString().substring(1, 8))
-                                                                .phone(UUID.randomUUID().toString().substring(1, 8))
-                                                                .migrated(false)
-                                                                .isNotifiedForInvoices(true)
-                                                                .externalKey(UUID.randomUUID().toString().substring(1, 8))
-                                                                .billingCycleDayLocal(31)
-                                                                .currency(accountCurrency)
-                                                                .paymentMethodId(UUID.randomUUID())
-                                                                .timeZone(DateTimeZone.UTC)
-                                                                .build();
-        final Account account = accountUserApi.createAccount(accountData, callContext);
-
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        final Account accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+                                                            .firstNameLength(6)
+                                                            .email(UUID.randomUUID().toString().substring(1, 8))
+                                                            .phone(UUID.randomUUID().toString().substring(1, 8))
+                                                            .migrated(false)
+                                                            .isNotifiedForInvoices(true)
+                                                            .externalKey(UUID.randomUUID().toString().substring(1, 8))
+                                                            .billingCycleDayLocal(31)
+                                                            .currency(accountCurrency)
+                                                            .paymentMethodId(UUID.randomUUID())
+                                                            .timeZone(DateTimeZone.UTC)
+                                                            .createdDate(clock.getUTCNow())
+                                                            .build();
+
+        final Account account;
+        if (isFastTest()) {
+            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountUserApi, accountApi, immutableAccountApi, nonEntityDao, clock, internalCallContextFactory, callContext, internalCallContext);
+        } else {
+            account = accountUserApi.createAccount(accountData, callContext);
+        }
+
+        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, callContext, internalCallContext);
 
         return account;
     }
@@ -303,8 +308,7 @@ public class TestInvoiceHelper {
                                                                                                                                          }));
 
         // The test does not use the invoice callback notifier hence the empty map
-        final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), internalCallContext);
-        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
+        invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
     }
 
     public void createPayment(final InvoicePayment invoicePayment, final InternalCallContext internalCallContext) {
@@ -389,7 +393,7 @@ public class TestInvoiceHelper {
             }
 
             @Override
-            public BigDecimal getRecurringPrice() {
+            public BigDecimal getRecurringPrice(DateTime effectiveDate) {
                 return recurringPrice;
             }
 
@@ -459,7 +463,7 @@ public class TestInvoiceHelper {
         }
 
         @Override
-        public DateTime getEffectiveDate() {
+        public LocalDate getEffectiveDate() {
             return null;
         }
 
@@ -478,4 +482,9 @@ public class TestInvoiceHelper {
             return null;
         }
     }
+
+    // Unfortunately, this helper is shared across fast and slow tests
+    private boolean isFastTest() {
+        return Mockito.mockingDetails(accountApi).isMock();
+    }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceNotificationQListener.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceNotificationQListener.java
index 9cda9fa..128616f 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceNotificationQListener.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceNotificationQListener.java
@@ -23,6 +23,7 @@ import javax.inject.Inject;
 import org.joda.time.DateTime;
 
 import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.clock.Clock;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 
@@ -32,8 +33,8 @@ public class TestInvoiceNotificationQListener extends InvoiceListener {
     UUID latestSubscriptionId = null;
 
     @Inject
-    public TestInvoiceNotificationQListener(final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory, final InvoiceDispatcher dispatcher) {
-        super(accountApi, clock, internalCallContextFactory, null, dispatcher);
+    public TestInvoiceNotificationQListener(final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory, final InvoiceDispatcher dispatcher, final InvoiceInternalApi invoiceApi) {
+        super(accountApi, clock, internalCallContextFactory, null, dispatcher, invoiceApi);
     }
 
     @Override
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
index 102f9c7..edc9cf5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/tree/TestSubscriptionItemTree.java
@@ -394,6 +394,189 @@ public class TestSubscriptionItemTree extends InvoiceTestSuiteNoDB {
         verifyResult(tree.getView(), expectedResult);
     }
 
+    // Will test the case A from ItemsNodeInterval#prune logic (when we delete a node while walking the tree)
+    @Test(groups = "fast")
+    public void testFullRepairPruneLogic1() {
+
+        final LocalDate startDate1 = new LocalDate(2015, 1, 1);
+        final LocalDate endDate1 = new LocalDate(2015, 2, 1);
+
+
+        final LocalDate startDate2 = endDate1;
+        final LocalDate endDate2 = new LocalDate(2015, 3, 1);
+
+        final LocalDate startDate3 = endDate2;
+        final LocalDate endDate3 = new LocalDate(2015, 4, 1);
+
+        final BigDecimal monthlyRate = new BigDecimal("12.00");
+        final BigDecimal monthlyAmount = monthlyRate;
+
+        final InvoiceItem monthly1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate1, endDate1, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem monthly2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate2, endDate2, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem repairMonthly2 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate2, endDate2, new BigDecimal("-12.00"), currency, monthly2.getId());
+        final InvoiceItem monthly3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate3, endDate3, monthlyAmount, monthlyRate, currency);
+
+        final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+        expectedResult.add(monthly1);
+        expectedResult.add(monthly3);
+
+        final SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId, invoiceId);
+        tree.addItem(monthly1);
+        tree.addItem(monthly2);
+        tree.addItem(repairMonthly2);
+        tree.addItem(monthly3);
+
+        tree.build();
+        verifyResult(tree.getView(), expectedResult);
+    }
+
+
+    // Will test the case A from ItemsNodeInterval#prune logic (an item is left on the interval)
+    @Test(groups = "fast")
+    public void testFullRepairPruneLogic2() {
+
+        final LocalDate startDate1 = new LocalDate(2015, 1, 1);
+        final LocalDate endDate1 = new LocalDate(2015, 2, 1);
+
+
+        final LocalDate startDate2 = endDate1;
+        final LocalDate endDate2 = new LocalDate(2015, 3, 1);
+
+        final LocalDate startDate3 = endDate2;
+        final LocalDate endDate3 = new LocalDate(2015, 4, 1);
+
+        final BigDecimal monthlyRateInit = new BigDecimal("12.00");
+        final BigDecimal monthlyAmountInit = monthlyRateInit;
+
+        final BigDecimal monthlyRateFinal = new BigDecimal("15.00");
+        final BigDecimal monthlyAmountFinal = monthlyRateFinal;
+
+        final InvoiceItem monthly1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate1, endDate1, monthlyRateInit, monthlyAmountInit, currency);
+        final InvoiceItem monthly2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate2, endDate2, monthlyRateInit, monthlyAmountInit, currency);
+        final InvoiceItem repairMonthly2 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate2, endDate2, new BigDecimal("-12.00"), currency, monthly2.getId());
+
+        final InvoiceItem monthly2New = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate2, endDate2, monthlyRateFinal, monthlyAmountFinal, currency);
+
+        final InvoiceItem monthly3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate3, endDate3, monthlyRateFinal, monthlyAmountFinal, currency);
+
+        final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+        expectedResult.add(monthly1);
+        expectedResult.add(monthly2New);
+        expectedResult.add(monthly3);
+
+        final SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId, invoiceId);
+        tree.addItem(monthly1);
+        tree.addItem(monthly2);
+        tree.addItem(repairMonthly2);
+        tree.addItem(monthly2New);
+        tree.addItem(monthly3);
+
+        tree.build();
+        verifyResult(tree.getView(), expectedResult);
+
+    }
+
+
+    // Will test the case B from ItemsNodeInterval#prune logic
+    @Test(groups = "fast")
+    public void testFullRepairByPartsPruneLogic1() {
+
+        final LocalDate startDate = new LocalDate(2015, 2, 1);
+        final LocalDate intermediate1 = new LocalDate(2015, 2, 8);
+        final LocalDate intermediate2 = new LocalDate(2015, 2, 16);
+        final LocalDate intermediate3 = new LocalDate(2015, 2, 24);
+        final LocalDate endDate = new LocalDate(2015, 3, 1);
+
+        final BigDecimal monthlyRate = new BigDecimal("12.00");
+        final BigDecimal monthlyAmount = monthlyRate;
+
+
+        final InvoiceItem monthly1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem repair11 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate, intermediate1, new BigDecimal("3.00"), currency, monthly1.getId());
+        final InvoiceItem repair12 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate1, intermediate2, new BigDecimal("3.00"), currency, monthly1.getId());
+        final InvoiceItem repair13 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate2, intermediate3, new BigDecimal("3.00"), currency, monthly1.getId());
+        final InvoiceItem repair14 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate3, endDate, new BigDecimal("3.00"), currency, monthly1.getId());
+
+        final InvoiceItem monthly2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem repair21 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate, intermediate1, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair22 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate1, intermediate2, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair23 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate2, intermediate3, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair24 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate3, endDate, new BigDecimal("3.00"), currency, monthly2.getId());
+
+        final InvoiceItem monthly3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+
+
+        final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+        expectedResult.add(monthly3);
+
+        // First test with items in order
+        final SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId, invoiceId);
+        tree.addItem(monthly1);
+        tree.addItem(repair11);
+        tree.addItem(repair12);
+        tree.addItem(repair13);
+        tree.addItem(repair14);
+
+        tree.addItem(monthly2);
+        tree.addItem(repair21);
+        tree.addItem(repair22);
+        tree.addItem(repair23);
+        tree.addItem(repair24);
+
+        tree.addItem(monthly3);
+
+        tree.build();
+        verifyResult(tree.getView(), expectedResult);
+    }
+
+    // Will test the case A and B from ItemsNodeInterval#prune logic
+    @Test(groups = "fast")
+    public void testFullRepairByPartsPruneLogic2() {
+
+        final LocalDate startDate = new LocalDate(2015, 2, 1);
+        final LocalDate intermediate1 = new LocalDate(2015, 2, 8);
+        final LocalDate intermediate2 = new LocalDate(2015, 2, 16);
+        final LocalDate intermediate3 = new LocalDate(2015, 2, 24);
+        final LocalDate endDate = new LocalDate(2015, 3, 1);
+
+        final BigDecimal monthlyRate = new BigDecimal("12.00");
+        final BigDecimal monthlyAmount = monthlyRate;
+
+
+        final InvoiceItem monthly1 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem repair11 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate, endDate, new BigDecimal("-12.00"), currency, monthly1.getId());
+
+        final InvoiceItem monthly2 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+        final InvoiceItem repair21 = new RepairAdjInvoiceItem(invoiceId, accountId, startDate, intermediate1, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair22 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate1, intermediate2, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair23 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate2, intermediate3, new BigDecimal("3.00"), currency, monthly2.getId());
+        final InvoiceItem repair24 = new RepairAdjInvoiceItem(invoiceId, accountId, intermediate3, endDate, new BigDecimal("3.00"), currency, monthly2.getId());
+
+        final InvoiceItem monthly3 = new RecurringInvoiceItem(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, monthlyAmount, monthlyRate, currency);
+
+
+        final List<InvoiceItem> expectedResult = Lists.newLinkedList();
+        expectedResult.add(monthly3);
+
+        // First test with items in order
+        final SubscriptionItemTree tree = new SubscriptionItemTree(subscriptionId, invoiceId);
+        tree.addItem(monthly1);
+        tree.addItem(repair11);
+
+        tree.addItem(monthly2);
+        tree.addItem(repair21);
+        tree.addItem(repair22);
+        tree.addItem(repair23);
+        tree.addItem(repair24);
+
+        tree.addItem(monthly3);
+
+        tree.build();
+        verifyResult(tree.getView(), expectedResult);
+    }
+
+
+
     @Test(groups = "fast")
     public void testMergeWithNoExisting() {
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestRawUsageOptimizer.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestRawUsageOptimizer.java
index eac8b46..292ec21 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestRawUsageOptimizer.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestRawUsageOptimizer.java
@@ -50,7 +50,7 @@ public class TestRawUsageOptimizer extends TestUsageInArrearBase {
         final DefaultUsage usage = createDefaultUsage(usageName, BillingPeriod.MONTHLY, tier);
         knownUsage.put(usageName, usage);
 
-        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, firstEventStartDate.plusDays(1), invoiceItems, knownUsage);
+        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, firstEventStartDate.plusDays(1), invoiceItems, knownUsage, internalCallContext);
         Assert.assertEquals(result.compareTo(firstEventStartDate), 0);
     }
 
@@ -69,7 +69,7 @@ public class TestRawUsageOptimizer extends TestUsageInArrearBase {
         final DefaultUsage usage = createDefaultUsage(usageName, BillingPeriod.MONTHLY, tier);
         knownUsage.put(usageName, usage);
 
-        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage);
+        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage, internalCallContext);
         // The largest endDate for ii is 2014-04-15, and by default org.killbill.invoice.readMaxRawUsagePreviousPeriod == 2 => targetDate =>  2014-02-15,
         // so we default to firstEventStartDate = 2014-03-15
         Assert.assertEquals(result.compareTo(firstEventStartDate), 0);
@@ -92,7 +92,7 @@ public class TestRawUsageOptimizer extends TestUsageInArrearBase {
         final DefaultUsage usage = createDefaultUsage(usageName, BillingPeriod.MONTHLY, tier);
         knownUsage.put(usageName, usage);
 
-        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage);
+        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage, internalCallContext);
         // The largest endDate for ii is 2014-08-15, and by default org.killbill.invoice.readMaxRawUsagePreviousPeriod == 2 => targetDate =>  2014-06-15
         Assert.assertEquals(result.compareTo(new LocalDate(2014, 06, 15)), 0, "112 got " + result);
     }
@@ -119,7 +119,7 @@ public class TestRawUsageOptimizer extends TestUsageInArrearBase {
         final DefaultUsage usage2 = createDefaultUsage("usageName2", BillingPeriod.ANNUAL, tier2);
         knownUsage.put("usageName2", usage2);
 
-        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage);
+        final LocalDate result = rawUsageOptimizer.getOptimizedRawUsageStartDate(firstEventStartDate, targetDate, invoiceItems, knownUsage, internalCallContext);
         // The same reasoning applies as previously because there is no usage items against the annual and
         // so, the largest endDate for ii is 2014-08-15, and by default org.killbill.invoice.readMaxRawUsagePreviousPeriod == 2 => targetDate =>  2014-06-15
         Assert.assertEquals(result.compareTo(new LocalDate(2014, 06, 15)), 0, "142 got " + result);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
index 506d4ce..19a4b45 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -28,8 +29,6 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.usage.RawUsage;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -75,9 +74,7 @@ public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
 
         LocalDate targetDate = new LocalDate(2013, 6, 23);
 
-        final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), internalCallContext);
-
-        final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), accountDateAndTimeZoneContext);
+        final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), internalCallContext);
         final List<ContiguousIntervalConsumableInArrear> result = foo.computeInArrearUsageInterval();
         assertEquals(result.size(), 3);
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
index 1277a52..838854d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
@@ -1,7 +1,8 @@
 /*
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -41,8 +42,6 @@ import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.usage.RawUsage;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 import org.mockito.Mockito;
 import org.testng.annotations.BeforeClass;
 
@@ -72,10 +71,9 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
         currency = Currency.BTC;
     }
 
-    protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, final BillingEvent... events) {
-        final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), internalCallContext);
-        final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawUsages, targetDate, new LocalDate(events[0].getEffectiveDate()), accountDateAndTimeZoneContext);
-        for (BillingEvent event : events) {
+    protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, final List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, final BillingEvent... events) {
+        final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawUsages, targetDate, new LocalDate(events[0].getEffectiveDate()), internalCallContext);
+        for (final BillingEvent event : events) {
             intervalConsumableInArrear.addBillingEvent(event);
         }
         intervalConsumableInArrear.build(closedInterval);
@@ -93,7 +91,7 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
     }
 
     protected DefaultTier createDefaultTier(final DefaultTieredBlock... blocks) {
-        DefaultTier tier = new DefaultTier();
+        final DefaultTier tier = new DefaultTier();
         tier.setBlocks(blocks);
         return tier;
     }
@@ -112,7 +110,7 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
         return block;
     }
 
-    protected BillingEvent createMockBillingEvent(DateTime effectiveDate, BillingPeriod billingPeriod, final List<Usage> usages) {
+    protected BillingEvent createMockBillingEvent(final DateTime effectiveDate, final BillingPeriod billingPeriod, final List<Usage> usages) {
         final BillingEvent result = Mockito.mock(BillingEvent.class);
         Mockito.when(result.getCurrency()).thenReturn(Currency.BTC);
         Mockito.when(result.getBillCycleDayLocal()).thenReturn(BCD);

jaxrs/pom.xml 16(+10 -6)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 4166b7e..45a9f4b 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
@@ -59,7 +59,7 @@
             <artifactId>jersey-core</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.wordnik</groupId>
+            <groupId>io.swagger</groupId>
             <artifactId>swagger-annotations</artifactId>
         </dependency>
         <dependency>
@@ -72,14 +72,14 @@
             <artifactId>javax.servlet-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>javax.ws.rs</groupId>
-            <artifactId>jsr311-api</artifactId>
-        </dependency>
-        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.shiro</groupId>
             <artifactId>shiro-core</artifactId>
         </dependency>
@@ -173,6 +173,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.skife.config</groupId>
+            <artifactId>config-magic</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java
index e8292d4..083a739 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java
@@ -22,7 +22,7 @@ import org.killbill.billing.jaxrs.JaxrsExecutors;
 import org.killbill.billing.jaxrs.JaxrsService;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.billing.util.config.definition.JaxrsConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java
index b2625f3..9ec7eea 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java
@@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
-import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.billing.util.config.definition.JaxrsConfig;
 import org.killbill.commons.concurrent.WithProfilingThreadPoolExecutor;
 
 public class JaxrsExecutors {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountEmailJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountEmailJson.java
index 5d91029..4375643 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountEmailJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountEmailJson.java
@@ -23,7 +23,7 @@ import org.killbill.billing.account.api.AccountEmail;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class AccountEmailJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
index 91763c0..e3d3e80 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AccountJson.java
@@ -33,7 +33,7 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Strings;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class AccountJson extends JsonBase {
 
@@ -48,6 +48,9 @@ public class AccountJson extends JsonBase {
     private final Integer billCycleDayLocal;
     private final String currency;
     @ApiModelProperty(dataType = "java.util.UUID")
+    private final String parentAccountId;
+    private final Boolean isPaymentDelegatedToParent;
+    @ApiModelProperty(dataType = "java.util.UUID")
     private final String paymentMethodId;
     private final String timeZone;
     private final String address1;
@@ -59,6 +62,7 @@ public class AccountJson extends JsonBase {
     private final String country;
     private final String locale;
     private final String phone;
+    private final String notes;
     private final Boolean isMigrated;
     private final Boolean isNotifiedForInvoices;
 
@@ -73,6 +77,8 @@ public class AccountJson extends JsonBase {
         this.email = account.getEmail();
         this.billCycleDayLocal = account.getBillCycleDayLocal();
         this.currency = account.getCurrency() != null ? account.getCurrency().toString() : null;
+        this.parentAccountId = account.getParentAccountId() != null ? account.getParentAccountId().toString() : null;
+        this.isPaymentDelegatedToParent = account.isPaymentDelegatedToParent();
         this.paymentMethodId = account.getPaymentMethodId() != null ? account.getPaymentMethodId().toString() : null;
         this.timeZone = account.getTimeZone() != null ? account.getTimeZone().toString() : null;
         this.address1 = account.getAddress1();
@@ -84,6 +90,7 @@ public class AccountJson extends JsonBase {
         this.country = account.getCountry();
         this.locale = account.getLocale();
         this.phone = account.getPhone();
+        this.notes = account.getNotes();
         this.isMigrated = account.isMigrated();
         this.isNotifiedForInvoices = account.isNotifiedForInvoices();
     }
@@ -96,6 +103,8 @@ public class AccountJson extends JsonBase {
                        @JsonProperty("email") final String email,
                        @JsonProperty("billCycleDayLocal") final Integer billCycleDayLocal,
                        @JsonProperty("currency") final String currency,
+                       @JsonProperty("parentAccountId") final String parentAccountId,
+                       @JsonProperty("isPaymentDelegatedToParent") final Boolean isPaymentDelegatedToParent,
                        @JsonProperty("paymentMethodId") final String paymentMethodId,
                        @JsonProperty("timeZone") final String timeZone,
                        @JsonProperty("address1") final String address1,
@@ -107,6 +116,7 @@ public class AccountJson extends JsonBase {
                        @JsonProperty("country") final String country,
                        @JsonProperty("locale") final String locale,
                        @JsonProperty("phone") final String phone,
+                       @JsonProperty("notes") final String notes,
                        @JsonProperty("isMigrated") final Boolean isMigrated,
                        @JsonProperty("isNotifiedForInvoices") final Boolean isNotifiedForInvoices,
                        @JsonProperty("accountBalance") final BigDecimal accountBalance,
@@ -121,6 +131,8 @@ public class AccountJson extends JsonBase {
         this.email = email;
         this.billCycleDayLocal = billCycleDayLocal;
         this.currency = currency;
+        this.parentAccountId = parentAccountId;
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
         this.paymentMethodId = paymentMethodId;
         this.timeZone = timeZone;
         this.address1 = address1;
@@ -132,6 +144,7 @@ public class AccountJson extends JsonBase {
         this.country = country;
         this.locale = locale;
         this.phone = phone;
+        this.notes = notes;
         this.isMigrated = isMigrated;
         this.isNotifiedForInvoices = isNotifiedForInvoices;
         this.accountCBA = accountCBA;
@@ -164,6 +177,11 @@ public class AccountJson extends JsonBase {
             }
 
             @Override
+            public String getNotes() {
+                return notes;
+            }
+
+            @Override
             public Boolean isMigrated() {
                 return isMigrated;
             }
@@ -245,6 +263,20 @@ public class AccountJson extends JsonBase {
             public String getAddress1() {
                 return address1;
             }
+
+            @Override
+            public UUID getParentAccountId() {
+                if (Strings.emptyToNull(parentAccountId) == null) {
+                    return null;
+                } else {
+                    return UUID.fromString(parentAccountId);
+                }
+            }
+
+            @Override
+            public Boolean isPaymentDelegatedToParent() {
+                return isPaymentDelegatedToParent;
+            }
         };
     }
 
@@ -284,6 +316,15 @@ public class AccountJson extends JsonBase {
         return currency;
     }
 
+    public String getParentAccountId() {
+        return parentAccountId;
+    }
+
+    @JsonProperty("isPaymentDelegatedToParent")
+    public Boolean isPaymentDelegatedToParent() {
+        return isPaymentDelegatedToParent;
+    }
+
     public String getPaymentMethodId() {
         return paymentMethodId;
     }
@@ -328,6 +369,10 @@ public class AccountJson extends JsonBase {
         return phone;
     }
 
+    public String getNotes() {
+        return notes;
+    }
+
     @JsonProperty("isMigrated")
     public Boolean isMigrated() {
         return isMigrated;
@@ -350,6 +395,8 @@ public class AccountJson extends JsonBase {
                ", email='" + email + '\'' +
                ", billCycleDayLocal=" + billCycleDayLocal +
                ", currency='" + currency + '\'' +
+               ", parentAccountId=" + parentAccountId + '\'' +
+               ", isPaymentDelegatedToParent=" + isPaymentDelegatedToParent + '\'' +
                ", paymentMethodId='" + paymentMethodId + '\'' +
                ", timeZone='" + timeZone + '\'' +
                ", address1='" + address1 + '\'' +
@@ -361,6 +408,7 @@ public class AccountJson extends JsonBase {
                ", country='" + country + '\'' +
                ", locale='" + locale + '\'' +
                ", phone='" + phone + '\'' +
+               ", notes='" + notes + '\'' +
                ", isMigrated=" + isMigrated +
                ", isNotifiedForInvoices=" + isNotifiedForInvoices +
                '}';
@@ -407,6 +455,12 @@ public class AccountJson extends JsonBase {
         if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
+        if (parentAccountId != null ? !parentAccountId.equals(that.parentAccountId) : that.parentAccountId != null) {
+            return false;
+        }
+        if (isPaymentDelegatedToParent != null ? !isPaymentDelegatedToParent.equals(that.isPaymentDelegatedToParent) : that.isPaymentDelegatedToParent != null) {
+            return false;
+        }
         if (email != null ? !email.equals(that.email) : that.email != null) {
             return false;
         }
@@ -434,6 +488,9 @@ public class AccountJson extends JsonBase {
         if (phone != null ? !phone.equals(that.phone) : that.phone != null) {
             return false;
         }
+        if (notes != null ? !notes.equals(that.notes) : that.notes != null) {
+            return false;
+        }
         if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) {
             return false;
         }
@@ -458,6 +515,8 @@ public class AccountJson extends JsonBase {
         result = 31 * result + (email != null ? email.hashCode() : 0);
         result = 31 * result + (billCycleDayLocal != null ? billCycleDayLocal.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (parentAccountId != null ? parentAccountId.hashCode() : 0);
+        result = 31 * result + (isPaymentDelegatedToParent != null ? isPaymentDelegatedToParent.hashCode() : 0);
         result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
         result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
         result = 31 * result + (address1 != null ? address1.hashCode() : 0);
@@ -469,6 +528,7 @@ public class AccountJson extends JsonBase {
         result = 31 * result + (country != null ? country.hashCode() : 0);
         result = 31 * result + (locale != null ? locale.hashCode() : 0);
         result = 31 * result + (phone != null ? phone.hashCode() : 0);
+        result = 31 * result + (notes != null ? notes.hashCode() : 0);
         result = 31 * result + (isMigrated != null ? isMigrated.hashCode() : 0);
         result = 31 * result + (isNotifiedForInvoices != null ? isNotifiedForInvoices.hashCode() : 0);
         return result;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
index 951e271..05813b1 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/AuditLogJson.java
@@ -22,7 +22,7 @@ import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class AuditLogJson {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java
index 3d4cd25..f594266 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java
@@ -22,12 +22,15 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class BlockingStateJson extends JsonBase {
 
@@ -38,7 +41,7 @@ public class BlockingStateJson extends JsonBase {
     private final Boolean blockChange;
     private final Boolean blockEntitlement;
     private final Boolean blockBilling;
-    private final LocalDate effectiveDate;
+    private final DateTime effectiveDate;
     private final BlockingStateType type;
 
     @JsonCreator
@@ -48,7 +51,7 @@ public class BlockingStateJson extends JsonBase {
                              @JsonProperty("blockChange") final Boolean blockChange,
                              @JsonProperty("blockEntitlement") final Boolean blockEntitlement,
                              @JsonProperty("blockBilling") final Boolean blockBilling,
-                             @JsonProperty("effectiveDate") final LocalDate effectiveDate,
+                             @JsonProperty("effectiveDate") final DateTime effectiveDate,
                              @JsonProperty("type") final BlockingStateType type,
                              @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
@@ -62,6 +65,19 @@ public class BlockingStateJson extends JsonBase {
         this.type = type;
     }
 
+    public BlockingStateJson(final BlockingState input, final AccountAuditLogs accountAuditLogs) {
+        this(input.getBlockedId().toString(),
+             input.getStateName(),
+             input.getService(),
+             input.isBlockChange(),
+             input.isBlockEntitlement(),
+             input.isBlockBilling(),
+             input.getEffectiveDate(),
+             input.getType(),
+             toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForBlockingState(input.getId())));
+    }
+
+
     public String getBlockedId() {
         return blockedId;
     }
@@ -86,7 +102,7 @@ public class BlockingStateJson extends JsonBase {
         return blockBilling;
     }
 
-    public LocalDate getEffectiveDate() {
+    public DateTime getEffectiveDate() {
         return effectiveDate;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleJson.java
index 0dbd4b5..ebc5d70 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleJson.java
@@ -31,7 +31,7 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class BundleJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleTimelineJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleTimelineJson.java
index 9dac4cb..569cdfa 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleTimelineJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BundleTimelineJson.java
@@ -28,7 +28,7 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class BundleTimelineJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
index 4a0f4f2..1f44b5d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
@@ -20,6 +20,7 @@ package org.killbill.billing.jaxrs.json;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -27,21 +28,24 @@ import java.util.List;
 import java.util.Map;
 
 import org.joda.time.DateTime;
-import org.killbill.billing.catalog.DefaultPriceListSet;
-import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.CurrencyValueNull;
 import org.killbill.billing.catalog.api.Duration;
+import org.killbill.billing.catalog.api.InternationalPrice;
+import org.killbill.billing.catalog.api.Limit;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.Price;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.Tier;
+import org.killbill.billing.catalog.api.TieredBlock;
 import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.catalog.api.Usage;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -77,7 +81,7 @@ public class CatalogJson {
         currencies = Arrays.asList(catalog.getSupportedCurrencies(requestedDate));
         priceLists = new ArrayList<PriceListJson>();
 
-        final Plan[] plans = catalog.getPlans(requestedDate);
+        final Collection<Plan> plans = catalog.getPlans(requestedDate);
         final Map<String, ProductJson> productMap = new HashMap<String, ProductJson>();
         for (final Plan plan : plans) {
             // Build the product associated with this plan
@@ -109,7 +113,8 @@ public class CatalogJson {
                 }
 
                 final DurationJson durationJson = new DurationJson(phase.getDuration().getUnit(), phase.getDuration().getNumber());
-                final PhaseJson phaseJson = new PhaseJson(phase.getPhaseType().toString(), prices, fixedPrices, durationJson);
+                final List<UsageJson> usagesJson = buildUsagesJson(phase.getUsages());
+                final PhaseJson phaseJson = new PhaseJson(phase.getPhaseType().toString(), prices, fixedPrices, durationJson, usagesJson);
                 phases.add(phaseJson);
             }
 
@@ -126,7 +131,67 @@ public class CatalogJson {
 
     }
 
-    private List<String> toProductNames(final Product[] in) {
+    private List<UsageJson> buildUsagesJson(final Usage[] usages) throws CurrencyValueNull {
+        List<UsageJson> usagesJson = new ArrayList<UsageJson>();
+        if (usages != null && usages.length > 0) {
+            for (int i=0; i < usages.length; i++) {
+                usagesJson.add(new UsageJson(usages[i].getBillingPeriod().toString(), buildTiers(usages[i].getTiers())));
+            }
+        }
+        return usagesJson;
+    }
+
+    private List<TierJson> buildTiers(final Tier[] tiers) throws CurrencyValueNull {
+        List<TierJson> tiersJson = new ArrayList<TierJson>();
+        if (tiers != null && tiers.length > 0) {
+            for (int i=0; i < tiers.length; i++) {
+                tiersJson.add(new TierJson(buildTieredBlocks(tiers[i].getTieredBlocks()),
+                                           buildLimits(tiers[i].getLimits()),
+                                           buildPrices(tiers[i].getFixedPrice()),
+                                           buildPrices(tiers[i].getRecurringPrice())));
+            }
+        }
+        return tiersJson;
+    }
+
+    private List<LimitJson> buildLimits(final Limit[] limits) {
+        List<LimitJson> limitsJson = new ArrayList<LimitJson>();
+        if (limits != null && limits.length > 0) {
+            for (int i=0; i < limits.length; i++) {
+                limitsJson.add(new LimitJson(limits[i].getUnit().getName(),
+                                             limits[i].getMax().toString(),
+                                             limits[i].getMin().toString()));
+            }
+        }
+        return limitsJson;
+    }
+
+    private List<TieredBlockJson> buildTieredBlocks(final TieredBlock[] tieredBlocks) throws CurrencyValueNull {
+        List<TieredBlockJson> tieredBlocksJson = new ArrayList<TieredBlockJson>();
+        if (tieredBlocks != null && tieredBlocks.length > 0) {
+            for (int i=0; i < tieredBlocks.length; i++) {
+                tieredBlocksJson.add(new TieredBlockJson(tieredBlocks[i].getUnit().getName(),
+                                                         tieredBlocks[i].getSize().toString(),
+                                                         tieredBlocks[i].getMax().toString(),
+                                                         buildPrices(tieredBlocks[i].getPrice())));
+            }
+        }
+        return tieredBlocksJson;
+    }
+
+    private List<PriceJson> buildPrices(final InternationalPrice internationalPrice) throws CurrencyValueNull {
+        List<PriceJson> pricesJson = new ArrayList<PriceJson>();
+        Price[] prices = (internationalPrice != null) ? internationalPrice.getPrices() : null;
+        if (prices != null && prices.length > 0) {
+            for (int i=0; i < prices.length; i++) {
+                pricesJson.add(new PriceJson(prices[i].getCurrency().name(),
+                                             prices[i].getValue()));
+            }
+        }
+        return pricesJson;
+    }
+
+    private List<String> toProductNames(final Collection<Product> in) {
         return Lists.transform(ImmutableList.<Product>copyOf(in),
                                new Function<Product, String>() {
                                    @Override
@@ -375,22 +440,304 @@ public class CatalogJson {
         }
     }
 
+    public static class TieredBlockJson {
+        private final String unit;
+        private final String size;
+        private final String max;
+        private final List<PriceJson> prices;
+
+        @JsonCreator
+        public TieredBlockJson(@JsonProperty("unit") final String unit,
+                               @JsonProperty("size") final String size,
+                               @JsonProperty("max") final String max,
+                               @JsonProperty("prices") final List<PriceJson> prices) {
+            this.unit = unit;
+            this.size = size;
+            this.max = max;
+            this.prices = prices;
+        }
+
+        public String getUnit() {
+            return unit;
+        }
+        public String getSize() {
+            return size;
+        }
+        public String getMax() {
+            return max;
+        }
+        public List<PriceJson> getPrices() {
+            return prices;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("TieredBlockJson{");
+            sb.append("unit='").append(unit).append('\'');
+            sb.append(", size=").append(size);
+            sb.append(", max=").append(max);
+            sb.append(", prices=").append(prices);
+            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 TieredBlockJson blockJson = (TieredBlockJson) o;
+
+            if (unit != null ? !unit.equals(blockJson.unit) : blockJson.unit != null) {
+                return false;
+            }
+            if (size != null ? !size.equals(blockJson.size) : blockJson.size != null) {
+                return false;
+            }
+            if (max != null ? !max.equals(blockJson.max) : blockJson.max != null) {
+                return false;
+            }
+            if (prices != null ? !prices.equals(blockJson.prices) : blockJson.prices != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = unit != null ? unit.hashCode() : 0;
+            result = 31 * result + (size != null ? size.hashCode() : 0);
+            result = 31 * result + (max != null ? max.hashCode() : 0);
+            result = 31 * result + (prices != null ? prices.hashCode() : 0);
+            return result;
+        }
+    }
+
+    public static class LimitJson {
+        private final String unit;
+        private final String max;
+        private final String min;
+
+        @JsonCreator
+        public LimitJson(@JsonProperty("unit") final String unit,
+                         @JsonProperty("max") final String max,
+                         @JsonProperty("min") final String min) {
+            this.unit = unit;
+            this.max = max;
+            this.min = min;
+        }
+
+        public String getUnit() {
+            return unit;
+        }
+        public String getMax() {
+            return max;
+        }
+        public String getMin() {
+            return min;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("LimitJson{");
+            sb.append("unit='").append(unit).append('\'');
+            sb.append(", max=").append(max);
+            sb.append(", min=").append(min);
+            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 LimitJson limitJson = (LimitJson) o;
+
+            if (unit != null ? !unit.equals(limitJson.unit) : limitJson.unit != null) {
+                return false;
+            }
+            if (min != null ? !min.equals(limitJson.min) : limitJson.min != null) {
+                return false;
+            }
+            if (max != null ? !max.equals(limitJson.max) : limitJson.max != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = unit != null ? unit.hashCode() : 0;
+            result = 31 * result + (max != null ? max.hashCode() : 0);
+            result = 31 * result + (min != null ? min.hashCode() : 0);
+            return result;
+        }
+    }
+
+    public static class TierJson {
+        private final List<TieredBlockJson> blocks;
+        private final List<LimitJson> limits;
+        private final List<PriceJson> fixedPrice;
+        private final List<PriceJson> recurringPrice;
+
+        @JsonCreator
+        public TierJson(@JsonProperty("tiers") final List<TieredBlockJson> blocks,
+                        @JsonProperty("limits") final List<LimitJson> limits,
+                        @JsonProperty("fixedPrice") final List<PriceJson> fixedPrice,
+                        @JsonProperty("recurringPrice") final List<PriceJson> recurringPrice) {
+            this.blocks = blocks;
+            this.limits = limits;
+            this.fixedPrice = fixedPrice;
+            this.recurringPrice = recurringPrice;
+        }
+
+        public List<TieredBlockJson> getBlocks() {
+            return blocks;
+        }
+        public List<LimitJson> getLimits() {
+            return limits;
+        }
+        public List<PriceJson> getFixedPrice() {
+            return fixedPrice;
+        }
+        public List<PriceJson> getRecurringPrice() {
+            return recurringPrice;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("TierJson{");
+            sb.append("blocks='").append(blocks);
+            sb.append(", limits=").append(limits);
+            sb.append(", fixedPrice=").append(fixedPrice);
+            sb.append(", recurringPrice=").append(recurringPrice);
+            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 TierJson tierJson = (TierJson) o;
+
+            if (blocks != null ? !blocks.equals(tierJson.blocks) : tierJson.blocks != null) {
+                return false;
+            }
+            if (limits != null ? !limits.equals(tierJson.limits) : tierJson.limits != null) {
+                return false;
+            }
+            if (fixedPrice != null ? !fixedPrice.equals(tierJson.fixedPrice) : tierJson.fixedPrice != null) {
+                return false;
+            }
+            if (recurringPrice != null ? !recurringPrice.equals(tierJson.recurringPrice) : tierJson.recurringPrice != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = blocks != null ? blocks.hashCode() : 0;
+            result = 31 * result + (limits != null ? limits.hashCode() : 0);
+            result = 31 * result + (fixedPrice != null ? fixedPrice.hashCode() : 0);
+            result = 31 * result + (recurringPrice != null ? recurringPrice.hashCode() : 0);
+            return result;
+        }
+    }
+
+    public static class UsageJson {
+        private final String billingPeriod;
+        private final List<TierJson> tiers;
+
+        @JsonCreator
+        public UsageJson(@JsonProperty("billingPeriod") final String billingPeriod,
+                        @JsonProperty("tiers") final List<TierJson> tiers) {
+            this.billingPeriod = billingPeriod;
+            this.tiers = tiers;
+        }
+
+        public String getBillingPeriod() {
+            return billingPeriod;
+        }
+        public List<TierJson> getTiers() {
+            return tiers;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("UsageJson{");
+            sb.append("billingPeriod='").append(billingPeriod).append('\'');
+            sb.append(", tiers=").append(tiers);
+            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 UsageJson usageJson = (UsageJson) o;
+
+            if (billingPeriod != null ? !billingPeriod.equals(usageJson.billingPeriod) : usageJson.billingPeriod != null) {
+                return false;
+            }
+            if (tiers != null ? !tiers.equals(usageJson.tiers) : usageJson.tiers != null) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = billingPeriod != null ? billingPeriod.hashCode() : 0;
+            result = 31 * result + (tiers != null ? tiers.hashCode() : 0);
+            return result;
+        }
+    }
+
     public static class PhaseJson {
 
         private final String type;
         private final List<PriceJson> prices;
         private final List<PriceJson> fixedPrices;
         private final DurationJson duration;
+        private final List<UsageJson> usages;
 
         @JsonCreator
         public PhaseJson(@JsonProperty("type") final String type,
                          @JsonProperty("prices") final List<PriceJson> prices,
                          @JsonProperty("fixedPrices") final List<PriceJson> fixedPrices,
-                         @JsonProperty("duration") final DurationJson duration) {
+                         @JsonProperty("duration") final DurationJson duration,
+                         @JsonProperty("usages") final List<UsageJson> usages) {
             this.type = type;
             this.prices = prices;
             this.fixedPrices = fixedPrices;
             this.duration = duration;
+            this.usages = usages;
         }
 
         public String getType() {
@@ -405,6 +752,9 @@ public class CatalogJson {
         public DurationJson getDuration() {
             return duration;
         }
+        public List<UsageJson> getUsages() {
+            return usages;
+        }
 
         @Override
         public String toString() {
@@ -413,6 +763,7 @@ public class CatalogJson {
             sb.append(", prices=").append(prices);
             sb.append(", fixedPrices=").append(fixedPrices);
             sb.append(", duration=").append(duration);
+            sb.append(", usages=").append(usages);
             sb.append('}');
             return sb.toString();
         }
@@ -440,6 +791,9 @@ public class CatalogJson {
             if (duration != null ? !duration.equals(phaseJson.duration) : phaseJson.duration != null) {
                 return false;
             }
+            if (usages != null ? !usages.equals(phaseJson.usages) : phaseJson.usages!= null) {
+                return false;
+            }
 
             return true;
         }
@@ -450,6 +804,7 @@ public class CatalogJson {
             result = 31 * result + (prices != null ? prices.hashCode() : 0);
             result = 31 * result + (fixedPrices != null ? fixedPrices.hashCode() : 0);
             result = 31 * result + (duration != null ? duration.hashCode() : 0);
+            result = 31 * result + (usages != null ? usages.hashCode() : 0);
             return result;
         }
     }
@@ -593,7 +948,7 @@ public class CatalogJson {
             this.number = number;
         }
 
-        public DurationJson(final Duration duration) throws CurrencyValueNull {
+        public DurationJson(final Duration duration) {
             this(duration.getUnit(), duration.getNumber());
         }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
index 5819a22..886b26d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CreditJson.java
@@ -22,13 +22,14 @@ import java.util.List;
 import javax.annotation.Nullable;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class CreditJson extends JsonBase {
 
@@ -40,19 +41,26 @@ public class CreditJson extends JsonBase {
     private final LocalDate effectiveDate;
     @ApiModelProperty(dataType = "java.util.UUID", required = true)
     private final String accountId;
+    private final String description;
+    private final String currency;
+
 
     @JsonCreator
     public CreditJson(@JsonProperty("creditAmount") final BigDecimal creditAmount,
+                      @JsonProperty("currency") final String currency,
                       @JsonProperty("invoiceId") final String invoiceId,
                       @JsonProperty("invoiceNumber") final String invoiceNumber,
                       @JsonProperty("effectiveDate") final LocalDate effectiveDate,
                       @JsonProperty("accountId") final String accountId,
+                      @JsonProperty("description") final String description,
                       @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.creditAmount = creditAmount;
+        this.currency = currency;
         this.invoiceId = invoiceId;
         this.invoiceNumber = invoiceNumber;
         this.effectiveDate = effectiveDate;
+        this.description = description;
         this.accountId = accountId;
     }
 
@@ -60,9 +68,11 @@ public class CreditJson extends JsonBase {
         super(toAuditLogJson(auditLogs));
         this.accountId = toString(credit.getAccountId());
         this.creditAmount = credit.getAmount();
+        this.currency = credit.getCurrency().name();
         this.invoiceId = toString(credit.getInvoiceId());
         this.invoiceNumber = invoice.getInvoiceNumber().toString();
         this.effectiveDate = credit.getStartDate();
+        this.description = credit.getDescription();
     }
 
     public CreditJson(final Invoice invoice, final InvoiceItem credit) {
@@ -89,14 +99,24 @@ public class CreditJson extends JsonBase {
         return accountId;
     }
 
+    public String getDescription() {
+        return description;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("CreditJson");
         sb.append("{creditAmount=").append(creditAmount);
+        sb.append(", currency=").append(currency);
         sb.append(", invoiceId=").append(invoiceId);
         sb.append(", invoiceNumber='").append(invoiceNumber).append('\'');
         sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", description=").append(description);
         sb.append(", accountId=").append(accountId);
         sb.append('}');
         return sb.toString();
@@ -117,12 +137,18 @@ public class CreditJson extends JsonBase {
               (creditAmount != null && that.creditAmount != null && creditAmount.compareTo(that.creditAmount) == 0))) {
             return false;
         }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
+            return false;
+        }
         if (invoiceId != null ? !invoiceId.equals(that.invoiceId) : that.invoiceId != null) {
             return false;
         }
         if (invoiceNumber != null ? !invoiceNumber.equals(that.invoiceNumber) : that.invoiceNumber != null) {
             return false;
         }
+        if (description != null ? !description.equals(that.description) : that.description != null) {
+            return false;
+        }
         if (!((effectiveDate == null && that.effectiveDate == null) ||
               (effectiveDate != null && that.effectiveDate != null && effectiveDate.compareTo(that.effectiveDate) == 0))) {
             return false;
@@ -134,7 +160,9 @@ public class CreditJson extends JsonBase {
     @Override
     public int hashCode() {
         int result = creditAmount != null ? creditAmount.hashCode() : 0;
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
+        result = 31 * result + (description != null ? description.hashCode() : 0);
         result = 31 * result + (invoiceNumber != null ? invoiceNumber.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         return result;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CustomFieldJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CustomFieldJson.java
index 9013d28..375b72d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CustomFieldJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CustomFieldJson.java
@@ -26,7 +26,7 @@ import org.killbill.billing.util.customfield.CustomField;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class CustomFieldJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
index bfc1b85..b8ef5ca 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/GatewayNotificationJson.java
@@ -28,7 +28,7 @@ import org.killbill.billing.payment.plugin.api.GatewayNotification;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class GatewayNotificationJson extends JsonBase {
 
@@ -37,14 +37,14 @@ public class GatewayNotificationJson extends JsonBase {
     private final Integer status;
     private final String entity;
     private final Map<String, List<String>> headers;
-    private final Map<String, String> properties;
+    private final Map<String, Object> properties;
 
     @JsonCreator
     public GatewayNotificationJson(@JsonProperty("kbPaymentId") final String kbPaymentId,
                                    @JsonProperty("status") final Integer status,
                                    @JsonProperty("entity") final String entity,
                                    @JsonProperty("headers") final Map<String, List<String>> headers,
-                                   @JsonProperty("properties") final Map<String, String> properties) {
+                                   @JsonProperty("properties") final Map<String, Object> properties) {
         this.kbPaymentId = kbPaymentId;
         this.status = status;
         this.entity = entity;
@@ -94,7 +94,7 @@ public class GatewayNotificationJson extends JsonBase {
         return headers;
     }
 
-    public Map<String, String> getProperties() {
+    public Map<String, Object> getProperties() {
         return properties;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
index 623cdca..24b292c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/HostedPaymentPageFormDescriptorJson.java
@@ -23,7 +23,7 @@ import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class HostedPaymentPageFormDescriptorJson extends JsonBase {
 
@@ -31,15 +31,15 @@ public class HostedPaymentPageFormDescriptorJson extends JsonBase {
     private final String kbAccountId;
     private final String formMethod;
     private final String formUrl;
-    private final Map<String, String> formFields;
-    private final Map<String, String> properties;
+    private final Map<String, Object> formFields;
+    private final Map<String, Object> properties;
 
     @JsonCreator
     public HostedPaymentPageFormDescriptorJson(@JsonProperty("kbAccountId") final String kbAccountId,
                                                @JsonProperty("formMethod") final String formMethod,
                                                @JsonProperty("formUrl") final String formUrl,
-                                               @JsonProperty("formFields") final Map<String, String> formFields,
-                                               @JsonProperty("properties") final Map<String, String> properties) {
+                                               @JsonProperty("formFields") final Map<String, Object> formFields,
+                                               @JsonProperty("properties") final Map<String, Object> properties) {
         this.kbAccountId = kbAccountId;
         this.formMethod = formMethod;
         this.formUrl = formUrl;
@@ -67,11 +67,11 @@ public class HostedPaymentPageFormDescriptorJson extends JsonBase {
         return formUrl;
     }
 
-    public Map<String, String> getFormFields() {
+    public Map<String, Object> getFormFields() {
         return formFields;
     }
 
-    public Map<String, String> getProperties() {
+    public Map<String, Object> getProperties() {
         return properties;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceEmailJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceEmailJson.java
index a0e5d9e..2eb503c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceEmailJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceEmailJson.java
@@ -19,7 +19,7 @@ package org.killbill.billing.jaxrs.json;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonGetter;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class InvoiceEmailJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
index a476a22..0d9c15c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceItemJson.java
@@ -31,7 +31,10 @@ import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import io.swagger.annotations.ApiModelProperty;
 
 public class InvoiceItemJson extends JsonBase {
 
@@ -43,6 +46,8 @@ public class InvoiceItemJson extends JsonBase {
     private final String linkedInvoiceItemId;
     @ApiModelProperty(dataType = "java.util.UUID", required = true)
     private final String accountId;
+    @ApiModelProperty(dataType = "java.util.UUID", required = false)
+    private final String childAccountId;
     @ApiModelProperty(dataType = "java.util.UUID")
     private final String bundleId;
     @ApiModelProperty(dataType = "java.util.UUID")
@@ -55,13 +60,15 @@ public class InvoiceItemJson extends JsonBase {
     private final LocalDate startDate;
     private final LocalDate endDate;
     private final BigDecimal amount;
-    private final Currency currency;
+    private final String currency;
+    private List<InvoiceItemJson> childItems;
 
     @JsonCreator
     public InvoiceItemJson(@JsonProperty("invoiceItemId") final String invoiceItemId,
                            @JsonProperty("invoiceId") final String invoiceId,
                            @JsonProperty("linkedInvoiceItemId") final String linkedInvoiceItemId,
                            @JsonProperty("accountId") final String accountId,
+                           @JsonProperty("childAccountId") final String childAccountId,
                            @JsonProperty("bundleId") final String bundleId,
                            @JsonProperty("subscriptionId") final String subscriptionId,
                            @JsonProperty("planName") final String planName,
@@ -72,13 +79,15 @@ public class InvoiceItemJson extends JsonBase {
                            @JsonProperty("startDate") final LocalDate startDate,
                            @JsonProperty("endDate") final LocalDate endDate,
                            @JsonProperty("amount") final BigDecimal amount,
-                           @JsonProperty("currency") final Currency currency,
+                           @JsonProperty("currency") final String currency,
+                           @JsonProperty("childItems") final List<InvoiceItemJson> childItems,
                            @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.invoiceItemId = invoiceItemId;
         this.invoiceId = invoiceId;
         this.linkedInvoiceItemId = linkedInvoiceItemId;
         this.accountId = accountId;
+        this.childAccountId = childAccountId;
         this.bundleId = bundleId;
         this.subscriptionId = subscriptionId;
         this.planName = planName;
@@ -90,14 +99,27 @@ public class InvoiceItemJson extends JsonBase {
         this.endDate = endDate;
         this.amount = amount;
         this.currency = currency;
+        this.childItems = childItems;
     }
 
-    public InvoiceItemJson(final InvoiceItem item, @Nullable final List<AuditLog> auditLogs) {
+    public InvoiceItemJson(final InvoiceItem item, final List<InvoiceItem> childItems, @Nullable final List<AuditLog> auditLogs) {
         this(toString(item.getId()), toString(item.getInvoiceId()), toString(item.getLinkedItemId()),
-             toString(item.getAccountId()), toString(item.getBundleId()), toString(item.getSubscriptionId()),
+             toString(item.getAccountId()), toString(item.getChildAccountId()), toString(item.getBundleId()), toString(item.getSubscriptionId()),
              item.getPlanName(), item.getPhaseName(), item.getUsageName(), item.getInvoiceItemType().toString(),
              item.getDescription(), item.getStartDate(), item.getEndDate(),
-             item.getAmount(), item.getCurrency(), toAuditLogJson(auditLogs));
+             item.getAmount(), item.getCurrency().name(), toInvoiceItemJson(childItems), toAuditLogJson(auditLogs));
+    }
+
+    private static List<InvoiceItemJson> toInvoiceItemJson(final List<InvoiceItem> childItems) {
+        if (childItems == null) {
+            return null;
+        }
+        return ImmutableList.copyOf(Collections2.transform(childItems, new Function<InvoiceItem, InvoiceItemJson>() {
+            @Override
+            public InvoiceItemJson apply(final InvoiceItem input) {
+                return new InvoiceItemJson(input);
+            }
+        }));
     }
 
     public InvoiceItem toInvoiceItem() {
@@ -118,6 +140,11 @@ public class InvoiceItemJson extends JsonBase {
             }
 
             @Override
+            public UUID getChildAccountId() {
+                return childAccountId != null ? UUID.fromString(childAccountId) : null;
+            }
+
+            @Override
             public LocalDate getStartDate() {
                 return startDate;
             }
@@ -134,7 +161,7 @@ public class InvoiceItemJson extends JsonBase {
 
             @Override
             public Currency getCurrency() {
-                return currency;
+                return Currency.valueOf(currency);
             }
 
             @Override
@@ -200,7 +227,7 @@ public class InvoiceItemJson extends JsonBase {
     }
 
     public InvoiceItemJson(final InvoiceItem input) {
-        this(input, null);
+        this(input, null, null);
     }
 
     public String getInvoiceItemId() {
@@ -219,6 +246,10 @@ public class InvoiceItemJson extends JsonBase {
         return accountId;
     }
 
+    public String getChildAccountId() {
+        return childAccountId;
+    }
+
     public String getBundleId() {
         return bundleId;
     }
@@ -259,10 +290,14 @@ public class InvoiceItemJson extends JsonBase {
         return amount;
     }
 
-    public Currency getCurrency() {
+    public String getCurrency() {
         return currency;
     }
 
+    public List<InvoiceItemJson> getChildItems() {
+        return childItems;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -271,6 +306,7 @@ public class InvoiceItemJson extends JsonBase {
         sb.append(", invoiceId='").append(invoiceId).append('\'');
         sb.append(", linkedInvoiceItemId='").append(linkedInvoiceItemId).append('\'');
         sb.append(", accountId='").append(accountId).append('\'');
+        sb.append(", childAccountId='").append(childAccountId).append('\'');
         sb.append(", bundleId='").append(bundleId).append('\'');
         sb.append(", subscriptionId='").append(subscriptionId).append('\'');
         sb.append(", planName='").append(planName).append('\'');
@@ -281,6 +317,7 @@ public class InvoiceItemJson extends JsonBase {
         sb.append(", endDate=").append(endDate);
         sb.append(", amount=").append(amount);
         sb.append(", currency=").append(currency);
+        sb.append(", childItems=").append(childItems);
         sb.append('}');
         return sb.toString();
     }
@@ -299,14 +336,16 @@ public class InvoiceItemJson extends JsonBase {
         if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
             return false;
         }
-        if (!((amount == null && that.amount == null) ||
-              (amount != null && that.amount != null && amount.compareTo(that.amount) == 0))) {
+        if (childAccountId != null ? !childAccountId.equals(that.childAccountId) : that.childAccountId != null) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
         if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
             return false;
         }
-        if (currency != that.currency) {
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
         if (description != null ? !description.equals(that.description) : that.description != null) {
@@ -341,6 +380,9 @@ public class InvoiceItemJson extends JsonBase {
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
             return false;
         }
+        if (childItems != null ? !childItems.equals(that.childItems) : that.childItems != null) {
+            return false;
+        }
 
         return true;
     }
@@ -351,6 +393,7 @@ public class InvoiceItemJson extends JsonBase {
         result = 31 * result + (invoiceItemId != null ? invoiceItemId.hashCode() : 0);
         result = 31 * result + (linkedInvoiceItemId != null ? linkedInvoiceItemId.hashCode() : 0);
         result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (childAccountId != null ? childAccountId.hashCode() : 0);
         result = 31 * result + (bundleId != null ? bundleId.hashCode() : 0);
         result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (planName != null ? planName.hashCode() : 0);
@@ -361,6 +404,7 @@ public class InvoiceItemJson extends JsonBase {
         result = 31 * result + (endDate != null ? endDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (childItems != null ? childItems.hashCode() : 0);
         return result;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
index 639d2c9..7b002a3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
@@ -22,16 +22,20 @@ import java.util.List;
 
 import javax.annotation.Nullable;
 
+import org.apache.shiro.util.CollectionUtils;
 import org.joda.time.LocalDate;
-
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import io.swagger.annotations.ApiModelProperty;
 
 public class InvoiceJson extends JsonBase {
 
@@ -50,10 +54,13 @@ public class InvoiceJson extends JsonBase {
     private final List<InvoiceItemJson> items;
     private final String bundleKeys;
     private final List<CreditJson> credits;
+    private final String status;
+    private final Boolean isParentInvoice;
 
     @JsonCreator
     public InvoiceJson(@JsonProperty("amount") final BigDecimal amount,
                        @JsonProperty("currency") final String currency,
+                       @JsonProperty("status") final String status,
                        @JsonProperty("creditAdj") final BigDecimal creditAdj,
                        @JsonProperty("refundAdj") final BigDecimal refundAdj,
                        @JsonProperty("invoiceId") final String invoiceId,
@@ -65,10 +72,12 @@ public class InvoiceJson extends JsonBase {
                        @JsonProperty("externalBundleKeys") final String bundleKeys,
                        @JsonProperty("credits") final List<CreditJson> credits,
                        @JsonProperty("items") final List<InvoiceItemJson> items,
+                       @JsonProperty("isParentInvoice") final Boolean isParentInvoice,
                        @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.amount = amount;
         this.currency = currency;
+        this.status = status;
         this.creditAdj = creditAdj;
         this.refundAdj = refundAdj;
         this.invoiceId = invoiceId;
@@ -80,28 +89,39 @@ public class InvoiceJson extends JsonBase {
         this.bundleKeys = bundleKeys;
         this.credits = credits;
         this.items = items;
+        this.isParentInvoice = isParentInvoice;
     }
 
     public InvoiceJson(final Invoice input) {
-        this(input, false, null);
+        this(input, false, null, null);
     }
 
     public InvoiceJson(final Invoice input, final String bundleKeys, final List<CreditJson> credits, final List<AuditLog> auditLogs) {
-        this(input.getChargedAmount(), input.getCurrency().toString(), input.getCreditedAmount(), input.getRefundedAmount(),
+        this(input.getChargedAmount(), input.getCurrency().toString(), input.getStatus().toString(), input.getCreditedAmount(), input.getRefundedAmount(),
              input.getId().toString(), input.getInvoiceDate(), input.getTargetDate(), String.valueOf(input.getInvoiceNumber()),
-             input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, toAuditLogJson(auditLogs));
+             input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, input.isParentInvoice(), toAuditLogJson(auditLogs));
     }
 
-    public InvoiceJson(final Invoice input, final boolean withItems, @Nullable final AccountAuditLogs accountAuditLogs) {
+    public InvoiceJson(final Invoice input, final boolean withItems, final List<InvoiceItem> childItems, @Nullable final AccountAuditLogs accountAuditLogs) {
         super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoice(input.getId())));
         this.items = new ArrayList<InvoiceItemJson>(input.getInvoiceItems().size());
-        if (withItems) {
+        if (withItems || !CollectionUtils.isEmpty(childItems)) {
             for (final InvoiceItem item : input.getInvoiceItems()) {
-                this.items.add(new InvoiceItemJson(item, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
+                ImmutableList<InvoiceItem> childItemsFiltered = null;
+                if (item.getInvoiceItemType().equals(InvoiceItemType.PARENT_SUMMARY) && !CollectionUtils.isEmpty(childItems)) {
+                    childItemsFiltered = ImmutableList.copyOf(Iterables.filter(childItems, new Predicate<InvoiceItem>() {
+                        @Override
+                        public boolean apply(@Nullable final InvoiceItem invoice) {
+                            return invoice.getAccountId().equals(item.getChildAccountId());
+                        }
+                    }));
+                }
+                this.items.add(new InvoiceItemJson(item, childItemsFiltered, accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForInvoiceItem(item.getId())));
             }
         }
         this.amount = input.getChargedAmount();
         this.currency = input.getCurrency().toString();
+        this.status = input.getStatus().toString();
         this.creditAdj = input.getCreditedAmount();
         this.refundAdj = input.getRefundedAmount();
         this.invoiceId = input.getId().toString();
@@ -112,6 +132,7 @@ public class InvoiceJson extends JsonBase {
         this.accountId = input.getAccountId().toString();
         this.bundleKeys = null;
         this.credits = null;
+        this.isParentInvoice = input.isParentInvoice();
     }
 
     public BigDecimal getAmount() {
@@ -166,11 +187,20 @@ public class InvoiceJson extends JsonBase {
         return credits;
     }
 
+    public String getStatus() {
+        return status;
+    }
+
+    public Boolean getIsParentInvoice() {
+        return isParentInvoice;
+    }
+
     @Override
     public String toString() {
         return "InvoiceJson{" +
                "amount=" + amount +
                ", currency='" + currency + '\'' +
+               ", status='" + status + '\'' +
                ", invoiceId='" + invoiceId + '\'' +
                ", invoiceDate=" + invoiceDate +
                ", targetDate=" + targetDate +
@@ -182,6 +212,7 @@ public class InvoiceJson extends JsonBase {
                ", items=" + items +
                ", bundleKeys='" + bundleKeys + '\'' +
                ", credits=" + credits +
+               ", isParentInvoice=" + isParentInvoice +
                '}';
     }
 
@@ -235,6 +266,12 @@ public class InvoiceJson extends JsonBase {
         if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
             return false;
         }
+        if (status != null ? !status.equals(that.status) : that.status != null) {
+            return false;
+        }
+        if (isParentInvoice != null ? !isParentInvoice.equals(that.isParentInvoice) : that.isParentInvoice != null) {
+            return false;
+        }
 
         return true;
     }
@@ -243,6 +280,7 @@ public class InvoiceJson extends JsonBase {
     public int hashCode() {
         int result = amount != null ? amount.hashCode() : 0;
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
         result = 31 * result + (invoiceDate != null ? invoiceDate.hashCode() : 0);
         result = 31 * result + (targetDate != null ? targetDate.hashCode() : 0);
@@ -254,6 +292,7 @@ public class InvoiceJson extends JsonBase {
         result = 31 * result + (items != null ? items.hashCode() : 0);
         result = 31 * result + (bundleKeys != null ? bundleKeys.hashCode() : 0);
         result = 31 * result + (credits != null ? credits.hashCode() : 0);
+        result = 31 * result + (isParentInvoice != null ? isParentInvoice.hashCode() : 0);
         return result;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentJson.java
index f37d2e4..7379469 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentJson.java
@@ -25,11 +25,12 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentAttempt;
 import org.killbill.billing.util.audit.AccountAuditLogs;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class InvoicePaymentJson extends PaymentJson {
 
@@ -51,8 +52,9 @@ public class InvoicePaymentJson extends PaymentJson {
                               @JsonProperty("currency") final String currency,
                               @JsonProperty("paymentMethodId") final String paymentMethodId,
                               @JsonProperty("transactions") final List<? extends PaymentTransactionJson> transactions,
+                              @JsonProperty("paymentAttempts") final List<PaymentAttemptJson> paymentAttempts,
                               @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
-        super(accountId, paymentId, paymentNumber, paymentExternalKey, authAmount, capturedAmount, purchasedAmount, refundedAmount, creditedAmount, currency, paymentMethodId, transactions, auditLogs);
+        super(accountId, paymentId, paymentNumber, paymentExternalKey, authAmount, capturedAmount, purchasedAmount, refundedAmount, creditedAmount, currency, paymentMethodId, transactions, paymentAttempts, auditLogs);
         this.targetInvoiceId = targetInvoiceId;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentTransactionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentTransactionJson.java
index 487825b..8c03d0e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentTransactionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoicePaymentTransactionJson.java
@@ -41,6 +41,8 @@ public class InvoicePaymentTransactionJson extends PaymentTransactionJson {
                                          @JsonProperty("amount") final BigDecimal amount,
                                          @JsonProperty("currency") final String currency,
                                          @JsonProperty("effectiveDate") final DateTime effectiveDate,
+                                         @JsonProperty("processedAmount") final BigDecimal processedAmount,
+                                         @JsonProperty("processedCurrency") final String processedCurrency,
                                          @JsonProperty("status") final String status,
                                          @JsonProperty("gatewayErrorCode") final String gatewayErrorCode,
                                          @JsonProperty("gatewayErrorMsg") final String gatewayErrorMsg,
@@ -50,8 +52,8 @@ public class InvoicePaymentTransactionJson extends PaymentTransactionJson {
                                          @JsonProperty("isAdjusted") final Boolean isAdjusted,
                                          @JsonProperty("adjustments") final List<InvoiceItemJson> adjustments,
                                          @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
-        super(transactionId, transactionExternalKey, paymentId, paymentExternalKey, transactionType, amount, currency, effectiveDate, status,
-              gatewayErrorCode, gatewayErrorMsg, firstPaymentReferenceId, secondPaymentReferenceId, properties, auditLogs);
+        super(transactionId, transactionExternalKey, paymentId, paymentExternalKey, transactionType, amount, currency, effectiveDate, processedAmount, processedCurrency,
+              status, gatewayErrorCode, gatewayErrorMsg, firstPaymentReferenceId, secondPaymentReferenceId, properties, auditLogs);
         this.isAdjusted = isAdjusted;
         this.adjustments = adjustments;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
index a92f6e9..936cb71 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/JsonBase.java
@@ -74,11 +74,11 @@ public abstract class JsonBase {
         return properties;
     }
 
-    protected Map<String, String> propertiesToMap(final Iterable<PluginProperty> properties) {
-        final Map<String, String> propertiesMap = new HashMap<String, String>();
+    protected Map<String, Object> propertiesToMap(final Iterable<PluginProperty> properties) {
+        final Map<String, Object> propertiesMap = new HashMap<String, Object>();
         for (final PluginProperty pluginProperty : properties) {
             if (pluginProperty.getValue() != null) {
-                propertiesMap.put(pluginProperty.getKey(), pluginProperty.getValue().toString());
+                propertiesMap.put(pluginProperty.getKey(), pluginProperty.getValue());
             }
         }
         return propertiesMap;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
index 469e2c4..22fe656 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/NotificationJson.java
@@ -20,7 +20,7 @@ import org.killbill.billing.notification.plugin.api.ExtBusEvent;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 /*
  * Use to communicate back with client after they registered a callback
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java
new file mode 100644
index 0000000..048a3c5
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+
+import org.killbill.billing.jaxrs.json.CatalogJson.DurationJson;
+import org.killbill.billing.overdue.api.OverdueCondition;
+import org.killbill.billing.overdue.config.DefaultDuration;
+import org.killbill.billing.overdue.config.DefaultOverdueCondition;
+import org.killbill.billing.payment.api.PaymentResponse;
+import org.killbill.billing.util.tag.ControlTagType;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OverdueConditionJson {
+
+    private final DurationJson timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+    private final ControlTagType controlTagInclusion;
+    private final ControlTagType controlTagExclusion;
+    private final Integer numberOfUnpaidInvoicesEqualsOrExceeds;
+    private final PaymentResponse[] responseForLastFailedPayment;
+    private final BigDecimal totalUnpaidInvoiceBalanceEqualsOrExceeds;
+
+
+    @JsonCreator
+    public OverdueConditionJson(@JsonProperty("timeSinceEarliestUnpaidInvoiceEqualsOrExceeds") final DurationJson timeSinceEarliestUnpaidInvoiceEqualsOrExceeds,
+                                @JsonProperty("controlTagInclusion") final ControlTagType controlTagInclusion,
+                                @JsonProperty("controlTagExclusion") final ControlTagType controlTagExclusion,
+                                @JsonProperty("numberOfUnpaidInvoicesEqualsOrExceeds") final Integer numberOfUnpaidInvoicesEqualsOrExceeds,
+                                @JsonProperty("responseForLastFailedPayment") final PaymentResponse[] responseForLastFailedPayment,
+                                @JsonProperty("totalUnpaidInvoiceBalanceEqualsOrExceeds") final BigDecimal totalUnpaidInvoiceBalanceEqualsOrExceeds) {
+        this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+        this.controlTagInclusion = controlTagInclusion;
+        this.controlTagExclusion = controlTagExclusion;
+        this.numberOfUnpaidInvoicesEqualsOrExceeds = numberOfUnpaidInvoicesEqualsOrExceeds;
+        this.responseForLastFailedPayment = responseForLastFailedPayment;
+        this.totalUnpaidInvoiceBalanceEqualsOrExceeds = totalUnpaidInvoiceBalanceEqualsOrExceeds;
+    }
+
+    public OverdueConditionJson(final OverdueCondition overdueCondition) {
+        this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = new DurationJson(overdueCondition.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds());
+        this.controlTagInclusion = overdueCondition.getInclusionControlTagType();
+        this.controlTagExclusion = overdueCondition.getExclusionControlTagType();
+        this.numberOfUnpaidInvoicesEqualsOrExceeds = overdueCondition.getNumberOfUnpaidInvoicesEqualsOrExceeds();
+        this.responseForLastFailedPayment = overdueCondition.getResponseForLastFailedPaymentIn();
+        this.totalUnpaidInvoiceBalanceEqualsOrExceeds = overdueCondition.getTotalUnpaidInvoiceBalanceEqualsOrExceeds();
+    }
+
+    public DurationJson getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds() {
+        return timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+    }
+
+    public ControlTagType getControlTagInclusion() {
+        return controlTagInclusion;
+    }
+
+    public ControlTagType getControlTagExclusion() {
+        return controlTagExclusion;
+    }
+
+    public Integer getNumberOfUnpaidInvoicesEqualsOrExceeds() {
+        return numberOfUnpaidInvoicesEqualsOrExceeds;
+    }
+
+    public PaymentResponse[] getResponseForLastFailedPayment() {
+        return responseForLastFailedPayment;
+    }
+
+    public BigDecimal getTotalUnpaidInvoiceBalanceEqualsOrExceeds() {
+        return totalUnpaidInvoiceBalanceEqualsOrExceeds;
+    }
+
+    @Override
+    public String toString() {
+        return "OverdueConditionJson{" +
+               "timeSinceEarliestUnpaidInvoiceEqualsOrExceeds=" + timeSinceEarliestUnpaidInvoiceEqualsOrExceeds +
+               ", controlTagInclusion=" + controlTagInclusion +
+               ", controlTagExclusion=" + controlTagExclusion +
+               ", numberOfUnpaidInvoicesEqualsOrExceeds=" + numberOfUnpaidInvoicesEqualsOrExceeds +
+               ", responseForLastFailedPayment=" + responseForLastFailedPayment +
+               ", totalUnpaidInvoiceBalanceEqualsOrExceeds=" + totalUnpaidInvoiceBalanceEqualsOrExceeds +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof OverdueConditionJson)) {
+            return false;
+        }
+
+        final OverdueConditionJson that = (OverdueConditionJson) o;
+
+        if (timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null ? !timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.equals(that.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds) : that.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null) {
+            return false;
+        }
+        if (controlTagInclusion != that.controlTagInclusion) {
+            return false;
+        }
+        if (controlTagExclusion != that.controlTagExclusion) {
+            return false;
+        }
+        if (numberOfUnpaidInvoicesEqualsOrExceeds != that.numberOfUnpaidInvoicesEqualsOrExceeds) {
+            return false;
+        }
+        if (responseForLastFailedPayment != that.responseForLastFailedPayment) {
+            return false;
+        }
+        return totalUnpaidInvoiceBalanceEqualsOrExceeds == that.totalUnpaidInvoiceBalanceEqualsOrExceeds;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null ? timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.hashCode() : 0;
+        result = 31 * result + (controlTagInclusion != null ? controlTagInclusion.hashCode() : 0);
+        result = 31 * result + (controlTagExclusion != null ? controlTagExclusion.hashCode() : 0);
+        result = 31 * result + (numberOfUnpaidInvoicesEqualsOrExceeds != null ? numberOfUnpaidInvoicesEqualsOrExceeds.hashCode() : 0);
+        result = 31 * result + (responseForLastFailedPayment != null ? responseForLastFailedPayment.hashCode() : 0);
+        result = 31 * result + (totalUnpaidInvoiceBalanceEqualsOrExceeds != null ? totalUnpaidInvoiceBalanceEqualsOrExceeds.hashCode() : 0);
+        return result;
+    }
+
+    public static DefaultOverdueCondition toOverdueCondition(final OverdueConditionJson input) {
+        final DefaultOverdueCondition result = new DefaultOverdueCondition();
+        if (input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds() != null) {
+            result.setTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds(new DefaultDuration().setUnit(input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getUnit()).setNumber(input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getNumber()));
+        }
+        result.setControlTagInclusion(input.getControlTagInclusion());
+        result.setControlTagExclusion(input.getControlTagExclusion());
+        result.setNumberOfUnpaidInvoicesEqualsOrExceeds(input.getNumberOfUnpaidInvoicesEqualsOrExceeds());
+        result.setResponseForLastFailedPayment(input.getResponseForLastFailedPayment());
+        result.setTotalUnpaidInvoiceBalanceEqualsOrExceeds(input.getTotalUnpaidInvoiceBalanceEqualsOrExceeds());
+
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java
new file mode 100644
index 0000000..c2284be
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import java.util.List;
+
+import org.killbill.billing.catalog.api.CurrencyValueNull;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.overdue.api.OverdueApiException;
+import org.killbill.billing.overdue.api.OverdueConfig;
+import org.killbill.billing.overdue.api.OverdueState;
+import org.killbill.billing.overdue.config.DefaultDuration;
+import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.config.DefaultOverdueState;
+import org.killbill.billing.overdue.config.DefaultOverdueStatesAccount;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class OverdueJson {
+
+    private final Integer initialReevaluationIntervalDays;
+    private final List<OverdueStateConfigJson> overdueStates;
+
+    @JsonCreator
+    public OverdueJson(@JsonProperty("initialReevaluationInterval") final Integer initialReevaluationInterval,
+                       @JsonProperty("overdueStates") final List<OverdueStateConfigJson> overdueStates) {
+        this.initialReevaluationIntervalDays = initialReevaluationInterval;
+        this.overdueStates = overdueStates;
+    }
+
+    public OverdueJson(final OverdueConfig overdueConfig) {
+        this.initialReevaluationIntervalDays = overdueConfig.getOverdueStatesAccount().getInitialReevaluationInterval() != null ?
+                                               overdueConfig.getOverdueStatesAccount().getInitialReevaluationInterval().getDays() : null;
+        this.overdueStates = ImmutableList.copyOf(Iterables.transform(ImmutableList.copyOf(overdueConfig.getOverdueStatesAccount().getStates()), new Function<OverdueState, OverdueStateConfigJson>() {
+            @Override
+            public OverdueStateConfigJson apply(final OverdueState input) {
+                    return new OverdueStateConfigJson(input);
+            }
+        }));
+    }
+
+    public Integer getInitialReevaluationInterval() {
+        return initialReevaluationIntervalDays;
+    }
+
+    public List<OverdueStateConfigJson> getOverdueStates() {
+        return overdueStates;
+    }
+
+    @Override
+    public String toString() {
+        return "OverdueJson{" +
+               "initialReevaluationIntervalDays=" + initialReevaluationIntervalDays +
+               ", overdueStates=" + overdueStates +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof OverdueJson)) {
+            return false;
+        }
+
+        final OverdueJson that = (OverdueJson) o;
+
+        if (initialReevaluationIntervalDays != null ? !initialReevaluationIntervalDays.equals(that.initialReevaluationIntervalDays) : that.initialReevaluationIntervalDays != null) {
+            return false;
+        }
+        return overdueStates != null ? overdueStates.equals(that.overdueStates) : that.overdueStates == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = initialReevaluationIntervalDays != null ? initialReevaluationIntervalDays.hashCode() : 0;
+        result = 31 * result + (overdueStates != null ? overdueStates.hashCode() : 0);
+        return result;
+    }
+
+    public static OverdueConfig toOverdueConfigWithValidation(final OverdueJson input) {
+        final DefaultOverdueConfig result = new DefaultOverdueConfig();
+        final DefaultOverdueStatesAccount overdueStateAccount = new DefaultOverdueStatesAccount();
+        result.setOverdueStates(overdueStateAccount);
+
+        final DefaultOverdueState [] states = new DefaultOverdueState[input.getOverdueStates().size()];
+        int i = 0;
+
+
+        int prevTimeSinceEarliestUnpaidInvoice = -1;
+        for (final OverdueStateConfigJson cur : input.getOverdueStates()) {
+
+            Preconditions.checkNotNull(cur.getName());
+
+            // We only support timeSinceEarliestUnpaidInvoiceEqualsOrExceeds condition (see #611)
+            Preconditions.checkNotNull(cur.getCondition());
+            Preconditions.checkNotNull(cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds());
+            Preconditions.checkNotNull(cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getUnit());
+            Preconditions.checkState(cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getUnit() == TimeUnit.DAYS);
+            Preconditions.checkState(cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getNumber() > 0);
+
+            final DefaultOverdueState state = new DefaultOverdueState();
+            state.setName(cur.getName());
+            state.setExternalMessage(cur.getExternalMessage());
+            state.setBlockChanges(cur.getBlockChanges());
+            state.setDisableEntitlement(cur.getDisableEntitlement());
+            state.setSubscriptionCancellationPolicy(cur.getSubscriptionCancellationPolicy());
+            state.setClearState(cur.isClearState());
+            state.setAutoReevaluationInterval(computeReevaluationInterval(cur.getAutoReevaluationIntervalDays(), prevTimeSinceEarliestUnpaidInvoice, cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getNumber()));
+            state.setCondition(OverdueConditionJson.toOverdueCondition(cur.getCondition()));
+            states[i++] = state;
+
+            prevTimeSinceEarliestUnpaidInvoice = cur.getCondition().getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getNumber();
+        }
+        overdueStateAccount.setAccountOverdueStates(states);
+        overdueStateAccount.setInitialReevaluationInterval(computeReevaluationInterval(null, prevTimeSinceEarliestUnpaidInvoice, 0));
+        return result;
+    }
+
+    // Unless the user knows what it's doing (inputReevaluationInterval != null), for time based condition we set the reevaluation interval to match the transition to the next state
+    private static DefaultDuration computeReevaluationInterval(final Integer inputReevaluationInterval,  int prevTimeSinceEarliestUnpaidInvoice, int curTimeSinceEarliestUnpaidInvoice) {
+        if (inputReevaluationInterval != null && inputReevaluationInterval > 0) {
+            return new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(inputReevaluationInterval);
+        }
+
+        if (prevTimeSinceEarliestUnpaidInvoice == -1) {
+            return null;
+        }
+
+        Preconditions.checkState(prevTimeSinceEarliestUnpaidInvoice - curTimeSinceEarliestUnpaidInvoice > 0);
+
+        return new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(prevTimeSinceEarliestUnpaidInvoice - curTimeSinceEarliestUnpaidInvoice);
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java
new file mode 100644
index 0000000..ab06260
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import org.killbill.billing.overdue.api.OverdueApiException;
+import org.killbill.billing.overdue.api.OverdueCancellationPolicy;
+import org.killbill.billing.overdue.api.OverdueState;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OverdueStateConfigJson {
+
+    private final String name;
+    private final Boolean isClearState;
+    private final OverdueConditionJson condition;
+    private final String externalMessage;
+    private final Boolean blockChanges;
+    private final Boolean disableEntitlement;
+    private final OverdueCancellationPolicy subscriptionCancellationPolicy;
+    private final Integer autoReevaluationIntervalDays;
+
+    @JsonCreator
+    public OverdueStateConfigJson(@JsonProperty("name") final String name,
+                                  @JsonProperty("isClearState") final Boolean isClearState,
+                                  @JsonProperty("condition") final OverdueConditionJson condition,
+                                  @JsonProperty("externalMessage") final String externalMessage,
+                                  @JsonProperty("blockChanges") final Boolean blockChanges,
+                                  @JsonProperty("disableEntitlement") final Boolean disableEntitlement,
+                                  @JsonProperty("subscriptionCancellationPolicy") final OverdueCancellationPolicy subscriptionCancellationPolicy,
+                                  @JsonProperty("autoReevaluationIntervalDays") final Integer autoReevaluationInterval) {
+        this.name = name;
+        this.isClearState = isClearState;
+        this.condition = condition;
+        this.externalMessage = externalMessage;
+        this.blockChanges = blockChanges;
+        this.disableEntitlement = disableEntitlement;
+        this.subscriptionCancellationPolicy = subscriptionCancellationPolicy;
+        this.autoReevaluationIntervalDays = autoReevaluationInterval;
+    }
+
+    public OverdueStateConfigJson(final OverdueState input) {
+        this.name = input.getName();
+        this.isClearState = input.isClearState();
+        this.condition = input.getOverdueCondition() != null ? new OverdueConditionJson(input.getOverdueCondition()) : null;
+        this.externalMessage = input.getExternalMessage();
+        this.blockChanges = input.isBlockChanges();
+        this.disableEntitlement = input.isDisableEntitlementAndChangesBlocked();
+        this.subscriptionCancellationPolicy = input.getOverdueCancellationPolicy();
+        Integer tmpAutoReevaluationIntervalDays = null;
+        try {
+            tmpAutoReevaluationIntervalDays = input.getAutoReevaluationInterval().getDays();
+        } catch (final OverdueApiException e) {
+        } finally {
+            this.autoReevaluationIntervalDays = tmpAutoReevaluationIntervalDays;
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @JsonProperty("isClearState")
+    public Boolean isClearState() {
+        return isClearState;
+    }
+
+    public OverdueConditionJson getCondition() {
+        return condition;
+    }
+
+    public String getExternalMessage() {
+        return externalMessage;
+    }
+
+    public Boolean getBlockChanges() {
+        return blockChanges;
+    }
+
+    public Boolean getDisableEntitlement() {
+        return disableEntitlement;
+    }
+
+    public OverdueCancellationPolicy getSubscriptionCancellationPolicy() {
+        return subscriptionCancellationPolicy;
+    }
+
+    public Integer getAutoReevaluationIntervalDays() {
+        return autoReevaluationIntervalDays;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof OverdueStateConfigJson)) {
+            return false;
+        }
+
+        final OverdueStateConfigJson that = (OverdueStateConfigJson) o;
+
+        if (name != null ? !name.equals(that.name) : that.name != null) {
+            return false;
+        }
+        if (isClearState != null ? !isClearState.equals(that.isClearState) : that.isClearState != null) {
+            return false;
+        }
+        if (condition != null ? !condition.equals(that.condition) : that.condition != null) {
+            return false;
+        }
+        if (externalMessage != null ? !externalMessage.equals(that.externalMessage) : that.externalMessage != null) {
+            return false;
+        }
+        if (blockChanges != null ? !blockChanges.equals(that.blockChanges) : that.blockChanges != null) {
+            return false;
+        }
+        if (disableEntitlement != null ? !disableEntitlement.equals(that.disableEntitlement) : that.disableEntitlement != null) {
+            return false;
+        }
+        if (subscriptionCancellationPolicy != that.subscriptionCancellationPolicy) {
+            return false;
+        }
+        return autoReevaluationIntervalDays != null ? autoReevaluationIntervalDays.equals(that.autoReevaluationIntervalDays) : that.autoReevaluationIntervalDays == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (isClearState != null ? isClearState.hashCode() : 0);
+        result = 31 * result + (condition != null ? condition.hashCode() : 0);
+        result = 31 * result + (externalMessage != null ? externalMessage.hashCode() : 0);
+        result = 31 * result + (blockChanges != null ? blockChanges.hashCode() : 0);
+        result = 31 * result + (disableEntitlement != null ? disableEntitlement.hashCode() : 0);
+        result = 31 * result + (subscriptionCancellationPolicy != null ? subscriptionCancellationPolicy.hashCode() : 0);
+        result = 31 * result + (autoReevaluationIntervalDays != null ? autoReevaluationIntervalDays.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "OverdueStateConfigJson{" +
+               "name='" + name + '\'' +
+               ", isClearState=" + isClearState +
+               ", condition=" + condition +
+               ", externalMessage='" + externalMessage + '\'' +
+               ", blockChanges=" + blockChanges +
+               ", disableEntitlement=" + disableEntitlement +
+               ", subscriptionCancellationPolicy=" + subscriptionCancellationPolicy +
+               ", autoReevaluationIntervalDays=" + autoReevaluationIntervalDays +
+               '}';
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateJson.java
index ed0fd75..a3fe920 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateJson.java
@@ -21,7 +21,7 @@ import java.util.List;
 import org.joda.time.Period;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueState;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -56,7 +56,8 @@ public class OverdueStateJson {
     public OverdueStateJson(final OverdueState overdueState, final PaymentConfig paymentConfig) {
         this.name = overdueState.getName();
         this.externalMessage = overdueState.getExternalMessage();
-        this.daysBetweenPaymentRetries = paymentConfig.getPaymentFailureRetryDays();
+        // TODO this is broken if the per tenant system property was updated, but should we really return that in the OverdueState ?
+        this.daysBetweenPaymentRetries = paymentConfig.getPaymentFailureRetryDays(null);
         this.disableEntitlementAndChangesBlocked = overdueState.isDisableEntitlementAndChangesBlocked();
         this.blockChanges = overdueState.isBlockChanges();
         this.isClearState = overdueState.isClearState();
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java
new file mode 100644
index 0000000..f8150da
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentAttemptJson.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.payment.api.PaymentAttempt;
+import org.killbill.billing.util.audit.AuditLog;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+@ApiModel(description = "Payment attempt")
+public class PaymentAttemptJson extends JsonBase {
+
+    @ApiModelProperty(dataType = "java.util.UUID")
+    private final String accountId;
+    @ApiModelProperty(dataType = "java.util.UUID")
+    private final String paymentMethodId;
+    private final String paymentExternalKey;
+    @ApiModelProperty(dataType = "java.util.UUID")
+    private final String transactionId;
+    private final String transactionExternalKey;
+    @ApiModelProperty(dataType = "org.killbill.billing.payment.api.TransactionType")
+    private final String transactionType;
+    @ApiModelProperty(dataType = "org.joda.time.DateTime")
+    private final DateTime effectiveDate;
+    private final String stateName;
+    @ApiModelProperty(value = "Transaction amount, required except for void operations")
+    private final BigDecimal amount;
+    @ApiModelProperty(value = "Amount currency (account currency unless specified)", dataType = "org.killbill.billing.catalog.api.Currency")
+    private final String currency;
+    // Plugin specific fields
+    private final String pluginName;
+    private final List<PluginPropertyJson> pluginProperties;
+
+    @JsonCreator
+    public PaymentAttemptJson(@JsonProperty("accountId") final String accountId,
+                              @JsonProperty("paymentMethodId") final String paymentMethodId,
+                              @JsonProperty("paymentExternalKey") final String paymentExternalKey,
+                              @JsonProperty("transactionId") final String transactionId,
+                              @JsonProperty("transactionExternalKey") final String transactionExternalKey,
+                              @JsonProperty("transactionType") final String transactionType,
+                              @JsonProperty("effectiveDate") final DateTime effectiveDate,
+                              @JsonProperty("stateName") final String stateName,
+                              @JsonProperty("amount") final BigDecimal amount,
+                              @JsonProperty("currency") final String currency,
+                              @JsonProperty("pluginName") final String pluginName,
+                              @JsonProperty("pluginProperties") final List<PluginPropertyJson> pluginProperties,
+                              @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
+        super(auditLogs);
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        this.paymentExternalKey = paymentExternalKey;
+        this.transactionId = transactionId;
+        this.transactionExternalKey = transactionExternalKey;
+        this.transactionType = transactionType;
+        this.effectiveDate = effectiveDate;
+        this.stateName = stateName;
+        this.amount = amount;
+        this.currency = currency;
+        this.pluginName = pluginName;
+        this.pluginProperties = pluginProperties;
+    }
+
+    public PaymentAttemptJson(final PaymentAttempt paymentAttempt, final String paymentExternalKey, @Nullable final List<AuditLog> attemptsLogs) {
+        this(paymentAttempt.getAccountId().toString(),
+             paymentAttempt.getPaymentMethodId().toString(),
+             paymentExternalKey,
+             paymentAttempt.getTransactionId() != null ? paymentAttempt.getTransactionId().toString() : null,
+             paymentAttempt.getTransactionExternalKey(),
+             paymentAttempt.getTransactionType().toString(),
+             paymentAttempt.getEffectiveDate(),
+             paymentAttempt.getStateName() != null ? paymentAttempt.getStateName() : null,
+             paymentAttempt.getAmount(),
+             paymentAttempt.getCurrency() != null ? paymentAttempt.getCurrency().toString() : null,
+             paymentAttempt.getPluginName(),
+             paymentAttempt.getPluginProperties() == null ? null : toPluginPropertyJson(paymentAttempt.getPluginProperties()),
+             toAuditLogJson(attemptsLogs));
+    }
+
+    public String getAccountId() {
+        return accountId;
+    }
+
+    public String getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    public String getPaymentExternalKey() {
+        return paymentExternalKey;
+    }
+
+    public String getTransactionId() {
+        return transactionId;
+    }
+
+    public String getTransactionExternalKey() {
+        return transactionExternalKey;
+    }
+
+    public String getTransactionType() {
+        return transactionType;
+    }
+
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public String getStateName() {
+        return stateName;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public String getCurrency() {
+        return currency;
+    }
+
+    public String getPluginName() {
+        return pluginName;
+    }
+
+    public List<PluginPropertyJson> getPluginProperties() {
+        return pluginProperties;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("PaymentAttemptJson{");
+        sb.append("accountId='").append(accountId).append('\'');
+        sb.append(", paymentMethodId='").append(paymentMethodId).append('\'');
+        sb.append(", paymentExternalKey='").append(paymentExternalKey).append('\'');
+        sb.append(", transactionId='").append(transactionId).append('\'');
+        sb.append(", transactionExternalKey='").append(transactionExternalKey).append('\'');
+        sb.append(", transactionType='").append(transactionType).append('\'');
+        sb.append(", effectiveDate=").append(effectiveDate);
+        sb.append(", stateName='").append(stateName).append('\'');
+        sb.append(", amount=").append(amount);
+        sb.append(", currency='").append(currency).append('\'');
+        sb.append(", pluginName='").append(pluginName).append('\'');
+        sb.append(", pluginProperties=").append(pluginProperties);
+        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 PaymentAttemptJson that = (PaymentAttemptJson) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (paymentExternalKey != null ? !paymentExternalKey.equals(that.paymentExternalKey) : that.paymentExternalKey != null) {
+            return false;
+        }
+        if (transactionId != null ? !transactionId.equals(that.transactionId) : that.transactionId != null) {
+            return false;
+        }
+        if (transactionExternalKey != null ? !transactionExternalKey.equals(that.transactionExternalKey) : that.transactionExternalKey != null) {
+            return false;
+        }
+        if (transactionType != null ? !transactionType.equals(that.transactionType) : that.transactionType != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
+        if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
+            return false;
+        }
+        if (pluginName != null ? !pluginName.equals(that.pluginName) : that.pluginName != null) {
+            return false;
+        }
+        if (pluginProperties != null ? !pluginProperties.equals(that.pluginProperties) : that.pluginProperties != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = accountId != null ? accountId.hashCode() : 0;
+        result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (paymentExternalKey != null ? paymentExternalKey.hashCode() : 0);
+        result = 31 * result + (transactionId != null ? transactionId.hashCode() : 0);
+        result = 31 * result + (transactionExternalKey != null ? transactionExternalKey.hashCode() : 0);
+        result = 31 * result + (transactionType != null ? transactionType.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (pluginName != null ? pluginName.hashCode() : 0);
+        result = 31 * result + (pluginProperties != null ? pluginProperties.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentJson.java
index 3a85882..4a605fa 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentJson.java
@@ -22,6 +22,7 @@ import java.util.List;
 import javax.annotation.Nullable;
 
 import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentAttempt;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.audit.AuditLog;
@@ -31,7 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class PaymentJson extends JsonBase {
 
@@ -50,6 +51,7 @@ public class PaymentJson extends JsonBase {
     @ApiModelProperty(dataType = "java.util.UUID")
     private final String paymentMethodId;
     private final List<? extends PaymentTransactionJson> transactions;
+    private final List<PaymentAttemptJson> paymentAttempts;
 
     @JsonCreator
     public PaymentJson(@JsonProperty("accountId") final String accountId,
@@ -64,6 +66,7 @@ public class PaymentJson extends JsonBase {
                        @JsonProperty("currency") final String currency,
                        @JsonProperty("paymentMethodId") final String paymentMethodId,
                        @JsonProperty("transactions") final List<? extends PaymentTransactionJson> transactions,
+                       @JsonProperty("paymentAttempts") final List<PaymentAttemptJson> paymentAttempts,
                        @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
         super(auditLogs);
         this.accountId = accountId;
@@ -78,6 +81,7 @@ public class PaymentJson extends JsonBase {
         this.currency = currency;
         this.paymentMethodId = paymentMethodId;
         this.transactions = transactions;
+        this.paymentAttempts = paymentAttempts;
     }
 
     public PaymentJson(final Payment dp, @Nullable final AccountAuditLogs accountAuditLogs) {
@@ -93,6 +97,7 @@ public class PaymentJson extends JsonBase {
              dp.getCurrency() != null ? dp.getCurrency().toString() : null,
              dp.getPaymentMethodId() != null ? dp.getPaymentMethodId().toString() : null,
              getTransactions(dp.getTransactions(), dp.getExternalKey(), accountAuditLogs),
+             getAttempts(dp.getPaymentAttempts(), dp.getExternalKey(), accountAuditLogs),
              toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForPayment(dp.getId())));
     }
 
@@ -108,6 +113,18 @@ public class PaymentJson extends JsonBase {
                                                        ));
     }
 
+    private static List<PaymentAttemptJson> getAttempts(final Iterable<PaymentAttempt> attempts, final String paymentExternalKey, @Nullable final AccountAuditLogs accountAuditLogs) {
+        return (attempts != null) ? ImmutableList.copyOf(Iterables.transform(attempts,
+                                                        new Function<PaymentAttempt, PaymentAttemptJson>() {
+                                                            @Override
+                                                            public PaymentAttemptJson apply(final PaymentAttempt paymentAttempt) {
+                                                                final List<AuditLog> auditLogsForPaymentAttempt = accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForPaymentAttempt(paymentAttempt.getId());
+                                                                return new PaymentAttemptJson(paymentAttempt, paymentExternalKey, auditLogsForPaymentAttempt);
+                                                            }
+                                                        }
+                                                       )) : null;
+    }
+
     public String getAccountId() {
         return accountId;
     }
@@ -156,6 +173,8 @@ public class PaymentJson extends JsonBase {
         return transactions;
     }
 
+    public List<? extends PaymentAttemptJson> getPaymentAttempts() { return paymentAttempts; }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("PaymentJson{");
@@ -171,6 +190,7 @@ public class PaymentJson extends JsonBase {
         sb.append(", currency='").append(currency).append('\'');
         sb.append(", paymentMethodId='").append(paymentMethodId).append('\'');
         sb.append(", transactions=").append(transactions);
+        sb.append(", paymentAttempts=").append(paymentAttempts);
         sb.append('}');
         return sb.toString();
     }
@@ -222,6 +242,9 @@ public class PaymentJson extends JsonBase {
         if (transactions != null ? !transactions.equals(that.transactions) : that.transactions != null) {
             return false;
         }
+        if (paymentAttempts != null ? !paymentAttempts.equals(that.paymentAttempts) : that.paymentAttempts!= null) {
+            return false;
+        }
 
         return true;
     }
@@ -240,6 +263,7 @@ public class PaymentJson extends JsonBase {
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
         result = 31 * result + (transactions != null ? transactions.hashCode() : 0);
+        result = 31 * result + (paymentAttempts != null ? paymentAttempts.hashCode() : 0);
         return result;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
index 19bf61e..77a5acd 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentMethodJson.java
@@ -36,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class PaymentMethodJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentTransactionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentTransactionJson.java
index 82a40dd..be81a3a 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentTransactionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PaymentTransactionJson.java
@@ -27,8 +27,8 @@ import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModel;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
 
 @ApiModel(description = "Payment transaction")
 public class PaymentTransactionJson extends JsonBase {
@@ -49,6 +49,8 @@ public class PaymentTransactionJson extends JsonBase {
     private final BigDecimal amount;
     @ApiModelProperty(value = "Amount currency (account currency unless specified)", dataType = "org.killbill.billing.catalog.api.Currency")
     private final String currency;
+    private final BigDecimal processedAmount;
+    private final String processedCurrency;
     private final String gatewayErrorCode;
     private final String gatewayErrorMsg;
     // Plugin specific fields
@@ -65,6 +67,8 @@ public class PaymentTransactionJson extends JsonBase {
                                   @JsonProperty("amount") final BigDecimal amount,
                                   @JsonProperty("currency") final String currency,
                                   @JsonProperty("effectiveDate") final DateTime effectiveDate,
+                                  @JsonProperty("processedAmount") final BigDecimal processedAmount,
+                                  @JsonProperty("processedCurrency") final String processedCurrency,
                                   @JsonProperty("status") final String status,
                                   @JsonProperty("gatewayErrorCode") final String gatewayErrorCode,
                                   @JsonProperty("gatewayErrorMsg") final String gatewayErrorMsg,
@@ -82,6 +86,8 @@ public class PaymentTransactionJson extends JsonBase {
         this.status = status;
         this.amount = amount;
         this.currency = currency;
+        this.processedAmount = processedAmount;
+        this.processedCurrency = processedCurrency;
         this.gatewayErrorCode = gatewayErrorCode;
         this.gatewayErrorMsg = gatewayErrorMsg;
         this.firstPaymentReferenceId = firstPaymentReferenceId;
@@ -98,6 +104,8 @@ public class PaymentTransactionJson extends JsonBase {
              transaction.getAmount(),
              transaction.getCurrency() != null ? transaction.getCurrency().toString() : null,
              transaction.getEffectiveDate(),
+             transaction.getProcessedAmount(),
+             transaction.getProcessedCurrency() != null ? transaction.getProcessedCurrency().toString() : null,
              transaction.getTransactionStatus() != null ? transaction.getTransactionStatus().toString() : null,
              transaction.getGatewayErrorCode(),
              transaction.getGatewayErrorMsg(),
@@ -163,6 +171,14 @@ public class PaymentTransactionJson extends JsonBase {
         return paymentExternalKey;
     }
 
+    public BigDecimal getProcessedAmount() {
+        return processedAmount;
+    }
+
+    public String getProcessedCurrency() {
+        return processedCurrency;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("PaymentTransactionJson{");
@@ -174,6 +190,8 @@ public class PaymentTransactionJson extends JsonBase {
         sb.append(", status='").append(status).append('\'');
         sb.append(", amount=").append(amount);
         sb.append(", currency='").append(currency).append('\'');
+        sb.append(", processedAmount=").append(processedAmount);
+        sb.append(", processedCurrency='").append(processedCurrency).append('\'');
         sb.append(", gatewayErrorCode='").append(gatewayErrorCode).append('\'');
         sb.append(", gatewayErrorMsg='").append(gatewayErrorMsg).append('\'');
         sb.append(", firstPaymentReferenceId='").append(firstPaymentReferenceId).append('\'');
@@ -197,9 +215,15 @@ public class PaymentTransactionJson extends JsonBase {
         if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
             return false;
         }
+        if (processedAmount != null ? processedAmount.compareTo(that.processedAmount) != 0 : that.processedAmount != null) {
+            return false;
+        }
         if (currency != null ? !currency.equals(that.currency) : that.currency != null) {
             return false;
         }
+        if (processedCurrency != null ? !processedCurrency.equals(that.processedCurrency) : that.processedCurrency != null) {
+            return false;
+        }
         if (transactionExternalKey != null ? !transactionExternalKey.equals(that.transactionExternalKey) : that.transactionExternalKey != null) {
             return false;
         }
@@ -233,7 +257,6 @@ public class PaymentTransactionJson extends JsonBase {
         if (transactionType != null ? !transactionType.equals(that.transactionType) : that.transactionType != null) {
             return false;
         }
-
         return true;
     }
 
@@ -247,6 +270,8 @@ public class PaymentTransactionJson extends JsonBase {
         result = 31 * result + (status != null ? status.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (processedAmount != null ? processedAmount.hashCode() : 0);
+        result = 31 * result + (processedCurrency != null ? processedCurrency.hashCode() : 0);
         result = 31 * result + (gatewayErrorCode != null ? gatewayErrorCode.hashCode() : 0);
         result = 31 * result + (gatewayErrorMsg != null ? gatewayErrorMsg.hashCode() : 0);
         result = 31 * result + (firstPaymentReferenceId != null ? firstPaymentReferenceId.hashCode() : 0);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
index 15092c2..ad38f38 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PhasePriceOverrideJson.java
@@ -201,8 +201,12 @@ public class PhasePriceOverrideJson {
                     return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice(), usagePriceOverrides);
                 } else {
                     final PhaseType phaseType = input.getPhaseType() != null ? PhaseType.valueOf(input.getPhaseType()) : null;
-                    final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(spec.getProductName(), spec.getProductCategory(), spec.getBillingPeriod(), spec.getPriceListName(), phaseType);
+
+                    final PlanPhaseSpecifier planPhaseSpecifier = spec.getPlanName() != null ?
+                                                                  new PlanPhaseSpecifier(spec.getPlanName(), phaseType) :
+                                                                  new PlanPhaseSpecifier(spec.getProductName(), spec.getBillingPeriod(), spec.getPriceListName(), phaseType);
                     return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice(), usagePriceOverrides);
+
                 }
             }
         }));
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RoleDefinitionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RoleDefinitionJson.java
index 07baeb9..1417203 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RoleDefinitionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RoleDefinitionJson.java
@@ -21,7 +21,7 @@ import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class RoleDefinitionJson {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java
index c8185b2..f07ac02 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/RolledUpUsageJson.java
@@ -27,7 +27,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class RolledUpUsageJson {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SessionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SessionJson.java
index 03f7395..1e28fbf 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SessionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SessionJson.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTimeZone;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class SessionJson {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
new file mode 100644
index 0000000..1c9bb24
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SimplePlanJson.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.TimeUnit;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class SimplePlanJson {
+
+    private final String planId;
+    private final String productName;
+    private final ProductCategory productCategory;
+    private final Currency currency;
+    private final BigDecimal amount;
+    private final BillingPeriod billingPeriod;
+    private final Integer trialLength;
+    private final TimeUnit trialTimeUnit;
+    private final List<String> availableBaseProducts;
+
+    @JsonCreator
+    public SimplePlanJson(@JsonProperty("planId")  final String planId,
+                          @JsonProperty("productName")  final String productName,
+                          @JsonProperty("productCategory")  final ProductCategory productCategory,
+                          @JsonProperty("currency")  final Currency currency,
+                          @JsonProperty("amount")  final BigDecimal amount,
+                          @JsonProperty("billingPeriod")  final BillingPeriod billingPeriod,
+                          @JsonProperty("trialLength")  final Integer trialLength,
+                          @JsonProperty("trialTimeUnit") final TimeUnit trialTimeUnit,
+                          @JsonProperty("availableBaseProducts") final List<String> availableBaseProducts) {
+        this.planId = planId;
+        this.productName = productName;
+        this.productCategory = productCategory;
+        this.currency = currency;
+        this.amount = amount;
+        this.billingPeriod = billingPeriod;
+        this.trialLength = trialLength;
+        this.trialTimeUnit = trialTimeUnit;
+        this.availableBaseProducts = availableBaseProducts;
+    }
+
+    public String getPlanId() {
+        return planId;
+    }
+
+    public String getProductName() {
+        return productName;
+    }
+
+    public ProductCategory getProductCategory() {
+        return productCategory;
+    }
+
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    public Integer getTrialLength() {
+        return trialLength;
+    }
+
+    public TimeUnit getTrialTimeUnit() {
+        return trialTimeUnit;
+    }
+
+    public List<String> getAvailableBaseProducts() {
+        return availableBaseProducts;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof SimplePlanJson)) {
+            return false;
+        }
+
+        final SimplePlanJson that = (SimplePlanJson) o;
+
+        if (planId != null ? !planId.equals(that.planId) : that.planId != null) {
+            return false;
+        }
+        if (productName != null ? !productName.equals(that.productName) : that.productName != null) {
+            return false;
+        }
+        if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (amount != null ? amount.compareTo(that.amount) != 0 : that.amount != null) {
+            return false;
+        }
+        if (billingPeriod != that.billingPeriod) {
+            return false;
+        }
+        if (trialLength != null ? !trialLength.equals(that.trialLength) : that.trialLength != null) {
+            return false;
+        }
+        if (availableBaseProducts != null ? !availableBaseProducts.equals(that.availableBaseProducts) : that.availableBaseProducts != null) {
+            return false;
+        }
+        return trialTimeUnit == that.trialTimeUnit;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = planId != null ? planId.hashCode() : 0;
+        result = 31 * result + (productName != null ? productName.hashCode() : 0);
+        result = 31 * result + (productCategory != null ? productCategory.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
+        result = 31 * result + (trialLength != null ? trialLength.hashCode() : 0);
+        result = 31 * result + (trialTimeUnit != null ? trialTimeUnit.hashCode() : 0);
+        result = 31 * result + (availableBaseProducts != null ? availableBaseProducts.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
index 645bf4c..13e4b82 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
@@ -26,6 +26,7 @@ import java.util.List;
 import javax.annotation.Nullable;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
@@ -36,10 +37,11 @@ import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.entitlement.api.Subscription;
 import org.killbill.billing.entitlement.api.SubscriptionEvent;
 import org.killbill.billing.util.audit.AccountAuditLogs;
+import org.killbill.billing.util.audit.AuditLog;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class SubscriptionJson extends JsonBase {
 
@@ -61,14 +63,19 @@ public class SubscriptionJson extends JsonBase {
     private final String phaseType;
     @ApiModelProperty(required = true)
     private final String priceList;
-    @ApiModelProperty(dataType = "org.killbill.billing.entitlement.api.Entitlement.EntitlementState")
+    @ApiModelProperty(required = true)
+    private final String planName;
+    //@ApiModelProperty(dataType = "org.killbill.billing.entitlement.api.Entitlement.EntitlementState")
+    @ApiModelProperty(dataType = "string", allowableValues = "PENDING,ACTIVE,BLOCKED,CANCELLED")
     private final String state;
-    @ApiModelProperty(dataType = "org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType")
+    //@ApiModelProperty(dataType = "org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType")
+    @ApiModelProperty(dataType = "string", allowableValues = "NATIVE,MIGRATED,TRANSFERRED")
     private final String sourceType;
     private final LocalDate cancelledDate;
     private final LocalDate chargedThroughDate;
     private final LocalDate billingStartDate;
     private final LocalDate billingEndDate;
+    private final Integer billCycleDayLocal;
     private final List<EventSubscriptionJson> events;
     private final List<PhasePriceOverrideJson> priceOverrides;
 
@@ -76,7 +83,6 @@ public class SubscriptionJson extends JsonBase {
 
         private final String eventId;
         private final String billingPeriod;
-        private final LocalDate requestedDate;
         private final LocalDate effectiveDate;
         private final String product;
         private final String priceList;
@@ -91,7 +97,6 @@ public class SubscriptionJson extends JsonBase {
         @JsonCreator
         public EventSubscriptionJson(@JsonProperty("eventId") final String eventId,
                                      @JsonProperty("billingPeriod") final String billingPeriod,
-                                     @JsonProperty("requestedDt") final LocalDate requestedDate,
                                      @JsonProperty("effectiveDt") final LocalDate effectiveDate,
                                      @JsonProperty("product") final String product,
                                      @JsonProperty("priceList") final String priceList,
@@ -105,7 +110,6 @@ public class SubscriptionJson extends JsonBase {
             super(auditLogs);
             this.eventId = eventId;
             this.billingPeriod = billingPeriod;
-            this.requestedDate = requestedDate;
             this.effectiveDate = effectiveDate;
             this.product = product;
             this.priceList = priceList;
@@ -118,14 +122,14 @@ public class SubscriptionJson extends JsonBase {
         }
 
         public EventSubscriptionJson(final SubscriptionEvent subscriptionEvent, @Nullable final AccountAuditLogs accountAuditLogs) {
-            super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscriptionEvent(subscriptionEvent.getId())));
+
+            super(toAuditLogJson(getAuditLogsForSubscriptionEvent(subscriptionEvent, accountAuditLogs)));
             final BillingPeriod billingPeriod = subscriptionEvent.getNextBillingPeriod() != null ? subscriptionEvent.getNextBillingPeriod() : subscriptionEvent.getPrevBillingPeriod();
             final Product product = subscriptionEvent.getNextProduct() != null ? subscriptionEvent.getNextProduct() : subscriptionEvent.getPrevProduct();
             final PriceList priceList = subscriptionEvent.getNextPriceList() != null ? subscriptionEvent.getNextPriceList() : subscriptionEvent.getPrevPriceList();
             final PlanPhase phase = subscriptionEvent.getNextPhase() != null ? subscriptionEvent.getNextPhase() : subscriptionEvent.getPrevPhase();
             this.eventId = subscriptionEvent.getId().toString();
             this.billingPeriod = billingPeriod != null ? billingPeriod.toString() : null;
-            this.requestedDate = subscriptionEvent.getRequestedDate();
             this.effectiveDate = subscriptionEvent.getEffectiveDate();
             this.product = product != null ? product.getName() : null;
             this.priceList = priceList != null ? priceList.getName() : null;
@@ -137,6 +141,20 @@ public class SubscriptionJson extends JsonBase {
             this.phase = phase != null ? phase.getName() : null;
         }
 
+
+        private static List<AuditLog> getAuditLogsForSubscriptionEvent(final SubscriptionEvent subscriptionEvent, @Nullable final AccountAuditLogs accountAuditLogs) {
+            if (accountAuditLogs == null) {
+                return null;
+            }
+            final ObjectType subscriptionEventObjectType = subscriptionEvent.getSubscriptionEventType().getObjectType();
+            if (subscriptionEventObjectType == ObjectType.SUBSCRIPTION_EVENT) {
+                return accountAuditLogs.getAuditLogsForSubscriptionEvent(subscriptionEvent.getId());
+            } else if (subscriptionEventObjectType == ObjectType.BLOCKING_STATES) {
+                return accountAuditLogs.getAuditLogsForBlockingState(subscriptionEvent.getId());
+            }
+            throw new IllegalStateException("Unepxected objectType " + subscriptionEventObjectType + " for SubscriptionEvent " + subscriptionEvent.getId());
+        }
+
         public String getEventId() {
             return eventId;
         }
@@ -145,10 +163,6 @@ public class SubscriptionJson extends JsonBase {
             return billingPeriod;
         }
 
-        public LocalDate getRequestedDate() {
-            return requestedDate;
-        }
-
         public LocalDate getEffectiveDate() {
             return effectiveDate;
         }
@@ -190,7 +204,6 @@ public class SubscriptionJson extends JsonBase {
             final StringBuilder sb = new StringBuilder("EventSubscriptionJson{");
             sb.append("eventId='").append(eventId).append('\'');
             sb.append(", billingPeriod='").append(billingPeriod).append('\'');
-            sb.append(", requestedDate=").append(requestedDate);
             sb.append(", effectiveDate=").append(effectiveDate);
             sb.append(", product='").append(product).append('\'');
             sb.append(", priceList='").append(priceList).append('\'');
@@ -242,9 +255,6 @@ public class SubscriptionJson extends JsonBase {
             if (product != null ? !product.equals(that.product) : that.product != null) {
                 return false;
             }
-            if (requestedDate != null ? requestedDate.compareTo(that.requestedDate) != 0 : that.requestedDate != null) {
-                return false;
-            }
             if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
                 return false;
             }
@@ -259,7 +269,6 @@ public class SubscriptionJson extends JsonBase {
         public int hashCode() {
             int result = eventId != null ? eventId.hashCode() : 0;
             result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
-            result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
             result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
             result = 31 * result + (product != null ? product.hashCode() : 0);
             result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
@@ -284,12 +293,14 @@ public class SubscriptionJson extends JsonBase {
                             @JsonProperty("billingPeriod") @Nullable final String billingPeriod,
                             @JsonProperty("phaseType") @Nullable final String phaseType,
                             @JsonProperty("priceList") @Nullable final String priceList,
+                            @JsonProperty("planName") @Nullable final String planName,
                             @JsonProperty("state") @Nullable final String state,
                             @JsonProperty("sourceType") @Nullable final String sourceType,
                             @JsonProperty("cancelledDate") @Nullable final LocalDate cancelledDate,
                             @JsonProperty("chargedThroughDate") @Nullable final LocalDate chargedThroughDate,
                             @JsonProperty("billingStartDate") @Nullable final LocalDate billingStartDate,
                             @JsonProperty("billingEndDate") @Nullable final LocalDate billingEndDate,
+                            @JsonProperty("billCycleDayLocal") @Nullable final Integer billCycleDayLocal,
                             @JsonProperty("events") @Nullable final List<EventSubscriptionJson> events,
                             @JsonProperty("priceOverrides") final List<PhasePriceOverrideJson> priceOverrides,
                             @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
@@ -300,12 +311,14 @@ public class SubscriptionJson extends JsonBase {
         this.billingPeriod = billingPeriod;
         this.phaseType = phaseType;
         this.priceList = priceList;
+        this.planName = planName;
         this.state = state;
         this.sourceType = sourceType;
         this.cancelledDate = cancelledDate;
         this.chargedThroughDate = chargedThroughDate;
         this.billingStartDate = billingStartDate;
         this.billingEndDate = billingEndDate;
+        this.billCycleDayLocal = billCycleDayLocal;
         this.accountId = accountId;
         this.bundleId = bundleId;
         this.subscriptionId = subscriptionId;
@@ -345,6 +358,12 @@ public class SubscriptionJson extends JsonBase {
         } else {
             this.priceList = subscription.getLastActivePriceList().getName();
         }
+        if (subscription.getLastActivePlan() == null) {
+            this.planName = firstEvent == null ? null : firstEvent.getNextPlan().getName();
+        } else {
+            this.planName = subscription.getLastActivePlan().getName();
+        }
+
 
         this.state = subscription.getState().name();
         this.sourceType = subscription.getSourceType().name();
@@ -352,6 +371,7 @@ public class SubscriptionJson extends JsonBase {
         this.chargedThroughDate = subscription.getChargedThroughDate();
         this.billingStartDate = subscription.getBillingStartDate();
         this.billingEndDate = subscription.getBillingEndDate();
+        this.billCycleDayLocal = subscription.getBillCycleDayLocal();
         this.accountId = subscription.getAccountId().toString();
         this.bundleId = subscription.getBundleId().toString();
         this.subscriptionId = subscription.getId().toString();
@@ -416,6 +436,10 @@ public class SubscriptionJson extends JsonBase {
         return priceList;
     }
 
+    public String getPlanName() {
+        return planName;
+    }
+
     public String getState() {
         return state;
     }
@@ -440,6 +464,10 @@ public class SubscriptionJson extends JsonBase {
         return billingEndDate;
     }
 
+    public Integer getBillCycleDayLocal() {
+        return billCycleDayLocal;
+    }
+
     public List<EventSubscriptionJson> getEvents() {
         return events;
     }
@@ -461,12 +489,14 @@ public class SubscriptionJson extends JsonBase {
         sb.append(", billingPeriod='").append(billingPeriod).append('\'');
         sb.append(", phaseType='").append(phaseType).append('\'');
         sb.append(", priceList='").append(priceList).append('\'');
+        sb.append(", planName='").append(planName).append('\'');
         sb.append(", state='").append(state).append('\'');
         sb.append(", sourceType='").append(sourceType).append('\'');
         sb.append(", cancelledDate=").append(cancelledDate);
         sb.append(", chargedThroughDate=").append(chargedThroughDate);
         sb.append(", billingStartDate=").append(billingStartDate);
         sb.append(", billingEndDate=").append(billingEndDate);
+        sb.append(", billCycleDayLocal=").append(billCycleDayLocal);
         sb.append(", events=").append(events);
         sb.append(", priceOverrides=").append(priceOverrides);
         sb.append('}');
@@ -517,6 +547,9 @@ public class SubscriptionJson extends JsonBase {
         if (priceList != null ? !priceList.equals(that.priceList) : that.priceList != null) {
             return false;
         }
+        if (planName != null ? !planName.equals(that.planName) : that.planName != null) {
+            return false;
+        }
         if (productCategory != null ? !productCategory.equals(that.productCategory) : that.productCategory != null) {
             return false;
         }
@@ -538,6 +571,9 @@ public class SubscriptionJson extends JsonBase {
         if (priceOverrides != null ? !priceOverrides.equals(that.priceOverrides) : that.priceOverrides != null) {
             return false;
         }
+        if (billCycleDayLocal != null ? !billCycleDayLocal.equals(that.billCycleDayLocal) : that.billCycleDayLocal != null) {
+            return false;
+        }
         return true;
     }
 
@@ -553,12 +589,14 @@ public class SubscriptionJson extends JsonBase {
         result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
         result = 31 * result + (phaseType != null ? phaseType.hashCode() : 0);
         result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
+        result = 31 * result + (planName != null ? planName.hashCode() : 0);
         result = 31 * result + (state != null ? state.hashCode() : 0);
         result = 31 * result + (sourceType != null ? sourceType.hashCode() : 0);
         result = 31 * result + (cancelledDate != null ? cancelledDate.hashCode() : 0);
         result = 31 * result + (chargedThroughDate != null ? chargedThroughDate.hashCode() : 0);
         result = 31 * result + (billingStartDate != null ? billingStartDate.hashCode() : 0);
         result = 31 * result + (billingEndDate != null ? billingEndDate.hashCode() : 0);
+        result = 31 * result + (billCycleDayLocal != null ? billCycleDayLocal.hashCode() : 0);
         result = 31 * result + (events != null ? events.hashCode() : 0);
         result = 31 * result + (priceOverrides != null ? priceOverrides.hashCode() : 0);
         return result;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java
index 9e961a6..23cc0e8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionUsageRecordJson.java
@@ -28,9 +28,10 @@ import org.killbill.billing.usage.api.UsageRecord;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Function;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class SubscriptionUsageRecordJson {
 
@@ -38,11 +39,15 @@ public class SubscriptionUsageRecordJson {
     private final String subscriptionId;
     @ApiModelProperty(required = true)
     private final List<UnitUsageRecordJson> unitUsageRecords;
+    @ApiModelProperty(required = false)
+    private final String trackingId;
 
     @JsonCreator
     public SubscriptionUsageRecordJson(@JsonProperty("subscriptionId") final String subscriptionId,
+                                       @JsonProperty("trackingId") final String trackingId,
                                        @JsonProperty("unitUsageRecords") final List<UnitUsageRecordJson> unitUsageRecords) {
         this.subscriptionId = subscriptionId;
+        this.trackingId = trackingId;
         this.unitUsageRecords = unitUsageRecords;
     }
 
@@ -54,6 +59,10 @@ public class SubscriptionUsageRecordJson {
         return unitUsageRecords;
     }
 
+    public String getTrackingId() {
+        return trackingId;
+    }
+
     public static class UnitUsageRecordJson {
 
         private final String unitType;
@@ -117,7 +126,7 @@ public class SubscriptionUsageRecordJson {
                 return input.toUnitUsageRecord();
             }
         }));
-        final SubscriptionUsageRecord result = new SubscriptionUsageRecord(UUID.fromString(subscriptionId), tmp);
+        final SubscriptionUsageRecord result = new SubscriptionUsageRecord(UUID.fromString(subscriptionId), trackingId, tmp);
         return result;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagDefinitionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagDefinitionJson.java
index fedac1c..453883e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagDefinitionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagDefinitionJson.java
@@ -29,7 +29,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class TagDefinitionJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagJson.java
index 16dc757..2b4f729 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TagJson.java
@@ -27,7 +27,7 @@ import org.killbill.billing.util.tag.TagDefinition;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class TagJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TenantJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TenantJson.java
index 76fe844..c6273b2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TenantJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/TenantJson.java
@@ -21,7 +21,7 @@ import org.killbill.billing.tenant.api.TenantData;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class TenantJson extends JsonBase {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/UserRolesJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/UserRolesJson.java
index 2a9a7b0..7cdf4fd 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/UserRolesJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/UserRolesJson.java
@@ -21,7 +21,7 @@ import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.wordnik.swagger.annotations.ApiModelProperty;
+import io.swagger.annotations.ApiModelProperty;
 
 public class UserRolesJson {
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index ca85ff4..265a1fa 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -51,15 +51,18 @@ import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.OrderingType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
 import org.killbill.billing.account.api.AccountEmail;
-import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.MutableAccountData;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
@@ -72,6 +75,7 @@ import org.killbill.billing.jaxrs.JaxrsExecutors;
 import org.killbill.billing.jaxrs.json.AccountEmailJson;
 import org.killbill.billing.jaxrs.json.AccountJson;
 import org.killbill.billing.jaxrs.json.AccountTimelineJson;
+import org.killbill.billing.jaxrs.json.BlockingStateJson;
 import org.killbill.billing.jaxrs.json.BundleJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.InvoiceEmailJson;
@@ -84,7 +88,7 @@ import org.killbill.billing.jaxrs.json.PaymentTransactionJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
-import org.killbill.billing.overdue.OverdueInternalApi;
+import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueState;
 import org.killbill.billing.overdue.config.api.OverdueException;
@@ -95,6 +99,7 @@ import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditLevel;
@@ -107,8 +112,8 @@ import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.JaxrsConfig;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.JaxrsConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
@@ -117,7 +122,6 @@ import org.killbill.commons.metrics.MetricTag;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
@@ -125,10 +129,10 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -142,7 +146,7 @@ public class AccountResource extends JaxRsResourceBase {
     private final SubscriptionApi subscriptionApi;
     private final InvoiceUserApi invoiceApi;
     private final InvoicePaymentApi invoicePaymentApi;
-    private final OverdueInternalApi overdueApi;
+    private final OverdueApi overdueApi;
     private final PaymentConfig paymentConfig;
     private final JaxrsExecutors jaxrsExecutors;
     private final JaxrsConfig jaxrsConfig;
@@ -157,14 +161,13 @@ public class AccountResource extends JaxRsResourceBase {
                            final AuditUserApi auditUserApi,
                            final CustomFieldUserApi customFieldUserApi,
                            final SubscriptionApi subscriptionApi,
-                           final AccountInternalApi accountInternalApi,
-                           final OverdueInternalApi overdueApi,
+                           final OverdueApi overdueApi,
                            final Clock clock,
                            final PaymentConfig paymentConfig,
                            final JaxrsExecutors jaxrsExecutors,
                            final JaxrsConfig jaxrsConfig,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, subscriptionApi, clock, context);
         this.subscriptionApi = subscriptionApi;
         this.invoiceApi = invoiceApi;
         this.invoicePaymentApi = invoicePaymentApi;
@@ -378,7 +381,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
     }
 
-
     @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
@@ -398,13 +400,13 @@ public class AccountResource extends JaxRsResourceBase {
         final Callable<List<SubscriptionBundle>> bundlesCallable = new Callable<List<SubscriptionBundle>>() {
             @Override
             public List<SubscriptionBundle> call() throws Exception {
-                return subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
+                return subscriptionApi.getSubscriptionBundlesForAccountId(accountId, tenantContext);
             }
         };
         final Callable<List<Invoice>> invoicesCallable = new Callable<List<Invoice>>() {
             @Override
             public List<Invoice> call() throws Exception {
-                return invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
+                return invoiceApi.getInvoicesByAccount(accountId, false, tenantContext);
             }
         };
         final Callable<List<InvoicePayment>> invoicePaymentsCallable = new Callable<List<InvoicePayment>>() {
@@ -416,7 +418,7 @@ public class AccountResource extends JaxRsResourceBase {
         final Callable<List<Payment>> paymentsCallable = new Callable<List<Payment>>() {
             @Override
             public List<Payment> call() throws Exception {
-                return paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), tenantContext);
+                return paymentApi.getAccountPayments(accountId, false, false, ImmutableList.<PluginProperty>of(), tenantContext);
             }
         };
         final Callable<AccountAuditLogs> auditsCallable = new Callable<AccountAuditLogs>() {
@@ -435,59 +437,76 @@ public class AccountResource extends JaxRsResourceBase {
         AccountAuditLogs accountAuditLogs = null;
 
         if (parallel) {
-
             final ExecutorService executor = jaxrsExecutors.getJaxrsExecutorService();
             final Future<List<SubscriptionBundle>> futureBundlesCallable = executor.submit(bundlesCallable);
             final Future<List<Invoice>> futureInvoicesCallable = executor.submit(invoicesCallable);
             final Future<List<InvoicePayment>> futureInvoicePaymentsCallable = executor.submit(invoicePaymentsCallable);
             final Future<List<Payment>> futurePaymentsCallable = executor.submit(paymentsCallable);
             final Future<AccountAuditLogs> futureAuditsCallable = executor.submit(auditsCallable);
-
-            try {
-                long ini = System.currentTimeMillis();
-                do {
-                    bundles = (bundles == null) ? runCallableAndHandleTimeout(futureBundlesCallable, 100) : bundles;
-                    invoices = (invoices == null) ? runCallableAndHandleTimeout(futureInvoicesCallable, 100) : invoices;
-                    invoicePayments = (invoicePayments == null) ? runCallableAndHandleTimeout(futureInvoicePaymentsCallable, 100) : invoicePayments;
-                    payments = (payments == null) ? runCallableAndHandleTimeout(futurePaymentsCallable, 100) : payments;
-                    accountAuditLogs = (accountAuditLogs == null) ? runCallableAndHandleTimeout(futureAuditsCallable, 100) : accountAuditLogs;
-                } while ((System.currentTimeMillis() - ini < jaxrsConfig.getJaxrsTimeout().getMillis()) &&
-                         (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null));
-
-                if (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null) {
-                    Response.status(Status.SERVICE_UNAVAILABLE).build();
-                }
-            } catch (InterruptedException e) {
-                handleCallableException(e, ImmutableList.<Future>of(futureBundlesCallable, futureInvoicesCallable, futureInvoicePaymentsCallable, futurePaymentsCallable, futureAuditsCallable));
-            } catch (ExecutionException e) {
-                handleCallableException(e.getCause(), ImmutableList.<Future>of(futureBundlesCallable, futureInvoicesCallable, futureInvoicePaymentsCallable, futurePaymentsCallable, futureAuditsCallable));
+            final ImmutableList<Future> toBeCancelled = ImmutableList.<Future>of(futureBundlesCallable, futureInvoicesCallable, futureInvoicePaymentsCallable, futurePaymentsCallable, futureAuditsCallable);
+            final int timeoutMsec = 100;
+
+            final long ini = System.currentTimeMillis();
+            do {
+                bundles = (bundles == null) ? waitOnFutureAndHandleTimeout("bundles", futureBundlesCallable, timeoutMsec, toBeCancelled) : bundles;
+                invoices = (invoices == null) ? waitOnFutureAndHandleTimeout("invoices", futureInvoicesCallable, timeoutMsec, toBeCancelled) : invoices;
+                invoicePayments = (invoicePayments == null) ? waitOnFutureAndHandleTimeout("invoicePayments", futureInvoicePaymentsCallable, timeoutMsec, toBeCancelled) : invoicePayments;
+                payments = (payments == null) ? waitOnFutureAndHandleTimeout("payments", futurePaymentsCallable, timeoutMsec, toBeCancelled) : payments;
+                accountAuditLogs = (accountAuditLogs == null) ? waitOnFutureAndHandleTimeout("accountAuditLogs", futureAuditsCallable, timeoutMsec, toBeCancelled) : accountAuditLogs;
+            } while ((System.currentTimeMillis() - ini < jaxrsConfig.getJaxrsTimeout().getMillis()) &&
+                     (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null));
+
+            if (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null) {
+                Response.status(Status.SERVICE_UNAVAILABLE).build();
             }
-
         } else {
-            try {
-                invoices = invoicesCallable.call();
-                payments = paymentsCallable.call();
-                bundles = bundlesCallable.call();
-                accountAuditLogs = auditsCallable.call();
-                invoicePayments = invoicePaymentsCallable.call();
-            } catch (Exception e) {
-                handleCallableException(e);
-            }
+            invoices = runCallable("invoices", invoicesCallable);
+            payments = runCallable("payments", paymentsCallable);
+            bundles = runCallable("bundles", bundlesCallable);
+            accountAuditLogs = runCallable("accountAuditLogs", auditsCallable);
+            invoicePayments = runCallable("invoicePayments", invoicePaymentsCallable);
         }
 
         json = new AccountTimelineJson(account, invoices, payments, invoicePayments, bundles, accountAuditLogs);
         return Response.status(Status.OK).entity(json).build();
     }
 
-    private <T> T runCallableAndHandleTimeout(final Future<T> future, final long timeoutMsec) throws ExecutionException, InterruptedException {
+    private <T> T waitOnFutureAndHandleTimeout(final String logSuffix, final Future<T> future, final long timeoutMsec, final Iterable<Future> toBeCancelled) throws PaymentApiException, AccountApiException, InvoiceApiException, SubscriptionApiException {
+        try {
+            return waitOnFutureAndHandleTimeout(future, timeoutMsec);
+        } catch (final InterruptedException e) {
+            log.warn("InterruptedException while retrieving {}", logSuffix, e);
+            handleCallableException(e, toBeCancelled);
+        } catch (final ExecutionException e) {
+            log.warn("ExecutionException while retrieving {}", logSuffix, e);
+            handleCallableException(e.getCause(), toBeCancelled);
+        }
+
+        // Never reached
+        return null;
+    }
+
+    private <T> T waitOnFutureAndHandleTimeout(final Future<T> future, final long timeoutMsec) throws ExecutionException, InterruptedException {
         try {
             return future.get(timeoutMsec, TimeUnit.MILLISECONDS);
-        } catch (TimeoutException e) {
+        } catch (final TimeoutException e) {
             return null;
         }
     }
 
-    private void handleCallableException(final Throwable causeOrException, final List<Future> toBeCancelled) throws AccountApiException, SubscriptionApiException, PaymentApiException, InvoiceApiException {
+    private <T> T runCallable(final String logSuffix, final Callable<T> callable) throws PaymentApiException, AccountApiException, InvoiceApiException, SubscriptionApiException {
+        try {
+            return callable.call();
+        } catch (final Exception e) {
+            log.warn("InterruptedException while retrieving {}", logSuffix, e);
+            handleCallableException(e);
+        }
+
+        // Never reached
+        return null;
+    }
+
+    private void handleCallableException(final Throwable causeOrException, final Iterable<Future> toBeCancelled) throws AccountApiException, SubscriptionApiException, PaymentApiException, InvoiceApiException {
         for (final Future f : toBeCancelled) {
             f.cancel(true);
         }
@@ -595,6 +614,7 @@ public class AccountResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Account not found")})
     public Response getInvoices(@PathParam("accountId") final String accountIdString,
                                 @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+                                @QueryParam(QUERY_WITH_MIGRATION_INVOICES) @DefaultValue("false") final boolean withMigrationInvoices,
                                 @QueryParam(QUERY_UNPAID_INVOICES_ONLY) @DefaultValue("false") final boolean unpaidInvoicesOnly,
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
@@ -606,13 +626,13 @@ public class AccountResource extends JaxRsResourceBase {
 
         final List<Invoice> invoices = unpaidInvoicesOnly ?
                                        new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, tenantContext)) :
-                                       invoiceApi.getInvoicesByAccount(accountId, tenantContext);
+                                       invoiceApi.getInvoicesByAccount(accountId, withMigrationInvoices, tenantContext);
 
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
 
         final List<InvoiceJson> result = new LinkedList<InvoiceJson>();
         for (final Invoice invoice : invoices) {
-            result.add(new InvoiceJson(invoice, withItems, accountAuditLogs));
+            result.add(new InvoiceJson(invoice, withItems, null, accountAuditLogs));
         }
 
         return Response.status(Status.OK).entity(result).build();
@@ -633,13 +653,14 @@ public class AccountResource extends JaxRsResourceBase {
     public Response getInvoicePayments(@PathParam("accountId") final String accountIdStr,
                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                        @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                       @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                        @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID accountId = UUID.fromString(accountIdStr);
         final TenantContext tenantContext = context.createContext(request);
         final Account account = accountUserApi.getAccountById(accountId, tenantContext);
-        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), withPluginInfo, pluginProperties, tenantContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(account.getId(), withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePaymentsByAccount(accountId, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
         final List<InvoicePaymentJson> result = new ArrayList<InvoicePaymentJson>(payments.size());
@@ -685,7 +706,7 @@ public class AccountResource extends JaxRsResourceBase {
                                            invoice.getBalance() : remainingRequestPayment;
             if (amountToPay.compareTo(BigDecimal.ZERO) > 0) {
                 final UUID paymentMethodId = externalPayment ? null : account.getPaymentMethodId();
-                createPurchaseForInvoice(account, invoice.getId(), amountToPay, paymentMethodId, externalPayment, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), amountToPay, paymentMethodId, externalPayment, null, null, pluginProperties, callContext);
             }
             remainingRequestPayment = remainingRequestPayment.subtract(amountToPay);
             if (remainingRequestPayment.compareTo(BigDecimal.ZERO) == 0) {
@@ -697,7 +718,7 @@ public class AccountResource extends JaxRsResourceBase {
         // then we apply some credit on the account.
         //
         if (externalPayment && remainingRequestPayment.compareTo(BigDecimal.ZERO) > 0) {
-            invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), callContext);
+            invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), true, "pay all invoices", callContext);
         }
         return Response.status(Status.OK).build();
     }
@@ -739,7 +760,7 @@ public class AccountResource extends JaxRsResourceBase {
         final UUID paymentMethodId = paymentApi.addPaymentMethod(account, data.getExternalKey(), data.getPluginName(), isDefault, data.getPluginDetail(), pluginProperties, callContext);
         if (payAllUnpaidInvoices && unpaidInvoices.size() > 0) {
             for (final Invoice invoice : unpaidInvoices) {
-                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), paymentMethodId, false, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), paymentMethodId, false, null, null, pluginProperties, callContext);
             }
         }
         return uriBuilder.buildResponse(PaymentMethodResource.class, "getPaymentMethod", paymentMethodId, uriInfo.getBaseUri().toString());
@@ -801,7 +822,6 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-
     @TimedResource
     @PUT
     @Consumes(APPLICATION_JSON)
@@ -828,7 +848,7 @@ public class AccountResource extends JaxRsResourceBase {
         if (payAllUnpaidInvoices) {
             final Collection<Invoice> unpaidInvoices = invoiceApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
             for (final Invoice invoice : unpaidInvoices) {
-                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), newPaymentMethodId, false, pluginProperties, callContext);
+                createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), newPaymentMethodId, false, null, null, pluginProperties, callContext);
             }
         }
         return Response.status(Status.OK).build();
@@ -847,11 +867,12 @@ public class AccountResource extends JaxRsResourceBase {
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                 @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                 @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final UUID accountId = UUID.fromString(accountIdStr);
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final TenantContext tenantContext = context.createContext(request);
-        final List<Payment> payments = paymentApi.getAccountPayments(accountId, withPluginInfo, pluginProperties, tenantContext);
+        final List<Payment> payments = paymentApi.getAccountPayments(accountId, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
         final List<PaymentJson> result = ImmutableList.copyOf(Iterables.transform(payments, new Function<Payment, PaymentJson>() {
             @Override
@@ -945,11 +966,16 @@ public class AccountResource extends JaxRsResourceBase {
         //
         final UUID paymentMethodId;
         if (paymentId != null) {
-            final Payment initialPayment = paymentApi.getPayment(paymentId, false, pluginProperties, callContext);
-            final PaymentTransaction pendingTransaction = lookupPendingTransaction(initialPayment,
+            final Payment initialPayment = paymentApi.getPayment(paymentId, false, false, pluginProperties, callContext);
+            final PaymentTransaction pendingOrSuccessTransaction = lookupPendingOrSuccessTransaction(initialPayment,
                                                                                    json != null ? json.getTransactionId() : null,
                                                                                    json != null ? json.getTransactionExternalKey() : null,
                                                                                    json != null ? json.getTransactionType() : null);
+            // If transaction was already completed, return early (See #626)
+            if (pendingOrSuccessTransaction.getTransactionStatus() == TransactionStatus.SUCCESS) {
+                return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", pendingOrSuccessTransaction.getPaymentId());
+            }
+
             paymentMethodId = initialPayment.getPaymentMethodId();
         } else {
             paymentMethodId = paymentMethodIdStr == null ? account.getPaymentMethodId() : UUID.fromString(paymentMethodIdStr);
@@ -997,11 +1023,62 @@ public class AccountResource extends JaxRsResourceBase {
         final TenantContext tenantContext = context.createContext(request);
 
         final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), tenantContext);
-        final OverdueState overdueState = overdueApi.getOverdueStateFor(account, tenantContext);
+        final OverdueState overdueState = overdueApi.getOverdueStateFor(account.getId(), tenantContext);
 
         return Response.status(Status.OK).entity(new OverdueStateJson(overdueState, paymentConfig)).build();
     }
 
+
+    /*
+     * *************************      BLOCKING STATE     *****************************
+     */
+
+    @TimedResource
+    @GET
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + BLOCK)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve blocking states for account", response = BlockingStateJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied")})
+    public Response getBlockingStates(@PathParam(ID_PARAM_NAME) final String id,
+                                      @QueryParam(QUERY_BLOCKING_STATE_TYPES) final List<BlockingStateType> typeFilter,
+                                      @QueryParam(QUERY_BLOCKING_STATE_SVCS) final List<String> svcsFilter,
+                                      @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
+                                      @javax.ws.rs.core.Context final HttpServletRequest request) throws EntitlementApiException {
+
+        final TenantContext tenantContext = this.context.createContext(request);
+        final UUID accountId = UUID.fromString(id);
+        final Iterable<BlockingState> blockingStates = subscriptionApi.getBlockingStates(accountId, typeFilter, svcsFilter, OrderingType.ASCENDING, SubscriptionApi.ALL_EVENTS, tenantContext);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
+
+        final List<BlockingStateJson> result = ImmutableList.copyOf(Iterables.transform(blockingStates, new Function<BlockingState, BlockingStateJson>() {
+            @Override
+            public BlockingStateJson apply(final BlockingState input) {
+                return new BlockingStateJson(input, accountAuditLogs);
+            }
+        }));
+
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+    @TimedResource
+    @PUT
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + BLOCK)
+    @Consumes(APPLICATION_JSON)
+    @ApiOperation(value = "Block an account")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied"),
+                           @ApiResponse(code = 404, message = "Account not found")})
+    public Response addAccountBlockingState(final BlockingStateJson json,
+                                           @PathParam(ID_PARAM_NAME) final String id,
+                                           @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                           @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                           @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                           @HeaderParam(HDR_REASON) final String reason,
+                                           @HeaderParam(HDR_COMMENT) final String comment,
+                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
+        return addBlockingState(json, id, BlockingStateType.ACCOUNT, requestedDate, pluginPropertiesString, createdBy, reason, comment, request);
+    }
+
+
     /*
      * *************************      CUSTOM FIELDS     *****************************
      */
@@ -1066,7 +1143,7 @@ public class AccountResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Account not found")})
     public Response getTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
         final UUID accountId = UUID.fromString(accountIdString);
         return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
@@ -1082,7 +1159,7 @@ public class AccountResource extends JaxRsResourceBase {
     public Response getAllTags(@PathParam(ID_PARAM_NAME) final String accountIdString,
                                @QueryParam(QUERY_OBJECT_TYPE) final ObjectType objectType,
                                @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                               @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                               @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
         final UUID accountId = UUID.fromString(accountIdString);
         final TenantContext tenantContext = context.createContext(request);
@@ -1092,7 +1169,6 @@ public class AccountResource extends JaxRsResourceBase {
         return createTagResponse(accountId, tags, auditMode, tenantContext);
     }
 
-
     @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
@@ -1202,7 +1278,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                                                }
                                                                            }
                                                                           )
-                                                    .orNull();
+                .orNull();
         if (existingEmail == null) {
             accountUserApi.addEmail(accountId, json.toAccountEmail(UUIDs.randomUUID()), callContext);
         }
@@ -1239,4 +1315,55 @@ public class AccountResource extends JaxRsResourceBase {
     protected ObjectType getObjectType() {
         return ObjectType.ACCOUNT;
     }
+
+    // -------------------------------------
+    //      Parent and child accounts
+    // -------------------------------------
+
+    @TimedResource
+    @GET
+    @Path("/{accountId:" + UUID_PATTERN + "}/" + CHILDREN)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "List children accounts", response = AccountJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid parent account id supplied"),
+                           @ApiResponse(code = 404, message = "Parent Account not found")})
+    public Response getChildrenAccounts(@PathParam("accountId") final String parentAccountId,
+                                        @QueryParam(QUERY_ACCOUNT_WITH_BALANCE) @DefaultValue("false") final Boolean accountWithBalance,
+                                        @QueryParam(QUERY_ACCOUNT_WITH_BALANCE_AND_CBA) @DefaultValue("false") final Boolean accountWithBalanceAndCBA,
+                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
+                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
+
+        final TenantContext tenantContext = context.createContext(request);
+        final List<Account> accounts = accountUserApi.getChildrenAccounts(UUID.fromString(parentAccountId), tenantContext);
+
+        final List<AccountJson> accountJson = new ArrayList<AccountJson>();
+        for (final Account account : accounts) {
+            final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(account.getId(), auditMode.getLevel(), tenantContext);
+            accountJson.add(getAccount(account, accountWithBalance, accountWithBalanceAndCBA, accountAuditLogs, tenantContext));
+        }
+        return Response.status(Status.OK).entity(accountJson).build();
+    }
+
+    @TimedResource
+    @POST
+    @Path("/{childAccountId:" + UUID_PATTERN + "}/" + TRANSFER_CREDIT)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Move a given child credit to the parent level")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Account does not have credit"),
+                           @ApiResponse(code = 404, message = "Account not found")})
+    public Response transferChildCreditToParent(@PathParam("childAccountId") final String childAccountIdString,
+                                                @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 InvoiceApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID childAccountId = UUID.fromString(childAccountIdString);
+
+        invoiceApi.transferChildCreditToParent(childAccountId, callContext);
+        return Response.status(Response.Status.OK).build();
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
index ed98edb..c964e67 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AdminResource.java
@@ -17,19 +17,24 @@
 
 package org.killbill.billing.jaxrs.resources;
 
+import java.util.List;
 import java.util.UUID;
 
 import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.jaxrs.json.AdminPaymentJson;
 import org.killbill.billing.jaxrs.util.Context;
@@ -41,20 +46,28 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.tenant.api.Tenant;
+import org.killbill.billing.tenant.api.TenantApiException;
+import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
+import org.killbill.billing.util.api.RecordIdApi;
 import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Ehcache;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -64,11 +77,17 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 public class AdminResource extends JaxRsResourceBase {
 
     private final AdminPaymentApi adminPaymentApi;
+    private final TenantUserApi tenantApi;
+    private final CacheManager cacheManager;
+    private final RecordIdApi recordIdApi;
 
     @Inject
-    public AdminResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi, final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final PaymentApi paymentApi, final AdminPaymentApi adminPaymentApi, final Clock clock, final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+    public AdminResource(final JaxrsUriBuilder uriBuilder, final TagUserApi tagUserApi, final CustomFieldUserApi customFieldUserApi, final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final PaymentApi paymentApi, final AdminPaymentApi adminPaymentApi, final CacheManager cacheManager, final TenantUserApi tenantApi, final RecordIdApi recordIdApi, final Clock clock, final Context context) {
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.adminPaymentApi = adminPaymentApi;
+        this.tenantApi = tenantApi;
+        this.recordIdApi = recordIdApi;
+        this.cacheManager = cacheManager;
     }
 
 
@@ -88,7 +107,7 @@ public class AdminResource extends JaxRsResourceBase {
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
-        final Payment payment = paymentApi.getPayment(UUID.fromString(paymentIdStr), false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = paymentApi.getPayment(UUID.fromString(paymentIdStr), false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         final UUID paymentTransactionId = UUID.fromString(paymentTransactionIdStr);
 
@@ -104,4 +123,106 @@ public class AdminResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
+    @DELETE
+    @Path("/" + CACHE)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Invalidates the given Cache if specified, otherwise invalidates all caches")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Cache name does not exist or is not alive")})
+    public Response invalidatesCache(@QueryParam("cacheName") final String cacheName,
+                                    @javax.ws.rs.core.Context final HttpServletRequest request) {
+        if (null != cacheName && !cacheName.isEmpty()) {
+            final Ehcache cache = cacheManager.getEhcache(cacheName);
+            // check if cache is null
+            if (cache == null) {
+                log.warn("Cache for specified cacheName='{}' does not exist or is not alive", cacheName);
+                return Response.status(Status.BAD_REQUEST).build();
+            }
+            // Clear given cache
+            cache.removeAll();
+        }
+        else {
+            // if not given a specific cacheName, clear all
+            cacheManager.clearAll();
+        }
+        return Response.status(Status.OK).build();
+    }
+
+    @DELETE
+    @Path("/" + CACHE + "/" + ACCOUNTS + "/{accountId:" + UUID_PATTERN + "}/")
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Invalidates Caches per account level")
+    @ApiResponses(value = {})
+    public Response invalidatesCacheByAccount(@PathParam("accountId") final String accountId,
+                                              @javax.ws.rs.core.Context final HttpServletRequest request) {
+
+        // clear account-record-id cache by accountId
+        final Ehcache accountRecordIdCache = cacheManager.getEhcache(CacheType.ACCOUNT_RECORD_ID.getCacheName());
+        accountRecordIdCache.remove(accountId);
+
+        // clear account-immutable cache by accountId
+        final Ehcache accountImmutableCache = cacheManager.getEhcache(CacheType.ACCOUNT_IMMUTABLE.getCacheName());
+        accountImmutableCache.remove(UUID.fromString(accountId));
+
+        // clear account-bcd cache by accountId
+        final Ehcache accountBcdCache = cacheManager.getEhcache(CacheType.ACCOUNT_BCD.getCacheName());
+        accountBcdCache.remove(UUID.fromString(accountId));
+
+        return Response.status(Status.OK).build();
+    }
+
+    @DELETE
+    @Path("/" + CACHE + "/" + TENANTS)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Invalidates Caches per tenant level")
+    @ApiResponses(value = {})
+    public Response invalidatesCacheByTenant(@QueryParam("tenantApiKey") final String tenantApiKey,
+                                              @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+
+        // creating Tenant Context from Request
+        TenantContext tenantContext = context.createContext(request);
+
+        Tenant currentTenant = tenantApi.getTenantById(tenantContext.getTenantId());
+
+        // getting Tenant Record Id
+        Long tenantRecordId = recordIdApi.getRecordId(tenantContext.getTenantId(), ObjectType.TENANT, tenantContext);
+
+        // clear tenant-record-id cache by tenantId
+        final Ehcache tenantRecordIdCache = cacheManager.getEhcache(CacheType.TENANT_RECORD_ID.getCacheName());
+        tenantRecordIdCache.remove(currentTenant.getId().toString());
+
+        // clear tenant-payment-state-machine-config cache by tenantRecordId
+        final Ehcache tenantPaymentStateMachineConfigCache = cacheManager.getEhcache(CacheType.TENANT_PAYMENT_STATE_MACHINE_CONFIG.getCacheName());
+        removeCacheByKey(tenantPaymentStateMachineConfigCache, tenantRecordId.toString());
+
+        // clear tenant cache by tenantApiKey
+        final Ehcache tenantCache = cacheManager.getEhcache(CacheType.TENANT.getCacheName());
+        tenantCache.remove(currentTenant.getApiKey());
+
+        // clear tenant-kv cache by tenantRecordId
+        final Ehcache tenantKvCache = cacheManager.getEhcache(CacheType.TENANT_KV.getCacheName());
+        removeCacheByKey(tenantKvCache, tenantRecordId.toString());
+
+        // clear tenant-config cache by tenantRecordId
+        final Ehcache tenantConfigCache = cacheManager.getEhcache(CacheType.TENANT_CONFIG.getCacheName());
+        tenantConfigCache.remove(tenantRecordId);
+
+        // clear tenant-overdue-config cache by tenantRecordId
+        final Ehcache tenantOverdueConfigCache = cacheManager.getEhcache(CacheType.TENANT_OVERDUE_CONFIG.getCacheName());
+        tenantOverdueConfigCache.remove(tenantRecordId);
+
+        // clear tenant-catalog cache by tenantRecordId
+        final Ehcache tenantCatalogCache = cacheManager.getEhcache(CacheType.TENANT_CATALOG.getCacheName());
+        tenantCatalogCache.remove(tenantRecordId);
+
+        return Response.status(Status.OK).build();
+    }
+
+    private void removeCacheByKey(final Ehcache tenantCache, final String tenantRecordId) {
+        for (Object key : tenantCache.getKeys()) {
+            if (null != key && key.toString().endsWith("::" + tenantRecordId)) {
+                tenantCache.remove(key);
+            }
+        }
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index d74eacd..97c3dad 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -17,6 +17,7 @@
 package org.killbill.billing.jaxrs.resources;
 
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -46,6 +47,7 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.EntitlementApi;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
@@ -73,12 +75,13 @@ import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -102,7 +105,7 @@ public class BundleResource extends JaxRsResourceBase {
                           final PaymentApi paymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, subscriptionApi, clock, context);
         this.entitlementApi = entitlementApi;
         this.subscriptionApi = subscriptionApi;
     }
@@ -131,14 +134,26 @@ public class BundleResource extends JaxRsResourceBase {
     @ApiOperation(value = "Retrieve a bundle by external key", response = BundleJson.class)
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Bundle not found")})
     public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
+                                   @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
 
         final TenantContext tenantContext = this.context.createContext(request);
 
-        final SubscriptionBundle bundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, tenantContext);
-        final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
-        final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
-        return Response.status(Status.OK).entity(json).build();
+        final List<SubscriptionBundle> bundles;
+        if (includedDeleted) {
+            bundles = subscriptionApi.getSubscriptionBundlesForExternalKey(externalKey, tenantContext);
+        } else {
+            final SubscriptionBundle activeBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, tenantContext);
+            bundles = ImmutableList.of(activeBundle);
+        }
+
+        final List<BundleJson> result = new ArrayList<BundleJson>(bundles.size());
+        for (final SubscriptionBundle bundle : bundles) {
+            final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
+            final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
+            result.add(json);
+        }
+        return Response.status(Status.OK).entity(result).build();
     }
 
     @TimedResource
@@ -225,12 +240,10 @@ public class BundleResource extends JaxRsResourceBase {
                                 @HeaderParam(HDR_REASON) final String reason,
                                 @HeaderParam(HDR_COMMENT) final String comment,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
-
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID bundleId = UUID.fromString(id);
-        final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
-        final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+        final LocalDate inputLocalDate = toLocalDate(requestedDate);
         entitlementApi.pause(bundleId, inputLocalDate, pluginProperties, callContext);
         return Response.status(Status.OK).build();
     }
@@ -250,12 +263,10 @@ public class BundleResource extends JaxRsResourceBase {
                                  @HeaderParam(HDR_REASON) final String reason,
                                  @HeaderParam(HDR_COMMENT) final String comment,
                                  @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
-
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID bundleId = UUID.fromString(id);
-        final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
-        final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+        final LocalDate inputLocalDate = toLocalDate(requestedDate);
         entitlementApi.resume(bundleId, inputLocalDate, pluginProperties, callContext);
         return Response.status(Status.OK).build();
     }
@@ -269,23 +280,13 @@ public class BundleResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Bundle not found")})
     public Response addBundleBlockingState(final BlockingStateJson json,
                                            @PathParam(ID_PARAM_NAME) final String id,
+                                           @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                            @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                            @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                            @HeaderParam(HDR_REASON) final String reason,
                                            @HeaderParam(HDR_COMMENT) final String comment,
                                            @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
-
-        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
-        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
-        final UUID bundleId = UUID.fromString(id);
-
-        final boolean isBlockBilling = (json.isBlockBilling() != null && json.isBlockBilling());
-        final boolean isBlockEntitlement = (json.isBlockEntitlement() != null && json.isBlockEntitlement());
-        final boolean isBlockChange = (json.isBlockChange() != null && json.isBlockChange());
-
-        entitlementApi.setBlockingState(bundleId, json.getStateName(), json.getService(), json.getEffectiveDate(), isBlockBilling, isBlockEntitlement, isBlockChange, pluginProperties, callContext);
-
-        return Response.status(Status.OK).build();
+        return addBlockingState(json, id, BlockingStateType.SUBSCRIPTION_BUNDLE, requestedDate, pluginPropertiesString, createdBy, reason, comment, request);
     }
 
 
@@ -346,7 +347,7 @@ public class BundleResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Bundle not found")})
     public Response getTags(@PathParam(ID_PARAM_NAME) final String bundleIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
         final UUID bundleId = UUID.fromString(bundleIdString);
         final TenantContext tenantContext = context.createContext(request);
@@ -382,7 +383,7 @@ public class BundleResource extends JaxRsResourceBase {
         final UUID bundleId = UUID.fromString(id);
 
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(bundleId, callContext);
-        final LocalDate inputLocalDate = toLocalDate(bundle.getAccountId(), requestedDate, callContext);
+        final LocalDate inputLocalDate = toLocalDate(requestedDate);
 
         final UUID newBundleId = entitlementApi.transferEntitlementsOverrideBillingPolicy(bundle.getAccountId(), UUID.fromString(json.getAccountId()), bundle.getExternalKey(), inputLocalDate, policy, pluginProperties, callContext);
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundleId, uriInfo.getBaseUri().toString());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index eddf1b9..22f7568 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -35,14 +35,19 @@ import javax.ws.rs.core.UriInfo;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.catalog.api.Listing;
+import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.catalog.api.user.DefaultSimplePlanDescriptor;
 import org.killbill.billing.jaxrs.json.CatalogJson;
 import org.killbill.billing.jaxrs.json.PlanDetailJson;
+import org.killbill.billing.jaxrs.json.SimplePlanJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
@@ -57,9 +62,9 @@ import org.killbill.xmlloader.XMLWriter;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.APPLICATION_XML;
@@ -81,7 +86,7 @@ public class CatalogResource extends JaxRsResourceBase {
                            final CatalogUserApi catalogUserApi,
                            final Clock clock,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.catalogUserApi = catalogUserApi;
     }
 
@@ -118,15 +123,24 @@ public class CatalogResource extends JaxRsResourceBase {
     @ApiResponses(value = {})
     public Response getCatalogJson(@QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
-        DateTime catalogDateVersion = clock.getUTCNow();
-        if (requestedDate != null) {
-            catalogDateVersion = DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC);
-        }
 
         final TenantContext tenantContext = context.createContext(request);
-        final Catalog catalog = catalogUserApi.getCatalog(catalogName, tenantContext);
-        final CatalogJson json = new CatalogJson(catalog, catalogDateVersion);
-        return Response.status(Status.OK).entity(json).build();
+        final DateTime catalogDateVersion = requestedDate != null ?
+                                      DATE_TIME_FORMATTER.parseDateTime(requestedDate).toDateTime(DateTimeZone.UTC) :
+                                      null;
+
+        // Yack...
+        final VersionedCatalog catalog = (VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext);
+
+        final List<CatalogJson> result = new ArrayList<CatalogJson>();
+        if (catalogDateVersion != null) {
+            result.add(new CatalogJson(catalog, catalogDateVersion));
+        } else {
+            for (final StandaloneCatalog v : catalog.getVersions()) {
+                result.add(new CatalogJson(catalog, new DateTime(v.getEffectiveDate())));
+            }
+        }
+        return Response.status(Status.OK).entity(result).build();
     }
 
     // Need to figure out dependency on StandaloneCatalog
@@ -179,4 +193,33 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
+
+    @TimedResource
+    @POST
+    @Path("/simplePlan")
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Upload the full catalog as XML")
+    @ApiResponses(value = {})
+    public Response addSimplePlan(final SimplePlanJson simplePlan,
+                                     @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 Exception {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+        final SimplePlanDescriptor desc = new DefaultSimplePlanDescriptor(simplePlan.getPlanId(),
+                                                                          simplePlan.getProductName(),
+                                                                          simplePlan.getProductCategory(),
+                                                                          simplePlan.getCurrency(),
+                                                                          simplePlan.getAmount(),
+                                                                          simplePlan.getBillingPeriod(),
+                                                                          simplePlan.getTrialLength(),
+                                                                          simplePlan.getTrialTimeUnit(),
+                                                                          simplePlan.getAvailableBaseProducts());
+        catalogUserApi.addSimplePlan(desc, clock.getUTCNow(), callContext);
+        return uriBuilder.buildResponse(uriInfo, CatalogResource.class, null, null);
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
index e308641..947e124 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ComboPaymentResource.java
@@ -59,7 +59,7 @@ public abstract class ComboPaymentResource extends JaxRsResourceBase {
                                 final PaymentApi paymentApi,
                                 final Clock clock,
                                 final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     protected Account getOrCreateAccount(final AccountJson accountJson, final CallContext callContext) throws AccountApiException {
@@ -121,9 +121,9 @@ public abstract class ComboPaymentResource extends JaxRsResourceBase {
         Preconditions.checkArgument(paymentIdStr != null || externalKey != null, "Need to set either paymentId or payment externalKey");
         if (paymentIdStr != null) {
             final UUID paymentId = UUID.fromString(paymentIdStr);
-            return paymentApi.getPayment(paymentId, false, pluginProperties, tenantContext);
+            return paymentApi.getPayment(paymentId, false, false, pluginProperties, tenantContext);
         } else {
-            return paymentApi.getPaymentByExternalKey(externalKey, false, pluginProperties, tenantContext);
+            return paymentApi.getPaymentByExternalKey(externalKey, false, false, pluginProperties, tenantContext);
         }
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
index bfad1b1..b6d005f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
@@ -20,12 +20,14 @@ import java.util.UUID;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
@@ -51,10 +53,10 @@ import org.killbill.clock.Clock;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -76,7 +78,7 @@ public class CreditResource extends JaxRsResourceBase {
                           final PaymentApi paymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.invoiceUserApi = invoiceUserApi;
         this.accountUserApi = accountUserApi;
     }
@@ -103,6 +105,7 @@ public class CreditResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied"),
                            @ApiResponse(code = 404, message = "Account not found")})
     public Response createCredit(final CreditJson json,
+                                 @QueryParam(QUERY_AUTO_COMMIT) @DefaultValue("false") final Boolean autoCommit,
                                  @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                  @HeaderParam(HDR_REASON) final String reason,
                                  @HeaderParam(HDR_COMMENT) final String comment,
@@ -121,11 +124,11 @@ public class CreditResource extends JaxRsResourceBase {
         if (json.getInvoiceId() != null) {
             // Apply an invoice level credit
             credit = invoiceUserApi.insertCreditForInvoice(account.getId(), UUID.fromString(json.getInvoiceId()), json.getCreditAmount(),
-                                                           effectiveDate, account.getCurrency(), callContext);
+                                                           effectiveDate, account.getCurrency(), json.getDescription(), callContext);
         } else {
             // Apply a account level credit
             credit = invoiceUserApi.insertCredit(account.getId(), json.getCreditAmount(), effectiveDate,
-                                                 account.getCurrency(), callContext);
+                                                 account.getCurrency(), autoCommit, json.getDescription(), callContext);
         }
 
         return uriBuilder.buildResponse(uriInfo, CreditResource.class, "getCredit", credit.getId());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
index 488850c..810035c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
@@ -49,9 +49,9 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -69,7 +69,7 @@ public class CustomFieldResource extends JaxRsResourceBase {
                                final PaymentApi paymentApi,
                                final Clock clock,
                                final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
index 64d7145..7714cbb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
@@ -43,10 +43,10 @@ import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
 
@@ -67,7 +67,7 @@ public class ExportResource extends JaxRsResourceBase {
                           final PaymentApi paymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.exportUserApi = exportUserApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index a7b2c9a..ff2ed5a 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -19,7 +19,9 @@
 package org.killbill.billing.jaxrs.resources;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -55,6 +57,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -70,13 +73,14 @@ import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -98,7 +102,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                   final InvoicePaymentApi invoicePaymentApi,
                                   final Clock clock,
                                   final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.invoicePaymentApi = invoicePaymentApi;
     }
 
@@ -111,6 +115,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Payment not found")})
     public Response getInvoicePayment(@PathParam("paymentId") final String paymentIdStr,
                                       @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                      @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                       @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                       @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
@@ -118,7 +123,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID paymentIdId = UUID.fromString(paymentIdStr);
         final TenantContext tenantContext = context.createContext(request);
-        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, pluginProperties, tenantContext);
+        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(payment.getAccountId(), auditMode.getLevel(), tenantContext);
 
         final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePayments(paymentIdId, tenantContext);
@@ -144,6 +149,8 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Account or payment not found")})
     public Response createRefundWithAdjustments(final InvoicePaymentTransactionJson json,
                                                 @PathParam("paymentId") final String paymentId,
+                                                @QueryParam(QUERY_PAYMENT_EXTERNAL) @DefaultValue("false") final Boolean externalPayment,
+                                                @QueryParam(QUERY_PAYMENT_METHOD_ID) @DefaultValue("") final String paymentMethodId,
                                                 @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                                 @HeaderParam(HDR_REASON) final String reason,
@@ -154,11 +161,12 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID paymentUuid = UUID.fromString(paymentId);
-        final Payment payment = paymentApi.getPayment(paymentUuid, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = paymentApi.getPayment(paymentUuid, false, false, ImmutableList.<PluginProperty>of(), callContext);
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
 
         final Iterable<PluginProperty> pluginProperties;
         final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
+        final String paymentExternalKey = json.getPaymentExternalKey() != null ? json.getPaymentExternalKey() : UUIDs.randomUUID().toString();
         if (json.isAdjusted() != null && json.isAdjusted()) {
             if (json.getAdjustments() != null && json.getAdjustments().size() > 0) {
                 final Map<UUID, BigDecimal> adjustments = new HashMap<UUID, BigDecimal>();
@@ -176,8 +184,22 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
             pluginProperties = extractPluginProperties(pluginPropertiesString);
         }
 
-        final Payment result = paymentApi.createRefundWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(), transactionExternalKey,
-                                                                               pluginProperties, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+        final Payment result;
+        if (externalPayment) {
+            UUID externalPaymentMethodId = Strings.isNullOrEmpty(paymentMethodId) ? null : UUID.fromString(paymentMethodId);
+
+            final Collection<PluginProperty> pluginPropertiesForExternalRefund = new LinkedList<PluginProperty>();
+            Iterables.addAll(pluginPropertiesForExternalRefund, pluginProperties);
+            pluginPropertiesForExternalRefund.add(new PluginProperty("IPCD_PAYMENT_ID", paymentUuid, false));
+
+            result = paymentApi.createCreditWithPaymentControl(account, externalPaymentMethodId, null, json.getAmount(), account.getCurrency(),
+                                                               paymentExternalKey, transactionExternalKey, pluginPropertiesForExternalRefund,
+                                                               createInvoicePaymentControlPluginApiPaymentOptions(true), callContext);
+        } else {
+            result = paymentApi.createRefundWithPaymentControl(account, payment.getId(), json.getAmount(), account.getCurrency(), transactionExternalKey,
+                                                               pluginProperties, createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+        }
+
         return uriBuilder.buildResponse(InvoicePaymentResource.class, "getInvoicePayment", result.getId(), uriInfo.getBaseUri().toString());
     }
 
@@ -201,7 +223,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
 
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final UUID paymentUuid = UUID.fromString(paymentId);
-        final Payment payment = paymentApi.getPayment(paymentUuid, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = paymentApi.getPayment(paymentUuid, false, false, ImmutableList.<PluginProperty>of(), callContext);
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
         final String transactionExternalKey = json.getTransactionExternalKey() != null ? json.getTransactionExternalKey() : UUIDs.randomUUID().toString();
 
@@ -211,6 +233,33 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
     }
 
     @TimedResource
+    @POST
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACK_REVERSALS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Record a chargebackReversal")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid payment id supplied"),
+                           @ApiResponse(code = 404, message = "Account or payment not found")})
+    public Response createChargebackReversal(final InvoicePaymentTransactionJson json,
+                                             @PathParam("paymentId") final String paymentId,
+                                             @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 UriInfo uriInfo,
+                                             @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        verifyNonNullOrEmpty(json, "InvoicePaymentTransactionJson body should be specified");
+        verifyNonNullOrEmpty(json.getTransactionExternalKey(), "transactionExternalKey amount needs to be set");
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID paymentUuid = UUID.fromString(paymentId);
+        final Payment payment = paymentApi.getPayment(paymentUuid, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
+
+        final Payment result = paymentApi.createChargebackReversalWithPaymentControl(account, payment.getId(), json.getTransactionExternalKey(), createInvoicePaymentControlPluginApiPaymentOptions(false), callContext);
+        return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
+    }
+
+    @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -267,12 +316,12 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
     public Response getTags(@PathParam(ID_PARAM_NAME) final String paymentIdString,
                             @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID paymentId = UUID.fromString(paymentIdString);
         final TenantContext tenantContext = context.createContext(request);
-        final Payment payment = paymentApi.getPayment(paymentId, false, pluginProperties, tenantContext);
+        final Payment payment = paymentApi.getPayment(paymentId, false, false, pluginProperties, tenantContext);
         return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 7642c3f..7a0e59c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -43,6 +43,7 @@ import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -51,8 +52,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
@@ -94,6 +93,7 @@ import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.LocaleUtils;
+import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -105,13 +105,13 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
-import org.killbill.clock.ClockUtil;
 import org.killbill.commons.metrics.TimedResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -119,10 +119,10 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 import com.google.inject.Inject;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.TEXT_HTML;
@@ -160,7 +160,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                            final AuditUserApi auditUserApi,
                            final TenantUserApi tenantApi,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.invoiceApi = invoiceApi;
         this.invoiceNotifier = invoiceNotifier;
         this.tenantApi = tenantApi;
@@ -176,16 +176,18 @@ public class InvoiceResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Invoice not found")})
     public Response getInvoice(@PathParam("invoiceId") final String invoiceId,
                                @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+                               @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
                                @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoice(UUID.fromString(invoiceId), tenantContext);
+        final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
         } else {
-            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -198,16 +200,18 @@ public class InvoiceResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Invoice not found")})
     public Response getInvoiceByNumber(@PathParam("invoiceNumber") final Integer invoiceNumber,
                                        @QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
+                                       @QueryParam(QUERY_INVOICE_WITH_CHILDREN_ITEMS) @DefaultValue("false") final boolean withChildrenItems,
                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                        @javax.ws.rs.core.Context final HttpServletRequest request) throws InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
         final Invoice invoice = invoiceApi.getInvoiceByNumber(invoiceNumber, tenantContext);
+        final List<InvoiceItem> childInvoiceItems = withChildrenItems ? invoiceApi.getInvoiceItemsByParentInvoice(invoice.getId(), tenantContext) : null;
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext);
 
         if (invoice == null) {
             throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceNumber);
         } else {
-            final InvoiceJson json = new InvoiceJson(invoice, withItems, accountAuditLogs);
+            final InvoiceJson json = new InvoiceJson(invoice, withItems, childInvoiceItems, accountAuditLogs);
             return Response.status(Status.OK).entity(json).build();
         }
     }
@@ -248,7 +252,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                         if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
                                                             accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
                                                         }
-                                                        return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+                                                        return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
                                                     }
                                                 },
                                                 nextPageUri
@@ -281,7 +285,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                         if (accountsAuditLogs.get().get(invoice.getAccountId()) == null) {
                                                             accountsAuditLogs.get().put(invoice.getAccountId(), auditUserApi.getAccountAuditLogs(invoice.getAccountId(), auditMode.getLevel(), tenantContext));
                                                         }
-                                                        return new InvoiceJson(invoice, withItems, accountsAuditLogs.get().get(invoice.getAccountId()));
+                                                        return new InvoiceJson(invoice, withItems, null, accountsAuditLogs.get().get(invoice.getAccountId()));
                                                     }
                                                 },
                                                 nextPageUri
@@ -302,7 +306,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         @javax.ws.rs.core.Context final HttpServletRequest request,
                                         @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
-        final LocalDate inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+        final LocalDate inputDate = toLocalDate(targetDate);
 
         try {
             final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, null,
@@ -318,6 +322,31 @@ public class InvoiceResource extends JaxRsResourceBase {
 
     @TimedResource
     @POST
+    @Path("/" + MIGRATION + "/{accountId:" + UUID_PATTERN + "}")
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Create a migration invoice", response = InvoiceJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id or target datetime supplied")})
+    public Response createMigrationInvoice(final Iterable<InvoiceItemJson> items,
+                                           @PathParam("accountId") final String accountId,
+                                           @Nullable @QueryParam(QUERY_TARGET_DATE) final String targetDate,
+                                           @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 AccountApiException, InvoiceApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+        final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
+        final Iterable<InvoiceItem> sanitizedInvoiceItems = validateSanitizeAndTranformInputItems(account.getCurrency(), items);
+        final LocalDate resolvedTargetDate =  toLocalDateDefaultToday(account, targetDate, callContext);
+        final UUID invoiceId = invoiceApi.createMigrationInvoice(UUID.fromString(accountId), resolvedTargetDate, sanitizedInvoiceItems, callContext);
+        return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", invoiceId);
+    }
+
+
+    @TimedResource
+    @POST
     @Path("/" + DRY_RUN)
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -339,10 +368,10 @@ public class InvoiceResource extends JaxRsResourceBase {
             } else if (DryRunType.SUBSCRIPTION_ACTION.name().equals(dryRunSubscriptionSpec.getDryRunType()) && dryRunSubscriptionSpec.getEffectiveDate() != null) {
                 inputDate = dryRunSubscriptionSpec.getEffectiveDate();
             } else {
-                inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+                inputDate = toLocalDate(targetDate);
             }
         } else {
-            inputDate = toLocalDate(UUID.fromString(accountId), targetDate, callContext);
+            inputDate = toLocalDate(targetDate);
         }
 
         // Passing a null or empty body means we are trying to generate an invoice with a (future) targetDate
@@ -366,11 +395,11 @@ public class InvoiceResource extends JaxRsResourceBase {
 
         final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
 
-        final DryRunArguments dryRunArguments = new DefaultDryRunArguments(dryRunSubscriptionSpec, account.getTimeZone(), account.getCurrency(), clock);
+        final DryRunArguments dryRunArguments = new DefaultDryRunArguments(dryRunSubscriptionSpec, account);
         try {
             final Invoice generatedInvoice = invoiceApi.triggerInvoiceGeneration(UUID.fromString(accountId), inputDate, dryRunArguments,
                                                                                  callContext);
-            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null)).build();
+            return Response.status(Status.OK).entity(new InvoiceJson(generatedInvoice, true, null, null)).build();
         } catch (InvoiceApiException e) {
             if (e.getCode() == ErrorCode.INVOICE_NOTHING_TO_DO.getCode()) {
                 return Response.status(Status.NOT_FOUND).build();
@@ -426,13 +455,14 @@ public class InvoiceResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
         final UUID accountId = UUID.fromString(json.getAccountId());
-        final LocalDate requestedDate = toLocalDate(accountId, requestedDateTimeString, callContext);
+        final LocalDate requestedDate = toLocalDateDefaultToday(accountId, requestedDateTimeString, callContext);
         final InvoiceItem adjustmentItem;
         if (json.getAmount() == null) {
             adjustmentItem = invoiceApi.insertInvoiceItemAdjustment(accountId,
                                                                     UUID.fromString(invoiceId),
                                                                     UUID.fromString(json.getInvoiceItemId()),
                                                                     requestedDate,
+                                                                    json.getDescription(),
                                                                     callContext);
         } else {
             adjustmentItem = invoiceApi.insertInvoiceItemAdjustment(accountId,
@@ -440,7 +470,8 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                                     UUID.fromString(json.getInvoiceItemId()),
                                                                     requestedDate,
                                                                     json.getAmount(),
-                                                                    json.getCurrency(),
+                                                                    Currency.valueOf(json.getCurrency()),
+                                                                    json.getDescription(),
                                                                     callContext);
         }
 
@@ -460,6 +491,9 @@ public class InvoiceResource extends JaxRsResourceBase {
                                           @QueryParam(QUERY_REQUESTED_DT) final String requestedDateTimeString,
                                           @QueryParam(QUERY_PAY_INVOICE) @DefaultValue("false") final Boolean payInvoice,
                                           @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                          @QueryParam(QUERY_AUTO_COMMIT) @DefaultValue("false") final Boolean autoCommit,
+                                          @QueryParam(QUERY_PAYMENT_EXTERNAL_KEY) final String paymentExternalKey,
+                                          @QueryParam(QUERY_TRANSACTION_EXTERNAL_KEY) final String transactionExternalKey,
                                           @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                           @HeaderParam(HDR_REASON) final String reason,
                                           @HeaderParam(HDR_COMMENT) final String comment,
@@ -469,20 +503,19 @@ public class InvoiceResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
         final Account account = accountUserApi.getAccountById(UUID.fromString(accountId), callContext);
-        final Iterable<InvoiceItemJson> sanitizedExternalChargesJson = cloneRefundItemsWithValidCurrency(account.getCurrency(), externalChargesJson);
+        final Iterable<InvoiceItem> sanitizedExternalChargesJson = validateSanitizeAndTranformInputItems(account.getCurrency(), externalChargesJson);
 
         // Get the effective date of the external charge, in the account timezone
-        final LocalDate requestedDate = toLocalDate(account, requestedDateTimeString, callContext);
-
-        final Iterable<InvoiceItem> externalCharges = Iterables.<InvoiceItemJson, InvoiceItem>transform(sanitizedExternalChargesJson,
-                                                                                                        new Function<InvoiceItemJson, InvoiceItem>() {
-                                                                                                            @Override
-                                                                                                            public InvoiceItem apply(final InvoiceItemJson invoiceItemJson) {
-                                                                                                                return invoiceItemJson.toInvoiceItem();
-                                                                                                            }
-                                                                                                        }
-                                                                                                       );
-        final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, externalCharges, callContext);
+        final LocalDate requestedDate = toLocalDateDefaultToday(account, requestedDateTimeString, callContext);
+        final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, sanitizedExternalChargesJson, autoCommit, callContext);
+
+        // if all createdExternalCharges point to the same invoiceId, use the provided paymentExternalKey and / or transactionExternalKey
+        final boolean haveSameInvoiceId = Iterables.all(createdExternalCharges, new Predicate<InvoiceItem>() {
+            @Override
+            public boolean apply(final InvoiceItem input) {
+                return input.getInvoiceId().equals(createdExternalCharges.get(0).getInvoiceId());
+            }
+        });
 
         if (payInvoice) {
             final Collection<UUID> paidInvoices = new HashSet<UUID>();
@@ -490,7 +523,10 @@ public class InvoiceResource extends JaxRsResourceBase {
                 if (!paidInvoices.contains(externalCharge.getInvoiceId())) {
                     paidInvoices.add(externalCharge.getInvoiceId());
                     final Invoice invoice = invoiceApi.getInvoice(externalCharge.getInvoiceId(), callContext);
-                    createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), account.getPaymentMethodId(), false, pluginProperties, callContext);
+                    createPurchaseForInvoice(account, invoice.getId(), invoice.getBalance(), account.getPaymentMethodId(), false,
+                                             (haveSameInvoiceId && paymentExternalKey != null) ? paymentExternalKey : null,
+                                             (haveSameInvoiceId && transactionExternalKey != null) ? transactionExternalKey : null,
+                                             pluginProperties, callContext);
                 }
             }
         }
@@ -506,20 +542,22 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(createdExternalChargesJson).build();
     }
 
-    private Iterable<InvoiceItemJson> cloneRefundItemsWithValidCurrency(final Currency accountCurrency, final Iterable<InvoiceItemJson> inputItems) throws InvoiceApiException {
+    private Iterable<InvoiceItem> validateSanitizeAndTranformInputItems(final Currency accountCurrency, final Iterable<InvoiceItemJson> inputItems) throws InvoiceApiException {
         try {
-            return Iterables.transform(inputItems, new Function<InvoiceItemJson, InvoiceItemJson>() {
+            final Iterable<InvoiceItemJson> sanitized = Iterables.transform(inputItems, new Function<InvoiceItemJson, InvoiceItemJson>() {
                 @Override
                 public InvoiceItemJson apply(final InvoiceItemJson input) {
                     if (input.getCurrency() != null) {
-                        if (!input.getCurrency().equals(accountCurrency)) {
+                        if (!input.getCurrency().equals(accountCurrency.name())) {
                             throw new IllegalArgumentException(input.getCurrency().toString());
                         }
                         return input;
                     } else {
                         return new InvoiceItemJson(null,
                                                    input.getInvoiceId(),
-                                                   null, input.getAccountId(),
+                                                   null,
+                                                   input.getAccountId(),
+                                                   input.getChildAccountId(),
                                                    input.getBundleId(),
                                                    input.getSubscriptionId(),
                                                    input.getPlanName(),
@@ -530,11 +568,19 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                    input.getStartDate(),
                                                    input.getEndDate(),
                                                    input.getAmount(),
-                                                   accountCurrency,
+                                                   accountCurrency.name(),
+                                                   null,
                                                    null);
                     }
                 }
             });
+
+            return Iterables.transform(sanitized, new Function<InvoiceItemJson, InvoiceItem>() {
+                @Override
+                public InvoiceItem apply(final InvoiceItemJson input) {
+                    return input.toInvoiceItem();
+                }
+            });
         } catch (IllegalArgumentException e) {
             throw new InvoiceApiException(ErrorCode.CURRENCY_INVALID, accountCurrency, e.getMessage());
         }
@@ -550,6 +596,7 @@ public class InvoiceResource extends JaxRsResourceBase {
     public Response getPayments(@PathParam("invoiceId") final String invoiceId,
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                 @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, InvoiceApiException {
 
         final TenantContext tenantContext = context.createContext(request);
@@ -568,7 +615,7 @@ public class InvoiceResource extends JaxRsResourceBase {
 
         final List<Payment> payments = new ArrayList<Payment>();
         for (final UUID paymentId : invoicePaymentIds) {
-            final Payment payment = paymentApi.getPayment(paymentId, withPluginInfo, ImmutableList.<PluginProperty>of(), tenantContext);
+            final Payment payment = paymentApi.getPayment(paymentId, withPluginInfo, withAttempts, ImmutableList.<PluginProperty>of(), tenantContext);
             payments.add(payment);
         }
 
@@ -601,7 +648,8 @@ public class InvoiceResource extends JaxRsResourceBase {
         verifyNonNullOrEmpty(payment.getAccountId(), "InvoicePaymentJson accountId needs to be set",
                              payment.getTargetInvoiceId(), "InvoicePaymentJson targetInvoiceId needs to be set",
                              payment.getPurchasedAmount(), "InvoicePaymentJson purchasedAmount needs to be set");
-        Preconditions.checkArgument(!externalPayment || payment.getPaymentMethodId() == null, "InvoicePaymentJson should not contain a paymwentMethodId when this is an external payment");
+        Preconditions.checkArgument(!externalPayment || payment.getPaymentMethodId() == null, "InvoicePaymentJson should not contain a paymentMethodId when this is an external payment");
+
 
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
@@ -612,7 +660,8 @@ public class InvoiceResource extends JaxRsResourceBase {
 
         final UUID invoiceId = UUID.fromString(payment.getTargetInvoiceId());
 
-        final Payment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment, pluginProperties, callContext);
+        final Payment result = createPurchaseForInvoice(account, invoiceId, payment.getPurchasedAmount(), paymentMethodId, externalPayment,
+                                                        (payment.getPaymentExternalKey() != null) ? payment.getPaymentExternalKey() : null, null, pluginProperties, callContext);
         return result != null ?
                uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId()) :
                Response.status(Status.NO_CONTENT).build();
@@ -899,7 +948,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Invoice not found")})
     public Response getTags(@PathParam(ID_PARAM_NAME) final String invoiceIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, InvoiceApiException {
         final UUID invoiceId = UUID.fromString(invoiceIdString);
         final TenantContext tenantContext = context.createContext(request);
@@ -942,6 +991,26 @@ public class InvoiceResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
+    @TimedResource
+    @PUT
+    @Path("/{invoiceId:" + UUID_PATTERN + "}/" + COMMIT_INVOICE)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Perform the invoice status transition from DRAFT to COMMITTED")
+    @ApiResponses(value = {@ApiResponse(code = 404, message = "Invoice not found")})
+    public Response commitInvoice(@PathParam("invoiceId") final String invoiceIdString,
+                                  @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 InvoiceApiException {
+
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID invoiceId = UUID.fromString(invoiceIdString);
+        invoiceApi.commitInvoice(invoiceId, callContext);
+        return Response.status(Response.Status.OK).build();
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.INVOICE;
@@ -952,13 +1021,13 @@ public class InvoiceResource extends JaxRsResourceBase {
         private final DryRunType dryRunType;
         private final SubscriptionEventType action;
         private final UUID subscriptionId;
-        private final DateTime effectiveDate;
+        private final LocalDate effectiveDate;
         private final PlanPhaseSpecifier specifier;
         private final UUID bundleId;
         private final BillingActionPolicy billingPolicy;
         private final List<PlanPhasePriceOverride> overrides;
 
-        public DefaultDryRunArguments(final InvoiceDryRunJson input, final DateTimeZone accountTimeZone, final Currency currency, final Clock clock) {
+        public DefaultDryRunArguments(final InvoiceDryRunJson input, final Account account) {
             if (input == null) {
                 this.dryRunType = DryRunType.TARGET_DATE;
                 this.action = null;
@@ -973,13 +1042,12 @@ public class InvoiceResource extends JaxRsResourceBase {
                 this.action = input.getDryRunAction() != null ? SubscriptionEventType.valueOf(input.getDryRunAction()) : null;
                 this.subscriptionId = input.getSubscriptionId() != null ? UUID.fromString(input.getSubscriptionId()) : null;
                 this.bundleId = input.getBundleId() != null ? UUID.fromString(input.getBundleId()) : null;
-                this.effectiveDate = input.getEffectiveDate() != null ? ClockUtil.computeDateTimeWithUTCReferenceTime(input.getEffectiveDate(), clock.getUTCNow().toLocalTime(), accountTimeZone, clock) : null;
+                this.effectiveDate = input.getEffectiveDate();
                 this.billingPolicy = input.getBillingPolicy() != null ? BillingActionPolicy.valueOf(input.getBillingPolicy()) : null;
                 final PlanPhaseSpecifier planPhaseSpecifier = (input.getProductName() != null &&
                                                                input.getProductCategory() != null &&
                                                                input.getBillingPeriod() != null) ?
                                                               new PlanPhaseSpecifier(input.getProductName(),
-                                                                                     ProductCategory.valueOf(input.getProductCategory()),
                                                                                      BillingPeriod.valueOf(input.getBillingPeriod()),
                                                                                      input.getPriceListName(),
                                                                                      input.getPhaseType() != null ? PhaseType.valueOf(input.getPhaseType()) : null) :
@@ -991,9 +1059,10 @@ public class InvoiceResource extends JaxRsResourceBase {
                                      @Override
                                      public PlanPhasePriceOverride apply(@Nullable final PhasePriceOverrideJson input) {
                                          if (input.getPhaseName() != null) {
-                                             return new DefaultPlanPhasePriceOverride(input.getPhaseName(), currency, input.getFixedPrice(), input.getRecurringPrice(), null);
+
+                                             return new DefaultPlanPhasePriceOverride(input.getPhaseName(), account.getCurrency(), input.getFixedPrice(), input.getRecurringPrice(), null);
                                          } else {
-                                             return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, currency, input.getFixedPrice(), input.getRecurringPrice(), null);
+                                             return new DefaultPlanPhasePriceOverride(planPhaseSpecifier, account.getCurrency(), input.getFixedPrice(), input.getRecurringPrice(), null);
                                          }
                                      }
                                  })) : ImmutableList.<PlanPhasePriceOverride>of();
@@ -1021,7 +1090,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
 
         @Override
-        public DateTime getEffectiveDate() {
+        public LocalDate getEffectiveDate() {
             return effectiveDate;
         }
 
@@ -1039,6 +1108,20 @@ public class InvoiceResource extends JaxRsResourceBase {
         public List<PlanPhasePriceOverride> getPlanPhasePriceOverrides() {
             return overrides;
         }
-    }
 
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("DefaultDryRunArguments{");
+            sb.append("dryRunType=").append(dryRunType);
+            sb.append(", action=").append(action);
+            sb.append(", subscriptionId=").append(subscriptionId);
+            sb.append(", effectiveDate=").append(effectiveDate);
+            sb.append(", specifier=").append(specifier);
+            sb.append(", bundleId=").append(bundleId);
+            sb.append(", billingPolicy=").append(billingPolicy);
+            sb.append(", overrides=").append(overrides);
+            sb.append('}');
+            return sb.toString();
+        }
+    }
 }
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 118c54a..071fbd0 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
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -29,10 +29,13 @@ 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 UPLOAD_PER_TENANT_CONFIG = "uploadPerTenantConfig";
     public static final String UPLOAD_PLUGIN_PAYMENT_STATE_MACHINE_CONFIG = "uploadPluginPaymentStateMachineConfig";
     public static final String USER_KEY_VALUE = "userKeyValue";
     public static final String SEARCH = "search";
 
+    public static final String PLUGIN_CONFIG = "pluginConfig";
+
     /*
      * Multi-Tenancy headers
      */
@@ -68,22 +71,34 @@ public interface JaxrsResource {
     public static final String QUERY_EXTERNAL_KEY = "externalKey";
     public static final String QUERY_API_KEY = "apiKey";
     public static final String QUERY_REQUESTED_DT = "requestedDate";
+    public static final String QUERY_PAYMENT_EXTERNAL_KEY = "paymentExternalKey";
+    public static final String QUERY_TRANSACTION_EXTERNAL_KEY = "transactionExternalKey";
+    public static final String QUERY_ENTITLEMENT_REQUESTED_DT = "entitlementDate";
+    public static final String QUERY_BILLING_REQUESTED_DT = "billingDate";
     public static final String QUERY_CALL_COMPLETION = "callCompletion";
     public static final String QUERY_USE_REQUESTED_DATE_FOR_BILLING = "useRequestedDateForBilling";
     public static final String QUERY_CALL_TIMEOUT = "callTimeoutSec";
     public static final String QUERY_TARGET_DATE = "targetDate";
     public static final String QUERY_BILLING_POLICY = "billingPolicy";
+    public static final String QUERY_MIGRATED = "migrated";
     public static final String QUERY_ENTITLEMENT_POLICY = "entitlementPolicy";
     public static final String QUERY_SEARCH_OFFSET = "offset";
     public static final String QUERY_SEARCH_LIMIT = "limit";
+    public static final String QUERY_ENTITLEMENT_EFFECTIVE_FROM_DT = "effectiveFromDate";
 
     public static final String QUERY_ACCOUNT_WITH_BALANCE = "accountWithBalance";
     public static final String QUERY_ACCOUNT_WITH_BALANCE_AND_CBA = "accountWithBalanceAndCBA";
 
     public static final String QUERY_ACCOUNT_ID = "accountId";
 
+    public static final String QUERY_BLOCKING_STATE_TYPES = "blockingStateTypes";
+    public static final String QUERY_BLOCKING_STATE_SVCS = "blockingStateSvcs";
+
+
     public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
+    public static final String QUERY_WITH_MIGRATION_INVOICES = "withMigrationInvoices";
     public static final String QUERY_UNPAID_INVOICES_ONLY = "unpaidInvoicesOnly";
+    public static final String QUERY_INVOICE_WITH_CHILDREN_ITEMS = "withChildrenItems";
 
     public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
     public static final String QUERY_PAYMENT_AMOUNT = "paymentAmount";
@@ -92,15 +107,17 @@ public interface JaxrsResource {
     public static final String QUERY_PAYMENT_METHOD_ID = "paymentMethodId";
     public static final String QUERY_PAYMENT_CONTROL_PLUGIN_NAME = "controlPluginName";
 
+    public static final String QUERY_TENANT_USE_GLOBAL_DEFAULT = "useGlobalDefault";
+    public static final String QUERY_TAGS_INCLUDED_DELETED = "includedDeleted";
 
     public static final String QUERY_TAGS = "tagList";
-    public static final String QUERY_TAGS_INCLUDED_DELETED = "includedDeleted";
     public static final String QUERY_CUSTOM_FIELDS = "customFieldList";
 
     public static final String QUERY_OBJECT_TYPE = "objectType";
 
     public static final String QUERY_PAYMENT_METHOD_PLUGIN_NAME = "pluginName";
     public static final String QUERY_WITH_PLUGIN_INFO = "withPluginInfo";
+    public static final String QUERY_WITH_ATTEMPTS = "withAttempts";
     public static final String QUERY_PAYMENT_METHOD_IS_DEFAULT = "isDefault";
 
     public static final String QUERY_PAY_ALL_UNPAID_INVOICES = "payAllUnpaidInvoices";
@@ -118,10 +135,16 @@ public interface JaxrsResource {
 
     public static final String QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF = "deleteDefaultPmWithAutoPayOff";
 
+    public static final String QUERY_FORCE_DEFAULT_PM_DELETION = "forceDefaultPmDeletion";
+
     public static final String QUERY_AUDIT = "audit";
 
+    public static final String QUERY_BCD = "bcd";
+
     public static final String QUERY_PARALLEL = "parallel";
 
+    public static final String QUERY_AUTO_COMMIT = "autoCommit";
+
     public static final String QUERY_NOTIFICATION_CALLBACK = "cb";
 
     public static final String PAGINATION = "pagination";
@@ -181,6 +204,9 @@ public interface JaxrsResource {
     public static final String CHARGEBACKS = "chargebacks";
     public static final String CHARGEBACKS_PATH = PREFIX + "/" + CHARGEBACKS;
 
+    public static final String CHARGEBACK_REVERSALS = "chargebackReversals";
+    public static final String CHARGEBACK_REVERSALS_PATH = PREFIX + "/" + CHARGEBACK_REVERSALS;
+
     public static final String ALL_TAGS = "allTags";
     public static final String TAGS = "tags";
     public static final String TAGS_PATH = PREFIX + "/" + TAGS;
@@ -232,12 +258,25 @@ public interface JaxrsResource {
     public static final String FORM = "form";
     public static final String NOTIFICATION = "notification";
 
+    public static final String CANCEL_SCHEDULED_PAYMENT_TRANSACTION = "cancelScheduledPaymentTransaction";
+
 
     public static final String INVOICE_TEMPLATE = "template";
     public static final String INVOICE_MP_TEMPLATE = "manualPayTemplate";
     public static final String INVOICE_TRANSLATION = "translation";
     public static final String INVOICE_CATALOG_TRANSLATION = "catalogTranslation";
+    public static final String COMMIT_INVOICE = "commitInvoice";
 
     public static final String COMBO = "combo";
+    public static final String MIGRATION = "migration";
+
+    public static final String CHILDREN = "children";
+    public static final String BCD = "bcd";
+    public static final String TRANSFER_CREDIT = "transferCredit";
+
+    public static final String CACHE = "cache";
+
+    public static final String QUERY_INCLUDED_DELETED = "includedDeleted";
+
 
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 30eecb0..ce30ab9 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -34,6 +34,7 @@ import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -42,8 +43,6 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.core.UriInfo;
 
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
@@ -53,16 +52,23 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.EntitlementApiException;
+import org.killbill.billing.entitlement.api.SubscriptionApi;
+import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.jaxrs.json.BillingExceptionJson;
 import org.killbill.billing.jaxrs.json.BillingExceptionJson.StackTraceElementJson;
+import org.killbill.billing.jaxrs.json.BlockingStateJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.JsonBase;
 import org.killbill.billing.jaxrs.json.PluginPropertyJson;
 import org.killbill.billing.jaxrs.json.TagJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.junction.DefaultBlockingState;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
@@ -98,6 +104,7 @@ import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
@@ -122,6 +129,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     protected final AuditUserApi auditUserApi;
     protected final AccountUserApi accountUserApi;
     protected final PaymentApi paymentApi;
+    protected final SubscriptionApi subscriptionApi;
     protected final Context context;
     protected final Clock clock;
 
@@ -134,6 +142,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
                              final AuditUserApi auditUserApi,
                              final AccountUserApi accountUserApi,
                              final PaymentApi paymentApi,
+                             final SubscriptionApi subscriptionApi,
                              final Clock clock,
                              final Context context) {
         this.uriBuilder = uriBuilder;
@@ -142,6 +151,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         this.auditUserApi = auditUserApi;
         this.accountUserApi = accountUserApi;
         this.paymentApi = paymentApi;
+        this.subscriptionApi = subscriptionApi;
         this.clock = clock;
         this.context = context;
     }
@@ -150,6 +160,33 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return null;
     }
 
+    public Response addBlockingState(final BlockingStateJson json,
+                                     final String id,
+                                     final BlockingStateType type,
+                                     final String requestedDate,
+                                     final List<String> pluginPropertiesString,
+                                     final String createdBy,
+                                     final String reason,
+                                     final String comment,
+                                     final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
+
+        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID blockableId = UUID.fromString(id);
+
+        final boolean isBlockBilling = (json.isBlockBilling() != null && json.isBlockBilling());
+        final boolean isBlockEntitlement = (json.isBlockEntitlement() != null && json.isBlockEntitlement());
+        final boolean isBlockChange = (json.isBlockChange() != null && json.isBlockChange());
+
+        final LocalDate resolvedRequestedDate = toLocalDate(requestedDate);
+        final BlockingState input = new DefaultBlockingState(blockableId, type, json.getStateName(), json.getService(), isBlockChange, isBlockEntitlement, isBlockBilling, null);
+        subscriptionApi.addBlockingState(input, resolvedRequestedDate, pluginProperties, callContext);
+        return Response.status(Status.OK).build();
+    }
+
+
+
+
     protected Response getTags(final UUID accountId, final UUID taggedObjectId, final AuditMode auditMode, final boolean includeDeleted, final TenantContext context) throws TagDefinitionApiException {
         final List<Tag> tags = tagUserApi.getTagsForObject(taggedObjectId, getObjectType(), includeDeleted, context);
         return createTagResponse(accountId, tags, auditMode, context);
@@ -302,19 +339,20 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
     }
 
     protected void validatePaymentMethodForAccount(final UUID accountId, final UUID paymentMethodId, final CallContext callContext) throws PaymentApiException {
-        verifyNonNull(paymentMethodId, "paymentMethodId should be specified");
-
+        if (paymentMethodId == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, accountId);
+        }
         final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(paymentMethodId, false, false, ImmutableList.<PluginProperty>of(), callContext);
         if (!paymentMethod.getAccountId().equals(accountId)) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
         }
     }
 
-    protected PaymentTransaction lookupPendingTransaction(final Payment initialPayment, @Nullable final String transactionId, @Nullable final String transactionExternalKey, @Nullable final String transactionType) throws PaymentApiException {
+    protected PaymentTransaction lookupPendingOrSuccessTransaction(final Payment initialPayment, @Nullable final String transactionId, @Nullable final String transactionExternalKey, @Nullable final String transactionType) throws PaymentApiException {
         final Collection<PaymentTransaction> pendingTransaction  =  Collections2.filter(initialPayment.getTransactions(), new Predicate<PaymentTransaction>() {
             @Override
             public boolean apply(final PaymentTransaction input) {
-                if (input.getTransactionStatus() != TransactionStatus.PENDING) {
+                if (input.getTransactionStatus() != TransactionStatus.PENDING && input.getTransactionStatus() != TransactionStatus.SUCCESS) {
                     return false;
                 }
                 if (transactionId != null && !transactionId.equals(input.getId().toString())) {
@@ -360,51 +398,19 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         }
     }
 
-    protected LocalDate toLocalDate(final UUID accountId, final String inputDate, final TenantContext context) throws AccountApiException {
-        final LocalDate maybeResult = extractLocalDate(inputDate);
-        if (maybeResult != null) {
-            return maybeResult;
-        }
-        Account account = accountId != null ? accountUserApi.getAccountById(accountId, context) : null;
-        final DateTime inputDateTime = inputDate != null ? DATE_TIME_FORMATTER.parseDateTime(inputDate) : clock.getUTCNow();
-        return toLocalDate(account, inputDateTime, context);
+    protected LocalDate toLocalDateDefaultToday(final UUID accountId, @Nullable final String inputDate, final TenantContext context) throws AccountApiException {
+        final Account account = accountId != null ? accountUserApi.getAccountById(accountId, context) : null;
+        return toLocalDateDefaultToday(account, inputDate, context);
     }
 
-    protected LocalDate toLocalDate(final Account account, final String inputDate, final TenantContext context) {
-
-        final LocalDate maybeResult = extractLocalDate(inputDate);
-        if (maybeResult != null) {
-            return maybeResult;
-        }
-        final DateTime inputDateTime = inputDate != null ? DATE_TIME_FORMATTER.parseDateTime(inputDate) : clock.getUTCNow();
-        return toLocalDate(account, inputDateTime, context);
-    }
-
-    private LocalDate toLocalDate(final Account account, final DateTime inputDate, final TenantContext context) {
-        if (account == null && inputDate == null) {
-            // We have no inputDate and so accountTimeZone so we default to LocalDate as seen in UTC
-            return new LocalDate(clock.getUTCNow(), DateTimeZone.UTC);
-        } else if (account == null && inputDate != null) {
-            // We were given a date but can't get timezone, default in UTC
-            return new LocalDate(inputDate, DateTimeZone.UTC);
-        } else if (account != null && inputDate == null) {
-            // We have no inputDate but for accountTimeZone so default to LocalDate as seen in account timezone
-            return new LocalDate(clock.getUTCNow(), account.getTimeZone());
-        } else {
-            // Precise LocalDate as requested
-            return new LocalDate(inputDate, account.getTimeZone());
-        }
+    protected LocalDate toLocalDateDefaultToday(final Account account, @Nullable final String inputDate, final TenantContext context) {
+        // TODO Switch to cached normalized timezone when available
+        return MoreObjects.firstNonNull(toLocalDate(inputDate), clock.getToday(account.getTimeZone()));
     }
 
-    private LocalDate extractLocalDate(final String inputDate) {
-        if (inputDate != null) {
-            try {
-                final LocalDate localDate = LocalDate.parse(inputDate, LOCAL_DATE_FORMATTER);
-                return localDate;
-            } catch (final IllegalArgumentException expectedAndIgnore) {
-            }
-        }
-        return null;
+    // API for subscription and invoice generation: keep null, the lower layers will default to now()
+    protected LocalDate toLocalDate(@Nullable final String inputDate) {
+        return inputDate == null || inputDate.isEmpty() ? null : LocalDate.parse(inputDate, LOCAL_DATE_FORMATTER);
     }
 
     protected Iterable<PluginProperty> extractPluginProperties(@Nullable final Iterable<PluginPropertyJson> pluginProperties) {
@@ -448,7 +454,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return properties;
     }
 
-    protected Payment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
+    protected Payment createPurchaseForInvoice(final Account account, final UUID invoiceId, final BigDecimal amountToPay, final UUID paymentMethodId, final Boolean externalPayment, final String paymentExternalKey, final String transactionExternalKey, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
 
         final List<PluginProperty> properties = new ArrayList<PluginProperty>();
         final Iterator<PluginProperty> pluginPropertyIterator = pluginProperties.iterator();
@@ -456,8 +462,6 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
             properties.add(pluginPropertyIterator.next());
         }
 
-        final String paymentExternalKey = UUIDs.randomUUID().toString();
-        final String transactionExternalKey = UUIDs.randomUUID().toString();
         final PluginProperty invoiceProperty = new PluginProperty("IPCD_INVOICE_ID" /* InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID (contract with plugin)  */,
                                                                   invoiceId.toString(), false);
         properties.add(invoiceProperty);
@@ -544,8 +548,13 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         Preconditions.checkArgument(actual == expected, errorMessage);
     }
 
-    protected Response createPaymentResponse(final UriInfo uriInfo, final Payment payment, final TransactionType transactionType,
-                                             @Nullable final String transactionExternalKey) {
+    protected void logDeprecationParameterWarningIfNeeded(@Nullable final String deprecatedParam, final String... replacementParams) {
+        if (deprecatedParam != null) {
+            log.warn(String.format("Parameter %s is being deprecated: Instead use parameters %s", deprecatedParam, Joiner.on(",").join(replacementParams)));
+        }
+    }
+
+    protected Response createPaymentResponse(final UriInfo uriInfo, final Payment payment, final TransactionType transactionType, @Nullable final String transactionExternalKey) {
         final PaymentTransaction createdTransaction = findCreatedTransaction(payment, transactionType, transactionExternalKey);
         Preconditions.checkNotNull(createdTransaction, "No transaction of type '%s' found", transactionType);
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
index a699485..e1d38f7 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/NodesInfoResource.java
@@ -64,10 +64,10 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -87,7 +87,7 @@ public class NodesInfoResource extends JaxRsResourceBase {
                              final KillbillNodesApi killbillInfoApi,
                              final Clock clock,
                              final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.killbillInfoApi = killbillInfoApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
index bfe8b11..534d07e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
@@ -33,9 +33,11 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.OverdueJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.overdue.api.OverdueApi;
+import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -50,10 +52,11 @@ import org.killbill.xmlloader.XMLWriter;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponses;
 
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.APPLICATION_XML;
 
 @Singleton
@@ -73,14 +76,14 @@ public class OverdueResource extends JaxRsResourceBase {
                            final OverdueApi overdueApi,
                            final Clock clock,
                            final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.overdueApi = overdueApi;
     }
 
     @TimedResource
     @GET
     @Produces(APPLICATION_XML)
-    @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
+    @ApiOperation(value = "Retrieve the overdue config as XML", response = String.class, hidden = true)
     @ApiResponses(value = {})
     public Response getOverdueConfigXml(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
         final TenantContext tenantContext = context.createContext(request);
@@ -106,4 +109,38 @@ public class OverdueResource extends JaxRsResourceBase {
         overdueApi.uploadOverdueConfig(overdueXML, callContext);
         return uriBuilder.buildResponse(uriInfo, OverdueResource.class, null, null);
     }
+
+    @TimedResource
+    @GET
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve the overdue config as JSON" , response = OverdueJson.class)
+    @ApiResponses(value = {})
+    public Response getOverdueConfigJson(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+        final TenantContext tenantContext = context.createContext(request);
+        final OverdueConfig overdueConfig = overdueApi.getOverdueConfig(tenantContext);
+        final OverdueJson result = new OverdueJson(overdueConfig);
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+
+
+    @TimedResource
+    @POST
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Upload the full overdue config as JSON")
+    @ApiResponses(value = {})
+    public Response uploadOverdueConfigJson(final OverdueJson overdueJson,
+                                  @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 Exception {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+        final OverdueConfig overdueConfig = OverdueJson.toOverdueConfigWithValidation(overdueJson);
+        overdueApi.uploadOverdueConfig(overdueConfig, callContext);
+        return uriBuilder.buildResponse(uriInfo, OverdueResource.class, null, null);
+    }
+
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
index aeb11d5..f6fc1ee 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
@@ -57,10 +57,10 @@ import org.killbill.commons.metrics.TimedResource;
 
 import com.google.common.base.Strings;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.WILDCARD;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 731db34..a5b528b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -68,10 +68,10 @@ import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -89,7 +89,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                  final PaymentApi paymentApi,
                                  final Clock clock,
                                  final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     @TimedResource(name = "getPaymentMethod")
@@ -259,6 +259,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Account or payment method not found")})
     public Response deletePaymentMethod(@PathParam("paymentMethodId") final String paymentMethodId,
                                         @QueryParam(QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF) @DefaultValue("false") final Boolean deleteDefaultPaymentMethodWithAutoPayOff,
+                                        @QueryParam(QUERY_FORCE_DEFAULT_PM_DELETION) @DefaultValue("false") final Boolean forceDefaultPaymentMethodDeletion,
                                         @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                         @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                         @HeaderParam(HDR_REASON) final String reason,
@@ -270,7 +271,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), false, false, pluginProperties, callContext);
         final Account account = accountUserApi.getAccountById(paymentMethod.getAccountId(), callContext);
 
-        paymentApi.deletePaymentMethod(account, UUID.fromString(paymentMethodId), deleteDefaultPaymentMethodWithAutoPayOff, pluginProperties, callContext);
+        paymentApi.deletePaymentMethod(account, UUID.fromString(paymentMethodId), deleteDefaultPaymentMethodWithAutoPayOff, forceDefaultPaymentMethodDeletion, pluginProperties, callContext);
 
         return Response.status(Status.OK).build();
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 30e5b07..653adb8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -61,6 +61,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentOptions;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
@@ -80,10 +81,11 @@ import com.google.common.base.Function;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import com.google.common.collect.Iterables;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -114,13 +116,14 @@ public class PaymentResource extends ComboPaymentResource {
                            @ApiResponse(code = 404, message = "Payment not found")})
     public Response getPayment(@PathParam("paymentId") final String paymentIdStr,
                                @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                               @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final UUID paymentIdId = UUID.fromString(paymentIdStr);
         final TenantContext tenantContext = context.createContext(request);
-        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, pluginProperties, tenantContext);
+        final Payment payment = paymentApi.getPayment(paymentIdId, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(payment.getAccountId(), auditMode.getLevel(), tenantContext);
         final PaymentJson result = new PaymentJson(payment, accountAuditLogs);
         return Response.status(Response.Status.OK).entity(result).build();
@@ -129,9 +132,10 @@ public class PaymentResource extends ComboPaymentResource {
     @TimedResource(name = "getPayment")
     @GET
     @Produces(APPLICATION_JSON)
-    @ApiOperation(value = "Retrieve a payment by id", response = PaymentJson.class)
+    @ApiOperation(value = "Retrieve a payment by external key", response = PaymentJson.class)
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Payment not found")})
     public Response getPaymentByExternalKey(@QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                            @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                             @QueryParam(QUERY_EXTERNAL_KEY) final String paymentExternalKey,
                                             @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@@ -139,7 +143,7 @@ public class PaymentResource extends ComboPaymentResource {
         verifyNonNullOrEmpty(paymentExternalKey, "Payment externalKey needs to be specified");
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final TenantContext tenantContext = context.createContext(request);
-        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, pluginProperties, tenantContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(payment.getAccountId(), auditMode.getLevel(), tenantContext);
         final PaymentJson result = new PaymentJson(payment, accountAuditLogs);
         return Response.status(Response.Status.OK).entity(result).build();
@@ -157,15 +161,16 @@ public class PaymentResource extends ComboPaymentResource {
                                 @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                 @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                 @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final TenantContext tenantContext = context.createContext(request);
 
         final Pagination<Payment> payments;
         if (Strings.isNullOrEmpty(pluginName)) {
-            payments = paymentApi.getPayments(offset, limit, withPluginInfo, pluginProperties, tenantContext);
+            payments = paymentApi.getPayments(offset, limit, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         } else {
-            payments = paymentApi.getPayments(offset, limit, pluginName, withPluginInfo, pluginProperties, tenantContext);
+            payments = paymentApi.getPayments(offset, limit, pluginName, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         }
 
         final URI nextPageUri = uriBuilder.nextPage(PaymentResource.class, "getPayments", payments.getNextOffset(), limit, ImmutableMap.<String, String>of(QUERY_PAYMENT_METHOD_PLUGIN_NAME, Strings.nullToEmpty(pluginName),
@@ -201,6 +206,7 @@ public class PaymentResource extends ComboPaymentResource {
                                    @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                    @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                    @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo,
+                                   @QueryParam(QUERY_WITH_ATTEMPTS) @DefaultValue("false") final Boolean withAttempts,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final TenantContext tenantContext = context.createContext(request);
@@ -208,9 +214,9 @@ public class PaymentResource extends ComboPaymentResource {
         // Search the plugin(s)
         final Pagination<Payment> payments;
         if (Strings.isNullOrEmpty(pluginName)) {
-            payments = paymentApi.searchPayments(searchKey, offset, limit, withPluginInfo, pluginProperties, tenantContext);
+            payments = paymentApi.searchPayments(searchKey, offset, limit, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         } else {
-            payments = paymentApi.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, pluginProperties, tenantContext);
+            payments = paymentApi.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, withAttempts, pluginProperties, tenantContext);
         }
 
         final URI nextPageUri = uriBuilder.nextPage(PaymentResource.class, "searchPayments", payments.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey,
@@ -298,7 +304,12 @@ public class PaymentResource extends ComboPaymentResource {
                                                  final UriInfo uriInfo,
                                                  final HttpServletRequest request) throws PaymentApiException, AccountApiException {
 
-        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+        final Iterable<PluginProperty> pluginPropertiesFromBody = extractPluginProperties(json.getProperties());
+
+        final Iterable<PluginProperty> pluginPropertiesFromQuery = extractPluginProperties(pluginPropertiesString);
+
+        final Iterable<PluginProperty> pluginProperties = Iterables.concat(pluginPropertiesFromQuery, pluginPropertiesFromBody);
+
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
         final Payment initialPayment = getPaymentByIdOrKey(paymentIdStr, json == null ? null : json.getPaymentExternalKey(), pluginProperties, callContext);
 
@@ -306,11 +317,17 @@ public class PaymentResource extends ComboPaymentResource {
         final BigDecimal amount = json == null ? null : json.getAmount();
         final Currency currency = json == null || json.getCurrency() == null ? null : Currency.valueOf(json.getCurrency());
 
-            final PaymentTransaction pendingTransaction = lookupPendingTransaction(initialPayment,
-                                                                                   json != null ? json.getTransactionId() : null,
-                                                                                   json != null ? json.getTransactionExternalKey() : null,
-                                                                                   json != null ? json.getTransactionType() : null);
+        final PaymentTransaction pendingOrSuccessTransaction = lookupPendingOrSuccessTransaction(initialPayment,
+                                                                                                 json != null ? json.getTransactionId() : null,
+                                                                                                 json != null ? json.getTransactionExternalKey() : null,
+                                                                                                 json != null ? json.getTransactionType() : null);
+        // If transaction was already completed, return early (See #626)
+        if (pendingOrSuccessTransaction.getTransactionStatus() == TransactionStatus.SUCCESS) {
+            return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", pendingOrSuccessTransaction.getPaymentId());
+        }
+
 
+        final PaymentTransaction pendingTransaction = pendingOrSuccessTransaction;
         final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
         final Payment result;
         switch (pendingTransaction.getTransactionType()) {
@@ -646,6 +663,80 @@ public class PaymentResource extends ComboPaymentResource {
         return createPaymentResponse(uriInfo, payment, TransactionType.CHARGEBACK, json.getTransactionExternalKey());
     }
 
+    @TimedResource(name = "chargebackReversalPayment")
+    @POST
+    @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACK_REVERSALS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Record a chargeback reversal")
+    @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+                           @ApiResponse(code = 400, message = "Invalid paymentId supplied"),
+                           @ApiResponse(code = 404, message = "Account or payment not found"),
+                           @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+                           @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+                           @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+                           @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+                           @ApiResponse(code = 504, message = "Payment operation timeout")})
+    public Response chargebackReversalPayment(final PaymentTransactionJson json,
+                                              @PathParam("paymentId") final String paymentIdStr,
+                                              @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
+                                              @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                              @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 UriInfo uriInfo,
+                                              @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        return chargebackReversalPaymentInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+    }
+
+    @TimedResource(name = "chargebackReversalPayment")
+    @POST
+    @Path("/" + CHARGEBACK_REVERSALS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Record a chargeback reversal")
+    @ApiResponses(value = {@ApiResponse(code = 201, message = "Payment transaction created successfully"),
+                           @ApiResponse(code = 404, message = "Account or payment not found"),
+                           @ApiResponse(code = 402, message = "Transaction declined by gateway"),
+                           @ApiResponse(code = 422, message = "Payment is aborted by a control plugin"),
+                           @ApiResponse(code = 502, message = "Failed to submit payment transaction"),
+                           @ApiResponse(code = 503, message = "Payment in unknown status, failed to receive gateway response"),
+                           @ApiResponse(code = 504, message = "Payment operation timeout")})
+    public Response chargebackReversalPaymentByExternalKey(final PaymentTransactionJson json,
+                                                           @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
+                                                           @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                                           @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 UriInfo uriInfo,
+                                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        return chargebackReversalPaymentInternal(json, null, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
+    }
+
+    private Response chargebackReversalPaymentInternal(final PaymentTransactionJson json,
+                                                       @Nullable final String paymentIdStr,
+                                                       final List<String> paymentControlPluginNames,
+                                                       final List<String> pluginPropertiesString,
+                                                       final String createdBy,
+                                                       final String reason,
+                                                       final String comment,
+                                                       final UriInfo uriInfo,
+                                                       final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        verifyNonNullOrEmpty(json, "PaymentTransactionJson body should be specified");
+        verifyNonNullOrEmpty(json.getTransactionExternalKey(), "PaymentTransactionJson transactionExternalKey needs to be set");
+
+        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final Payment initialPayment = getPaymentByIdOrKey(paymentIdStr, json.getPaymentExternalKey(), pluginProperties, callContext);
+
+        final Account account = accountUserApi.getAccountById(initialPayment.getAccountId(), callContext);
+
+        final PaymentOptions paymentOptions = createControlPluginApiPaymentOptions(paymentControlPluginNames);
+
+        final Payment payment = paymentApi.createChargebackReversalWithPaymentControl(account, initialPayment.getId(), json.getTransactionExternalKey(), paymentOptions, callContext);
+        return createPaymentResponse(uriInfo, payment, TransactionType.CHARGEBACK, json.getTransactionExternalKey());
+    }
+
     @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
@@ -706,6 +797,45 @@ public class PaymentResource extends ComboPaymentResource {
         return createPaymentResponse(uriInfo, result, transactionType, paymentTransactionJson.getTransactionExternalKey());
     }
 
+    @TimedResource(name = "cancelScheduledPaymentTransaction")
+    @DELETE
+    @Path("/{paymentTransactionId:" + UUID_PATTERN + "}/" + CANCEL_SCHEDULED_PAYMENT_TRANSACTION)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Cancels a scheduled payment attempt retry")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentTransactionId supplied")})
+    public Response cancelScheduledPaymentTransactionById(@PathParam("paymentTransactionId") final String paymentTransactionId,
+                                                          @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 UriInfo uriInfo,
+                                                          @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        paymentApi.cancelScheduledPaymentTransaction(UUID.fromString(paymentTransactionId), callContext);
+        return Response.status(Status.OK).build();
+    }
+
+    @TimedResource(name = "cancelScheduledPaymentTransaction")
+    @DELETE
+    @Path("/" + CANCEL_SCHEDULED_PAYMENT_TRANSACTION)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Cancels a scheduled payment attempt retry")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentTransactionExternalKey supplied")})
+    public Response cancelScheduledPaymentTransactionByExternalKey(@QueryParam(QUERY_TRANSACTION_EXTERNAL_KEY) final String paymentTransactionExternalKey,
+                                                                   @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 UriInfo uriInfo,
+                                                                   @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        paymentApi.cancelScheduledPaymentTransaction(paymentTransactionExternalKey, callContext);
+        return Response.status(Status.OK).build();
+    }
+
+
+
+
     @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
@@ -766,7 +896,7 @@ public class PaymentResource extends ComboPaymentResource {
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
         final UUID paymentId = UUID.fromString(id);
-        final Payment payment = paymentApi.getPayment(paymentId, false, ImmutableList.<PluginProperty>of(), tenantContext);
+        final Payment payment = paymentApi.getPayment(paymentId, false, false, ImmutableList.<PluginProperty>of(), tenantContext);
         return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
index 8c6ce93..ffbd88c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
@@ -42,8 +42,8 @@ import org.killbill.commons.metrics.TimedResource;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -63,7 +63,7 @@ public class PluginInfoResource extends JaxRsResourceBase {
                               final PluginsInfoApi pluginsInfoApi,
                               final Clock clock,
                               final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.pluginsInfoApi = pluginsInfoApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index 6be2cdb..27bf057 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -70,7 +70,7 @@ import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.name.Named;
 import com.sun.jersey.api.representation.Form;
-import com.wordnik.swagger.annotations.Api;
+import io.swagger.annotations.Api;
 
 @Singleton
 @Path(JaxrsResource.PLUGINS_PATH + "{subResources:.*}")
@@ -93,7 +93,7 @@ public class PluginResource extends JaxRsResourceBase {
                           final PaymentApi paymentApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.osgiServlet = osgiServlet;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
index ac8a283..b6f7b27 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
@@ -57,9 +57,9 @@ import com.google.common.base.Functions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -80,7 +80,7 @@ public class SecurityResource extends JaxRsResourceBase {
                             final PaymentApi paymentApi,
                             final Clock clock,
                             final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.securityApi = securityApi;
     }
 
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 0efacbc..8a9a799 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -53,6 +53,7 @@ import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.Entitlement;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
 import org.killbill.billing.entitlement.api.EntitlementApi;
@@ -62,12 +63,15 @@ import org.killbill.billing.entitlement.api.Subscription;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
+import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.NullInvoiceInternalEvent;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
 import org.killbill.billing.events.PaymentInfoInternalEvent;
 import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
+import org.killbill.billing.jaxrs.json.BlockingStateJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PhasePriceOverrideJson;
 import org.killbill.billing.jaxrs.json.SubscriptionJson;
@@ -95,10 +99,10 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -125,7 +129,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                 final PaymentApi paymentApi,
                                 final Clock clock,
                                 final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, subscriptionApi, clock, context);
         this.killbillHandler = killbillHandler;
         this.entitlementApi = entitlementApi;
         this.subscriptionApi = subscriptionApi;
@@ -141,10 +145,9 @@ public class SubscriptionResource extends JaxRsResourceBase {
     public Response getEntitlement(@PathParam("subscriptionId") final String subscriptionId,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
         final UUID uuid = UUID.fromString(subscriptionId);
-        final TenantContext tenantContext = this.context.createContext(request);
-        final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(uuid, tenantContext);
-
-        final Account account = accountUserApi.getAccountById(subscription.getAccountId(), tenantContext);
+        final TenantContext context = this.context.createContext(request);
+        final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(uuid, context);
+        final Account account = accountUserApi.getAccountById(subscription.getAccountId(), context);
         final SubscriptionJson json = new SubscriptionJson(subscription, account.getCurrency(), null);
         return Response.status(Status.OK).entity(json).build();
     }
@@ -156,7 +159,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @ApiOperation(value = "Create an entitlement")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
     public Response createEntitlement(final SubscriptionJson entitlement,
-                                      @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                      @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
+                                      @QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate,
+                                      @QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate,
+                                      @QueryParam(QUERY_MIGRATED) @DefaultValue("false") final Boolean isMigrated,
+                                      @QueryParam(QUERY_BCD) final Integer newBCD,
                                       @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
                                       @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
                                       @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -166,10 +173,15 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                       @javax.ws.rs.core.Context final HttpServletRequest request,
                                       @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
         verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified");
-        verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set",
-                             entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set",
-                             entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
-                             entitlement.getPriceList(), "SubscriptionJson priceList needs to be set");
+        if (entitlement.getPlanName() == null) {
+            verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set",
+                                 entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set",
+                                 entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
+                                 entitlement.getPriceList(), "SubscriptionJson priceList needs to be set");
+        }
+
+        logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
+
         // For ADD_ON we can provide externalKey or the bundleId
         final boolean createAddOnEntitlement = ProductCategory.ADD_ON.toString().equals(entitlement.getProductCategory());
         if (createAddOnEntitlement) {
@@ -179,26 +191,27 @@ public class SubscriptionResource extends JaxRsResourceBase {
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
-
         final EntitlementCallCompletionCallback<Entitlement> callback = new EntitlementCallCompletionCallback<Entitlement>() {
             @Override
             public Entitlement doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
 
                 final Account account = getAccountFromSubscriptionJson(entitlement, callContext);
                 final PhaseType phaseType = entitlement.getPhaseType() != null ? PhaseType.valueOf(entitlement.getPhaseType()) : null;
-                final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(entitlement.getProductName(),
-                                                                       ProductCategory.valueOf(entitlement.getProductCategory()),
+                final PlanPhaseSpecifier spec = entitlement.getPlanName() != null ?
+                                                new PlanPhaseSpecifier(entitlement.getPlanName(), phaseType) :
+                                                new PlanPhaseSpecifier(entitlement.getProductName(),
                                                                        BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), phaseType);
 
-                final LocalDate inputLocalDate = requestedDate == null ? null : toLocalDate(account, requestedDate, callContext);
-                final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                 ProductCategory.valueOf(entitlement.getProductCategory()),
-                                                                 BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
-
-                final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
-                return createAddOnEntitlement ?
-                       entitlementApi.addEntitlement(getBundleIdForAddOnCreation(entitlement), spec, overrides, inputLocalDate, pluginProperties, callContext) :
-                       entitlementApi.createBaseEntitlement(account.getId(), spec, entitlement.getExternalKey(), overrides, inputLocalDate, pluginProperties, callContext);
+                final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
+                final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
+                final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), spec, account.getCurrency());
+                final Entitlement result = createAddOnEntitlement ?
+                                           entitlementApi.addEntitlement(getBundleIdForAddOnCreation(entitlement), spec, overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext) :
+                                           entitlementApi.createBaseEntitlement(account.getId(), spec, entitlement.getExternalKey(), overrides, resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext);
+                if (newBCD != null) {
+                    result.updateBCD(newBCD, null, callContext);
+                }
+                return result;
             }
 
             private UUID getBundleIdForAddOnCreation(final SubscriptionJson entitlement) throws SubscriptionApiException {
@@ -234,26 +247,33 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @ApiOperation(value = "Create an entitlement with addOn products")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
     public Response createEntitlementWithAddOns(final List<SubscriptionJson> entitlements,
-                                      @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
-                                      @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
-                                      @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
-                                      @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
-                                      @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 EntitlementApiException, AccountApiException, SubscriptionApiException {
+                                                @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
+                                                @QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate,
+                                                @QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate,
+                                                @QueryParam(QUERY_MIGRATED) @DefaultValue("false") final Boolean isMigrated,
+                                                @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion,
+                                                @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec,
+                                                @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                                @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 EntitlementApiException, AccountApiException, SubscriptionApiException {
 
         Preconditions.checkArgument(Iterables.size(entitlements) > 0, "Subscription list mustn't be null or empty.");
 
         for (SubscriptionJson entitlement : entitlements) {
             verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified for each element");
-            verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set for each element",
-                                 entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set for each element",
-                                 entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set for each element",
-                                 entitlement.getPriceList(), "SubscriptionJson priceList needs to be set for each element");
+            if (entitlement.getPlanName() == null) {
+                verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set for each element",
+                                     entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set for each element",
+                                     entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set for each element",
+                                     entitlement.getPriceList(), "SubscriptionJson priceList needs to be set for each element");
+            }
         }
 
+        logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
+
         final int baseSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {
             @Override
             public boolean apply(final SubscriptionJson subscription) {
@@ -272,11 +292,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
 
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final SubscriptionJson baseEntitlement = Iterables.tryFind(entitlements, new Predicate<SubscriptionJson>() {
-                    @Override
-                    public boolean apply(final SubscriptionJson subscription) {
-                        return ProductCategory.BASE.toString().equalsIgnoreCase(subscription.getProductCategory());
-                    }
-            }).orNull();
+            @Override
+            public boolean apply(final SubscriptionJson subscription) {
+                return ProductCategory.BASE.toString().equalsIgnoreCase(subscription.getProductCategory());
+            }
+        }).orNull();
 
         verifyNonNull(baseEntitlement.getAccountId(), "SubscriptionJson accountId needs to be set for BASE product.");
 
@@ -292,14 +312,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
 
                 for (final SubscriptionJson entitlement : entitlements) {
 
-                    final PlanPhaseSpecifier planPhaseSpecifier = new PlanPhaseSpecifier(entitlement.getProductName(),
-                                                                                         ProductCategory.valueOf(entitlement.getProductCategory()),
+                    final PlanPhaseSpecifier planPhaseSpecifier = entitlement.getPlanName() != null ?
+                                                                  new PlanPhaseSpecifier(entitlement.getPlanName(), null) :
+                                                                  new PlanPhaseSpecifier(entitlement.getProductName(),
                                                                                          BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), null);
-
-                    final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                    ProductCategory.valueOf(entitlement.getProductCategory()),
-                                                                    BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
-                    final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
+                    final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planPhaseSpecifier, account.getCurrency());
 
                     EntitlementSpecifier specifier = new EntitlementSpecifier() {
 
@@ -317,9 +334,10 @@ public class SubscriptionResource extends JaxRsResourceBase {
                     entitlementSpecifierList.add(specifier);
                 }
 
-                final LocalDate inputLocalDate = toLocalDate(account, requestedDate, callContext);
+                final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
+                final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
                 return entitlementApi.createBaseEntitlementWithAddOns(account.getId(), baseEntitlement.getExternalKey(), entitlementSpecifierList,
-                                                                      inputLocalDate, pluginProperties, callContext);
+                                                                      resolvedEntitlementDate, resolvedBillingDate, isMigrated, pluginProperties, callContext);
             }
 
             @Override
@@ -378,9 +396,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
                                           @HeaderParam(HDR_COMMENT) final String comment,
                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
         verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified");
-        verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set",
-                             entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
-                             entitlement.getPriceList(), "SubscriptionJson priceList needs to be set");
+        if (entitlement.getPlanName() == null) {
+            verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set",
+                                 entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set",
+                                 entitlement.getPriceList(), "SubscriptionJson priceList needs to be set");
+        }
 
         final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
@@ -395,22 +415,23 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final UUID uuid = UUID.fromString(subscriptionId);
 
                 final Entitlement current = entitlementApi.getEntitlementForId(uuid, callContext);
-                final LocalDate inputLocalDate = toLocalDate(current.getAccountId(), requestedDate, callContext);
+                final LocalDate inputLocalDate = toLocalDate(requestedDate);
                 final Entitlement newEntitlement;
 
                 final Account account = accountUserApi.getAccountById(current.getAccountId(), callContext);
-                final PlanSpecifier planSpec = new PlanSpecifier(entitlement.getProductName(),
-                                                                 ProductCategory.valueOf(entitlement.getProductCategory()),
+                final PlanSpecifier planSpec = entitlement.getPlanName() != null ?
+                                               new PlanSpecifier(entitlement.getPlanName()) :
+                                               new PlanSpecifier(entitlement.getProductName(),
                                                                  BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList());
                 final List<PlanPhasePriceOverride> overrides = PhasePriceOverrideJson.toPlanPhasePriceOverrides(entitlement.getPriceOverrides(), planSpec, account.getCurrency());
 
                 if (requestedDate == null && policyString == null) {
-                    newEntitlement = current.changePlan(entitlement.getProductName(), BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), overrides, pluginProperties, ctx);
+                    newEntitlement = current.changePlan(planSpec, overrides, pluginProperties, ctx);
                 } else if (policyString == null) {
-                    newEntitlement = current.changePlanWithDate(entitlement.getProductName(), BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), overrides, inputLocalDate, pluginProperties, ctx);
+                    newEntitlement = current.changePlanWithDate(planSpec, overrides, inputLocalDate, pluginProperties, ctx);
                 } else {
                     final BillingActionPolicy policy = BillingActionPolicy.valueOf(policyString.toUpperCase());
-                    newEntitlement = current.changePlanOverrideBillingPolicy(entitlement.getProductName(), BillingPeriod.valueOf(entitlement.getBillingPeriod()), entitlement.getPriceList(), overrides, inputLocalDate, policy, pluginProperties, ctx);
+                    newEntitlement = current.changePlanOverrideBillingPolicy(planSpec, overrides, inputLocalDate, policy, pluginProperties, ctx);
                 }
                 isImmediateOp = newEntitlement.getLastActiveProduct().getName().equals(entitlement.getProductName()) &&
                                 newEntitlement.getLastActivePlan().getRecurringBillingPeriod() == BillingPeriod.valueOf(entitlement.getBillingPeriod()) &&
@@ -437,6 +458,25 @@ public class SubscriptionResource extends JaxRsResourceBase {
     }
 
     @TimedResource
+    @PUT
+    @Path("/{subscriptionId:" + UUID_PATTERN + "}/" + BLOCK)
+    @Consumes(APPLICATION_JSON)
+    @ApiOperation(value = "Block a subscription")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid subscription id supplied"),
+                           @ApiResponse(code = 404, message = "Subscription not found")})
+    public Response addSubscriptionBlockingState(final BlockingStateJson json,
+                                                 @PathParam(ID_PARAM_NAME) final String id,
+                                                 @QueryParam(QUERY_REQUESTED_DT) final String requestedDate,
+                                                 @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                                 @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                                 @HeaderParam(HDR_REASON) final String reason,
+                                                 @HeaderParam(HDR_COMMENT) final String comment,
+                                                 @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
+
+        return addBlockingState(json, id, BlockingStateType.SUBSCRIPTION, requestedDate, pluginPropertiesString, createdBy, reason, comment, request);
+    }
+
+    @TimedResource
     @DELETE
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -470,7 +510,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 final UUID uuid = UUID.fromString(subscriptionId);
 
                 final Entitlement current = entitlementApi.getEntitlementForId(uuid, ctx);
-                final LocalDate inputLocalDate = toLocalDate(current.getAccountId(), requestedDate, callContext);
+                final LocalDate inputLocalDate = toLocalDate(requestedDate);
                 final Entitlement newEntitlement;
                 if (billingPolicyString == null && entitlementPolicyString == null) {
                     newEntitlement = current.cancelEntitlementWithDate(inputLocalDate, useRequestedDateForBilling, pluginProperties, ctx);
@@ -509,6 +549,34 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
+    @TimedResource
+    @PUT
+    @Produces(APPLICATION_JSON)
+    @Consumes(APPLICATION_JSON)
+    @Path("/{subscriptionId:" + UUID_PATTERN + "}/" + BCD)
+    @ApiOperation(value = "Update the BCD associated to a subscription")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid entitlement supplied")})
+    public Response updateSubscriptionBCD(final SubscriptionJson json,
+                                          @PathParam(ID_PARAM_NAME) final String id,
+                                          @QueryParam(QUERY_ENTITLEMENT_EFFECTIVE_FROM_DT) final String effectiveFromDateStr,
+                                          @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 UriInfo uriInfo,
+                                          @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException {
+
+        verifyNonNullOrEmpty(json, "SubscriptionJson body should be specified");
+        verifyNonNullOrEmpty(json.getBillCycleDayLocal(), "SubscriptionJson new BCD should be specified");
+
+        final LocalDate effectiveFromDate = toLocalDate(effectiveFromDateStr);
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID subscriptionId = UUID.fromString(id);
+
+        final Entitlement entitlement = entitlementApi.getEntitlementForId(subscriptionId, callContext);
+        entitlement.updateBCD(json.getBillCycleDayLocal(), effectiveFromDate, callContext);
+        return Response.status(Status.OK).build();
+    }
+
     private static final class CompletionUserRequestEntitlement extends CompletionUserRequestBase {
 
         public CompletionUserRequestEntitlement(final UUID userToken) {
@@ -522,6 +590,11 @@ public class SubscriptionResource extends JaxRsResourceBase {
         }
 
         @Override
+        public void onBlockingState(final BlockingTransitionInternalEvent event) {
+            log.info(String.format("Got event BlockingTransitionInternalEvent token = %s", event.getUserToken()));
+        }
+
+        @Override
         public void onEmptyInvoice(final NullInvoiceInternalEvent event) {
             log.info("Got event EmptyInvoiceNotification token='{}'", event.getUserToken());
             notifyForCompletion();
@@ -553,6 +626,12 @@ public class SubscriptionResource extends JaxRsResourceBase {
             log.info("Got event PaymentPluginError token='{}'", event.getUserToken());
             notifyForCompletion();
         }
+
+        @Override
+        public void onInvoicePaymentError(final InvoicePaymentErrorInternalEvent event) {
+            log.info("Got event InvoicePaymentError token='{}'", event.getUserToken());
+            notifyForCompletion();
+        }
     }
 
     private interface EntitlementCallCompletionCallback<T> {
@@ -569,7 +648,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         public Response withSynchronization(final EntitlementCallCompletionCallback<T> callback,
                                             final long timeoutSec,
                                             final boolean callCompletion,
-                                            final CallContext callContext) throws SubscriptionApiException, AccountApiException, EntitlementApiException{
+                                            final CallContext callContext) throws SubscriptionApiException, AccountApiException, EntitlementApiException {
             final CompletionUserRequestEntitlement waiter = callCompletion ? new CompletionUserRequestEntitlement(callContext.getUserToken()) : null;
             try {
                 if (waiter != null) {
@@ -647,7 +726,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Subscription not found")})
     public Response getTags(@PathParam(ID_PARAM_NAME) final String subscriptionIdString,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                            @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                            @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
                             @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, SubscriptionApiException {
         final UUID subscriptionId = UUID.fromString(subscriptionIdString);
         final TenantContext tenantContext = context.createContext(request);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
index f5536f2..8e30db8 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
@@ -54,10 +54,10 @@ import org.killbill.commons.metrics.TimedResource;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -75,7 +75,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
                                  final PaymentApi paymentApi,
                                  final Clock clock,
                                  final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
index 305aa45..5c6350d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
@@ -53,9 +53,9 @@ import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -73,7 +73,7 @@ public class TagResource extends JaxRsResourceBase {
                        final PaymentApi paymentApi,
                        final Clock clock,
                        final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     @TimedResource
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 3854b14..d348596 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,13 +16,16 @@
 
 package org.killbill.billing.jaxrs.resources;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
 import javax.ws.rs.POST;
@@ -36,6 +39,9 @@ import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.callcontext.DefaultCallContext;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.jaxrs.json.TenantJson;
 import org.killbill.billing.jaxrs.json.TenantKeyJson;
 import org.killbill.billing.jaxrs.util.Context;
@@ -50,16 +56,18 @@ import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.callcontext.UserType;
 import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
@@ -70,6 +78,7 @@ import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
 public class TenantResource extends JaxRsResourceBase {
 
     private final TenantUserApi tenantApi;
+    private final CatalogUserApi catalogUserApi;
 
     @Inject
     public TenantResource(final TenantUserApi tenantApi,
@@ -79,10 +88,12 @@ public class TenantResource extends JaxRsResourceBase {
                           final AuditUserApi auditUserApi,
                           final AccountUserApi accountUserApi,
                           final PaymentApi paymentApi,
+                          final CatalogUserApi catalogUserApi,
                           final Clock clock,
                           final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.tenantApi = tenantApi;
+        this.catalogUserApi = catalogUserApi;
     }
 
     @TimedResource
@@ -114,17 +125,23 @@ public class TenantResource extends JaxRsResourceBase {
     @ApiOperation(value = "Create a tenant")
     @ApiResponses(value = {@ApiResponse(code = 500, message = "Tenant already exists")})
     public Response createTenant(final TenantJson json,
+                                 @QueryParam(QUERY_TENANT_USE_GLOBAL_DEFAULT) @DefaultValue("true") final Boolean useGlobalDefault,
                                  @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 {
+                                 @javax.ws.rs.core.Context final UriInfo uriInfo) throws TenantApiException, CatalogApiException {
         verifyNonNullOrEmpty(json, "TenantJson body should be specified");
         verifyNonNullOrEmpty(json.getApiKey(), "TenantJson apiKey needs to be set",
                              json.getApiSecret(), "TenantJson apiSecret needs to be set");
 
         final TenantData data = json.toTenantData();
         final Tenant tenant = tenantApi.createTenant(data, context.createContext(createdBy, reason, comment, request));
+        if (!useGlobalDefault) {
+            final CallContext callContext = new DefaultCallContext(tenant.getId(), createdBy, CallOrigin.EXTERNAL,
+                                                                   UserType.CUSTOMER, Context.getOrCreateUserToken(), clock);
+            catalogUserApi.createDefaultEmptyCatalog(clock.getUTCNow(),callContext);
+        }
         return uriBuilder.buildResponse(uriInfo, TenantResource.class, "getTenant", tenant.getId());
     }
 
@@ -183,6 +200,8 @@ public class TenantResource extends JaxRsResourceBase {
         return insertTenantKey(TenantKey.PLUGIN_CONFIG_, pluginName, pluginConfig, uriInfo, "getPluginConfiguration", createdBy, reason, comment, request);
     }
 
+
+
     @TimedResource
     @GET
     @Path("/" + UPLOAD_PLUGIN_CONFIG + "/{pluginName:" + ANYTHING_PATTERN + "}")
@@ -207,6 +226,64 @@ public class TenantResource extends JaxRsResourceBase {
         return deleteTenantKey(TenantKey.PLUGIN_CONFIG_, pluginName, createdBy, reason, comment, request);
     }
 
+
+    @TimedResource
+    @GET
+    @Path("/" + UPLOAD_PER_TENANT_CONFIG + "/{keyPrefix:" + ANYTHING_PATTERN + "}" + "/" + SEARCH)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve a per tenant key value based on key prefix", response = TenantKeyJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response getAllPluginConfiguration(@PathParam("keyPrefix") final String keyPrefix,
+                                              @javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+
+        final TenantContext tenantContext = context.createContext(request);
+        final Map<String, List<String>> apiResult = tenantApi.searchTenantKeyValues(keyPrefix, tenantContext);
+        final List<TenantKeyJson> result = new ArrayList<TenantKeyJson>();
+        for (final String cur : apiResult.keySet()) {
+            result.add(new TenantKeyJson(cur, apiResult.get(cur)));
+        }
+        return Response.status(Status.OK).entity(result).build();
+    }
+
+
+    @TimedResource
+    @POST
+    @Path("/" + UPLOAD_PER_TENANT_CONFIG)
+    @Consumes(TEXT_PLAIN)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Add a per tenant configuration (system properties)")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response uploadPerTenantConfiguration(final String perTenantConfig,
+                                                 @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 {
+        return insertTenantKey(TenantKey.PER_TENANT_CONFIG, null, perTenantConfig, uriInfo, "getPerTenantConfiguration", createdBy, reason, comment, request);
+    }
+
+    @TimedResource
+    @GET
+    @Path("/" + UPLOAD_PER_TENANT_CONFIG)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve a per tenant configuration (system properties)", response = TenantKeyJson.class)
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response getPerTenantConfiguration(@javax.ws.rs.core.Context final HttpServletRequest request) throws TenantApiException {
+        return getTenantKey(TenantKey.PER_TENANT_CONFIG, null, request);
+    }
+
+    @TimedResource
+    @DELETE
+    @Path("/" + UPLOAD_PER_TENANT_CONFIG)
+    @ApiOperation(value = "Delete a per tenant configuration (system properties)")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid tenantId supplied")})
+    public Response deletePerTenantConfiguration(@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 {
+        return deleteTenantKey(TenantKey.PER_TENANT_CONFIG, null, createdBy, reason, comment, request);
+    }
+
     @TimedResource
     @POST
     @Path("/" + UPLOAD_PLUGIN_PAYMENT_STATE_MACHINE_CONFIG + "/{pluginName:" + ANYTHING_PATTERN + "}")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
index ed9e462..9e1f704 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TestResource.java
@@ -64,10 +64,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -96,7 +96,7 @@ public class TestResource extends JaxRsResourceBase {
                         final AuditUserApi auditUserApi, final AccountUserApi accountUserApi, final RecordIdApi recordIdApi,
                         final PersistentBus persistentBus, final NotificationQueueService notificationQueueService, final PaymentApi paymentApi,
                         final Clock clock, final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.persistentBus = persistentBus;
         this.notificationQueueService = notificationQueueService;
         this.recordIdApi = recordIdApi;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index df14d0f..1fe9878 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -47,6 +47,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.util.api.AuditUserApi;
@@ -61,10 +62,10 @@ import org.killbill.clock.Clock;
 import org.killbill.commons.metrics.TimedResource;
 
 import com.google.common.collect.ImmutableList;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -83,7 +84,7 @@ public class TransactionResource extends JaxRsResourceBase {
                                final PaymentApi paymentApi,
                                final Clock clock,
                                final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
     }
 
     @TimedResource
@@ -108,7 +109,7 @@ public class TransactionResource extends JaxRsResourceBase {
         final CallContext callContext = context.createContext(createdBy, reason, comment, request);
 
         final UUID paymentId = UUID.fromString(json.getPaymentId());
-        final Payment payment = paymentApi.getPayment(paymentId, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = paymentApi.getPayment(paymentId, false, false, ImmutableList.<PluginProperty>of(), callContext);
         final Account account = accountUserApi.getAccountById(payment.getAccountId(), callContext);
 
         final boolean success = TransactionStatus.SUCCESS.name().equals(json.getStatus());
@@ -174,10 +175,10 @@ public class TransactionResource extends JaxRsResourceBase {
     public Response getTags(@PathParam(ID_PARAM_NAME) final String id,
                             @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                             @QueryParam(QUERY_TAGS_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
-                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException {
+                            @javax.ws.rs.core.Context final HttpServletRequest request) throws TagDefinitionApiException, PaymentApiException {
         final TenantContext tenantContext = context.createContext(request);
-        //return super.getTags(accountId, invoiceId, auditMode, includedDeleted, tenantContext);
-        throw new IllegalStateException("Not implemented");
+        final Payment payment = paymentApi.getPaymentByTransactionId(UUID.fromString(id), false, false, ImmutableList.<PluginProperty>of(), tenantContext);
+        return super.getTags(payment.getAccountId(), UUID.fromString(id), auditMode, includedDeleted, tenantContext);
     }
 
     @TimedResource
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index 55eee36..6434b39 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -49,6 +49,7 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.usage.api.RolledUpUsage;
 import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UsageApiException;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -62,10 +63,10 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Singleton;
-import com.wordnik.swagger.annotations.Api;
-import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
-import com.wordnik.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
@@ -88,7 +89,7 @@ public class UsageResource extends JaxRsResourceBase {
                          final EntitlementApi entitlementApi,
                          final Clock clock,
                          final Context context) {
-        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
+        super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, null, clock, context);
         this.usageUserApi = usageUserApi;
         this.entitlementApi = entitlementApi;
     }
@@ -104,7 +105,9 @@ public class UsageResource extends JaxRsResourceBase {
                                 @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 EntitlementApiException, AccountApiException {
+                                @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException,
+                                                                                        AccountApiException,
+                                                                                        UsageApiException {
         verifyNonNullOrEmpty(json, "SubscriptionUsageRecordJson body should be specified");
         verifyNonNullOrEmpty(json.getSubscriptionId(), "SubscriptionUsageRecordJson subscriptionId needs to be set",
                              json.getUnitUsageRecords(), "SubscriptionUsageRecordJson unitUsageRecords needs to be set");
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
index d431641..8e8dc7c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/Context.java
@@ -84,7 +84,7 @@ public class Context {
     }
 
     // Use REQUEST_ID_HEADER if this is provided and lloks like a UUID, if not allocate a random one.
-    private UUID getOrCreateUserToken() {
+    public static  UUID getOrCreateUserToken() {
         UUID userToken;
         if (Request.getPerThreadRequestData().getRequestId() != null) {
             try {
@@ -110,6 +110,6 @@ public class Context {
 
     private void populateMDCContext(final TenantContext tenantContext) {
         // InternalCallContextFactory will do it for us
-        internalCallContextFactory.createInternalTenantContext(tenantContext);
+        internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
index 68d4fda..ee09c1a 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
@@ -17,6 +17,7 @@
 package org.killbill.billing.jaxrs.util;
 
 import java.net.URI;
+import java.util.HashMap;
 import java.util.Map;
 
 import javax.annotation.Nullable;
@@ -28,11 +29,15 @@ import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.jaxrs.resources.JaxRsResourceBase;
 import org.killbill.billing.jaxrs.resources.JaxrsResource;
-import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.billing.util.config.definition.JaxrsConfig;
 
 public class JaxrsUriBuilder {
 
     private final JaxrsConfig jaxrsConfig;
+    private final Map<Class, UriBuilder> classToUriBuilder = new HashMap<Class, UriBuilder>();
+    private final Map<String, UriBuilder> classAndMethodToUriBuilder = new HashMap<String, UriBuilder>();
+    private final Map<String, UriBuilder> pathAndClassToUriBuilder = new HashMap<String, UriBuilder>();
+    private final Map<String, UriBuilder> pathClassAndMethodToUriBuilder = new HashMap<String, UriBuilder>();
 
     @Inject
     public JaxrsUriBuilder(JaxrsConfig jaxrsConfig) {
@@ -53,8 +58,8 @@ public class JaxrsUriBuilder {
 
         if (jaxrsConfig.isJaxrsLocationFullUrl()) {
             uriBuilder.scheme(uriInfo.getAbsolutePath().getScheme())
-              .host(uriInfo.getAbsolutePath().getHost())
-              .port(uriInfo.getAbsolutePath().getPort());
+                      .host(uriInfo.getAbsolutePath().getHost())
+                      .port(uriInfo.getAbsolutePath().getPort());
         }
         return objectId != null ? uriBuilder.build(objectId) : uriBuilder.build();
     }
@@ -92,15 +97,74 @@ public class JaxrsUriBuilder {
 
     private UriBuilder getUriBuilder(final String path, final Class<? extends JaxrsResource> theClassMaybeEnhanced, @Nullable final String getMethodName) {
         final Class theClass = getNonEnhancedClass(theClassMaybeEnhanced);
-        return getMethodName != null ? UriBuilder.fromPath(path.equals("/") ? path.substring(1) : path).path(theClass).path(theClass, getMethodName) :
-               UriBuilder.fromPath(path).path(theClass);
+        return getMethodName != null ? fromPath(path.equals("/") ? path.substring(1) : path, theClass, getMethodName) : fromPath(path, theClass);
+    }
+
+    private UriBuilder fromPath(final String path, final Class theClass, final String getMethodName) {
+        final String key = path + theClass.getName() + getMethodName;
+
+        UriBuilder uriBuilder = pathClassAndMethodToUriBuilder.get(key);
+        if (uriBuilder == null) {
+            synchronized (pathClassAndMethodToUriBuilder) {
+                uriBuilder = pathClassAndMethodToUriBuilder.get(key);
+                if (uriBuilder == null) {
+                    uriBuilder = fromPath(path, theClass).path(theClass, getMethodName);
+                    pathClassAndMethodToUriBuilder.put(key, uriBuilder);
+                }
+            }
+        }
+        return uriBuilder.clone();
+    }
+
+    private UriBuilder fromPath(final String path, final Class theClass) {
+        final String key = path + theClass.getName();
+
+        UriBuilder uriBuilder = pathAndClassToUriBuilder.get(key);
+        if (uriBuilder == null) {
+            synchronized (pathAndClassToUriBuilder) {
+                uriBuilder = pathAndClassToUriBuilder.get(key);
+                if (uriBuilder == null) {
+                    uriBuilder = UriBuilder.fromPath(path).path(theClass);
+                    pathAndClassToUriBuilder.put(key, uriBuilder);
+                }
+            }
+        }
+        return uriBuilder.clone();
     }
 
     private UriBuilder getUriBuilder(final Class<? extends JaxrsResource> theClassMaybeEnhanced, @Nullable final String getMethodName) {
         final Class theClass = getNonEnhancedClass(theClassMaybeEnhanced);
-        return getMethodName != null ? UriBuilder.fromResource(theClass).path(theClass, getMethodName) :
-               UriBuilder.fromResource(theClass);
+        return getMethodName != null ? fromResource(theClass, getMethodName) : fromResource(theClass);
+    }
 
+    private UriBuilder fromResource(final Class theClass, final String getMethodName) {
+        final String key = theClass.getName() + getMethodName;
+
+        UriBuilder uriBuilder = classAndMethodToUriBuilder.get(key);
+        if (uriBuilder == null) {
+            synchronized (classAndMethodToUriBuilder) {
+                uriBuilder = classAndMethodToUriBuilder.get(key);
+                if (uriBuilder == null) {
+                    uriBuilder = fromResource(theClass).path(theClass, getMethodName);
+                    classAndMethodToUriBuilder.put(key, uriBuilder);
+                }
+            }
+        }
+        return uriBuilder.clone();
+    }
+
+    private UriBuilder fromResource(final Class theClass) {
+        UriBuilder uriBuilder = classToUriBuilder.get(theClass);
+        if (uriBuilder == null) {
+            synchronized (classToUriBuilder) {
+                uriBuilder = classToUriBuilder.get(theClass);
+                if (uriBuilder == null) {
+                    uriBuilder = UriBuilder.fromResource(theClass);
+                    classToUriBuilder.put(theClass, uriBuilder);
+                }
+            }
+        }
+        return uriBuilder.clone();
     }
 
     private Class getNonEnhancedClass(final Class<? extends JaxrsResource> theClassMaybeEnhanced) {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
index 1b4a1d4..68e8875 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/KillbillEventHandler.java
@@ -58,7 +58,7 @@ public class KillbillEventHandler {
      */
     @AllowConcurrentEvents
     @Subscribe
-    public void handleSubscriptionevents(final BusInternalEvent event) {
+    public void handleSubscriptionEvents(final BusInternalEvent event) {
         final List<CompletionUserRequestNotifier> runningWaiters = new ArrayList<CompletionUserRequestNotifier>();
         synchronized (activeWaiters) {
             runningWaiters.addAll(activeWaiters);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
index fa2dd35..6fa4edb 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/glue/TestJaxrsModuleNoDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -18,8 +18,15 @@
 
 package org.killbill.billing.jaxrs.glue;
 
+import org.apache.shiro.mgt.SecurityManager;
 import org.killbill.billing.GuicyKillbillTestNoDBModule;
+import org.killbill.billing.mock.glue.MockAccountModule;
+import org.killbill.billing.mock.glue.MockNonEntityDaoModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.util.glue.CacheModule;
+import org.killbill.billing.util.glue.ConfigModule;
+import org.mockito.Mockito;
 
 public class TestJaxrsModuleNoDB extends TestJaxrsModule {
 
@@ -31,5 +38,12 @@ public class TestJaxrsModuleNoDB extends TestJaxrsModule {
     public void configure() {
         super.configure();
         install(new GuicyKillbillTestNoDBModule(configSource));
+
+        install(new MockNonEntityDaoModule(configSource));
+        install(new MockAccountModule(configSource));
+        install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
+        bind(TenantInternalApi.class).toInstance(Mockito.mock(TenantInternalApi.class));
+        bind(SecurityManager.class).toInstance(Mockito.mock(SecurityManager.class));
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAccountJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAccountJson.java
index 4a87407..b601e7c 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAccountJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestAccountJson.java
@@ -49,13 +49,15 @@ public class TestAccountJson extends JaxrsTestSuiteNoDB {
         final String country = UUID.randomUUID().toString();
         final String locale = UUID.randomUUID().toString();
         final String phone = UUID.randomUUID().toString();
+        final String notes = UUID.randomUUID().toString();
         final Boolean isMigrated = true;
         final Boolean isNotifiedForInvoice = false;
+        final String parentAccountId = UUID.randomUUID().toString();
 
         final AccountJson accountJson = new AccountJson(accountId, name, length, externalKey,
-                                                        email, billCycleDayLocal, currency, paymentMethodId,
+                                                        email, billCycleDayLocal, currency, parentAccountId, true, paymentMethodId,
                                                         timeZone, address1, address2, postalCode, company, city, state,
-                                                        country, locale, phone, isMigrated, isNotifiedForInvoice, null, null, null);
+                                                        country, locale, phone, notes, isMigrated, isNotifiedForInvoice, null, null, null);
         Assert.assertEquals(accountJson.getAccountId(), accountId);
         Assert.assertEquals(accountJson.getName(), name);
         Assert.assertEquals(accountJson.getFirstNameLength(), length);
@@ -74,8 +76,11 @@ public class TestAccountJson extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(accountJson.getCountry(), country);
         Assert.assertEquals(accountJson.getLocale(), locale);
         Assert.assertEquals(accountJson.getPhone(), phone);
+        Assert.assertEquals(accountJson.getNotes(), notes);
         Assert.assertEquals(accountJson.isMigrated(), isMigrated);
         Assert.assertEquals(accountJson.isNotifiedForInvoices(), isNotifiedForInvoice);
+        Assert.assertEquals(accountJson.getParentAccountId(), parentAccountId);
+        Assert.assertEquals(accountJson.isPaymentDelegatedToParent(), Boolean.TRUE);
 
         final String asJson = mapper.writeValueAsString(accountJson);
         final AccountJson fromJson = mapper.readValue(asJson, AccountJson.class);
@@ -105,6 +110,7 @@ public class TestAccountJson extends JaxrsTestSuiteNoDB {
         accountBuilder.postalCode(UUID.randomUUID().toString());
         accountBuilder.stateOrProvince(UUID.randomUUID().toString());
         accountBuilder.timeZone(DateTimeZone.UTC);
+        accountBuilder.parentAccountId(UUID.randomUUID());
         final Account account = accountBuilder.build();
 
         final AccountJson accountJson = new AccountJson(account, null, null, null);
@@ -125,5 +131,6 @@ public class TestAccountJson extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(accountJson.isNotifiedForInvoices(), account.isNotifiedForInvoices());
         Assert.assertEquals(accountJson.getState(), account.getStateOrProvince());
         Assert.assertEquals(accountJson.getTimeZone(), account.getTimeZone().toString());
+        Assert.assertEquals(accountJson.getParentAccountId(), account.getParentAccountId().toString());
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
index 947473c..3230c8b 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleJsonWithSubscriptions.java
@@ -45,7 +45,6 @@ public class TestBundleJsonWithSubscriptions extends JaxrsTestSuiteNoDB {
         final EventSubscriptionJson event = new EventSubscriptionJson(UUID.randomUUID().toString(),
                                                                       BillingPeriod.NO_BILLING_PERIOD.toString(),
                                                                       new LocalDate(),
-                                                                      new LocalDate(),
                                                                       UUID.randomUUID().toString(),
                                                                       UUID.randomUUID().toString(),
                                                                       UUID.randomUUID().toString(),
@@ -70,10 +69,12 @@ public class TestBundleJsonWithSubscriptions extends JaxrsTestSuiteNoDB {
                                                                    UUID.randomUUID().toString(),
                                                                    UUID.randomUUID().toString(),
                                                                    UUID.randomUUID().toString(),
+                                                                   UUID.randomUUID().toString(),
                                                                    new LocalDate(),
                                                                    new LocalDate(),
                                                                    new LocalDate(),
                                                                    new LocalDate(),
+                                                                   null,
                                                                    ImmutableList.<EventSubscriptionJson>of(event),
                                                                    ImmutableList.of(priceOverride),
                                                                    auditLogs);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
index 9874b55..5749e61 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestBundleTimelineJson.java
@@ -36,7 +36,6 @@ public class TestBundleTimelineJson extends JaxrsTestSuiteNoDB {
         final EventSubscriptionJson event = new EventSubscriptionJson(UUID.randomUUID().toString(),
                                                                       BillingPeriod.NO_BILLING_PERIOD.toString(),
                                                                       new LocalDate(),
-                                                                      new LocalDate(),
                                                                       UUID.randomUUID().toString(),
                                                                       UUID.randomUUID().toString(),
                                                                       UUID.randomUUID().toString(),
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
index c86a648..422a0f1 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestCreditJson.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.Currency;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -34,13 +35,14 @@ public class TestCreditJson extends JaxrsTestSuiteNoDB {
     @Test(groups = "fast")
     public void testJson() throws Exception {
         final BigDecimal creditAmount = BigDecimal.TEN;
+        final Currency currency = Currency.AED;
         final String invoiceId = UUID.randomUUID().toString();
         final String invoiceNumber = UUID.randomUUID().toString();
         final LocalDate effectiveDate = clock.getUTCToday();
         final String accountId = UUID.randomUUID().toString();
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
-        final CreditJson creditJson = new CreditJson(creditAmount, invoiceId, invoiceNumber, effectiveDate,
-                                                     accountId, auditLogs);
+        final CreditJson creditJson = new CreditJson(creditAmount, currency.name(), invoiceId, invoiceNumber, effectiveDate,
+                                                     accountId, null, auditLogs);
         Assert.assertEquals(creditJson.getEffectiveDate(), effectiveDate);
         Assert.assertEquals(creditJson.getCreditAmount(), creditAmount);
         Assert.assertEquals(creditJson.getInvoiceId(), invoiceId);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
index 14e7b6e..adceb4a 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestEntitlementJsonWithEvents.java
@@ -47,14 +47,12 @@ public class TestEntitlementJsonWithEvents extends JaxrsTestSuiteNoDB {
         final String bundleId = UUID.randomUUID().toString();
         final String subscriptionId = UUID.randomUUID().toString();
         final String externalKey = UUID.randomUUID().toString();
-        final DateTime requestedDate = DefaultClock.toUTCDateTime(new DateTime(DateTimeZone.UTC));
         final DateTime effectiveDate = DefaultClock.toUTCDateTime(new DateTime(DateTimeZone.UTC));
         final UUID eventId = UUID.randomUUID();
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
 
         final EventSubscriptionJson newEvent = new EventSubscriptionJson(eventId.toString(),
                                                                          BillingPeriod.NO_BILLING_PERIOD.toString(),
-                                                                         requestedDate.toLocalDate(),
                                                                          effectiveDate.toLocalDate(),
                                                                          UUID.randomUUID().toString(),
                                                                          UUID.randomUUID().toString(),
@@ -80,10 +78,12 @@ public class TestEntitlementJsonWithEvents extends JaxrsTestSuiteNoDB {
                                                                                 UUID.randomUUID().toString(),
                                                                                 UUID.randomUUID().toString(),
                                                                                 UUID.randomUUID().toString(),
+                                                                                UUID.randomUUID().toString(),
                                                                                 new LocalDate(),
                                                                                 new LocalDate(),
                                                                                 new LocalDate(),
                                                                                 new LocalDate(),
+                                                                                null,
                                                                                 ImmutableList.<EventSubscriptionJson>of(newEvent),
                                                                                 ImmutableList.of(priceOverride),
                                                                                 null);
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
index 96eb7a7..bd54e91 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceItemJsonSimple.java
@@ -40,6 +40,7 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
         final String invoiceId = UUID.randomUUID().toString();
         final String linkedInvoiceItemId = UUID.randomUUID().toString();
         final String accountId = UUID.randomUUID().toString();
+        final String childAccountId = UUID.randomUUID().toString();
         final String bundleId = UUID.randomUUID().toString();
         final String subscriptionId = UUID.randomUUID().toString();
         final String planName = UUID.randomUUID().toString();
@@ -52,13 +53,14 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
         final BigDecimal amount = BigDecimal.TEN;
         final Currency currency = Currency.MXN;
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
-        final InvoiceItemJson invoiceItemJson = new InvoiceItemJson(invoiceItemId, invoiceId, linkedInvoiceItemId, accountId,
+        final InvoiceItemJson invoiceItemJson = new InvoiceItemJson(invoiceItemId, invoiceId, linkedInvoiceItemId, accountId, childAccountId,
                                                                                       bundleId, subscriptionId, planName, phaseName, usageName, type, description,
-                                                                                      startDate, endDate, amount, currency, auditLogs);
+                                                                                      startDate, endDate, amount, currency.name(), null, auditLogs);
         Assert.assertEquals(invoiceItemJson.getInvoiceItemId(), invoiceItemId);
         Assert.assertEquals(invoiceItemJson.getInvoiceId(), invoiceId);
         Assert.assertEquals(invoiceItemJson.getLinkedInvoiceItemId(), linkedInvoiceItemId);
         Assert.assertEquals(invoiceItemJson.getAccountId(), accountId);
+        Assert.assertEquals(invoiceItemJson.getChildAccountId(), childAccountId);
         Assert.assertEquals(invoiceItemJson.getBundleId(), bundleId);
         Assert.assertEquals(invoiceItemJson.getSubscriptionId(), subscriptionId);
         Assert.assertEquals(invoiceItemJson.getPlanName(), planName);
@@ -69,7 +71,7 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(invoiceItemJson.getStartDate(), startDate);
         Assert.assertEquals(invoiceItemJson.getEndDate(), endDate);
         Assert.assertEquals(invoiceItemJson.getAmount(), amount);
-        Assert.assertEquals(invoiceItemJson.getCurrency(), currency);
+        Assert.assertEquals(invoiceItemJson.getCurrency(), currency.name());
         Assert.assertEquals(invoiceItemJson.getAuditLogs(), auditLogs);
 
         final String asJson = mapper.writeValueAsString(invoiceItemJson);
@@ -110,6 +112,6 @@ public class TestInvoiceItemJsonSimple extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(invoiceItemJson.getStartDate(), invoiceItem.getStartDate());
         Assert.assertEquals(invoiceItemJson.getEndDate(), invoiceItem.getEndDate());
         Assert.assertEquals(invoiceItemJson.getAmount(), invoiceItem.getAmount());
-        Assert.assertEquals(invoiceItemJson.getCurrency(), invoiceItem.getCurrency());
+        Assert.assertEquals(invoiceItemJson.getCurrency(), invoiceItem.getCurrency().name());
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
index 2a38928..6b9a29e 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.mockito.Mockito;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -50,9 +51,10 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
         final CreditJson creditJson = createCreditJson();
         final List<CreditJson> credits = ImmutableList.<CreditJson>of(creditJson);
         final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
-        final InvoiceJson invoiceJsonSimple = new InvoiceJson(amount, Currency.USD.toString(), creditAdj, refundAdj, invoiceId, invoiceDate,
-                                                                                          targetDate, invoiceNumber, balance, accountId, bundleKeys,
-                                                                                          credits, null, auditLogs);
+        final InvoiceJson invoiceJsonSimple = new InvoiceJson(amount, Currency.USD.toString(), InvoiceStatus.COMMITTED.toString(),
+                                                              creditAdj, refundAdj, invoiceId, invoiceDate,
+                                                              targetDate, invoiceNumber, balance, accountId, bundleKeys,
+                                                              credits, null, false, auditLogs);
         Assert.assertEquals(invoiceJsonSimple.getAmount(), amount);
         Assert.assertEquals(invoiceJsonSimple.getCreditAdj(), creditAdj);
         Assert.assertEquals(invoiceJsonSimple.getRefundAdj(), refundAdj);
@@ -65,6 +67,8 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(invoiceJsonSimple.getBundleKeys(), bundleKeys);
         Assert.assertEquals(invoiceJsonSimple.getCredits(), credits);
         Assert.assertEquals(invoiceJsonSimple.getAuditLogs(), auditLogs);
+        Assert.assertEquals(invoiceJsonSimple.getStatus(), InvoiceStatus.COMMITTED.toString());
+        Assert.assertFalse(invoiceJsonSimple.getIsParentInvoice());
 
         final String asJson = mapper.writeValueAsString(invoiceJsonSimple);
         final InvoiceJson fromJson = mapper.readValue(asJson, InvoiceJson.class);
@@ -84,6 +88,7 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
         Mockito.when(invoice.getBalance()).thenReturn(BigDecimal.ZERO);
         Mockito.when(invoice.getAccountId()).thenReturn(UUID.randomUUID());
         Mockito.when(invoice.getCurrency()).thenReturn(Currency.MXN);
+        Mockito.when(invoice.getStatus()).thenReturn(InvoiceStatus.COMMITTED);
 
 
         final String bundleKeys = UUID.randomUUID().toString();
@@ -102,14 +107,16 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
         Assert.assertEquals(invoiceJson.getBundleKeys(), bundleKeys);
         Assert.assertEquals(invoiceJson.getCredits(), credits);
         Assert.assertNull(invoiceJson.getAuditLogs());
+        Assert.assertEquals(invoiceJson.getStatus(), InvoiceStatus.COMMITTED.toString());
     }
 
     private CreditJson createCreditJson() {
         final BigDecimal creditAmount = BigDecimal.TEN;
+        final Currency currency = Currency.USD;
         final String invoiceId = UUID.randomUUID().toString();
         final String invoiceNumber = UUID.randomUUID().toString();
         final LocalDate effectiveDate = clock.getUTCToday();
         final String accountId = UUID.randomUUID().toString();
-        return new CreditJson(creditAmount, invoiceId, invoiceNumber, effectiveDate,  accountId, null);
+        return new CreditJson(creditAmount, currency.name(), invoiceId, invoiceNumber, effectiveDate,  accountId, null, null);
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestSubscriptionUsageRecordJson.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestSubscriptionUsageRecordJson.java
new file mode 100644
index 0000000..95c232b
--- /dev/null
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestSubscriptionUsageRecordJson.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs.json;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.jaxrs.JaxrsTestSuiteNoDB;
+import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UnitUsageRecordJson;
+import org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UsageRecordJson;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestSubscriptionUsageRecordJson extends JaxrsTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testJson() throws Exception {
+        final LocalDate localDate = new LocalDate();
+        final String subscriptionId = UUID.randomUUID().toString();
+        final String trackingId = UUID.randomUUID().toString();
+        final List<UnitUsageRecordJson> unitUsageRecords = new ArrayList<UnitUsageRecordJson>();
+        final List<UsageRecordJson> usageRecords = new ArrayList<UsageRecordJson>();
+        final UsageRecordJson usageRecordJson = new UsageRecordJson(localDate, 5L);
+        usageRecords.add(usageRecordJson);
+        final UnitUsageRecordJson unitUsageRecordJson = new UnitUsageRecordJson("foo", usageRecords);
+        unitUsageRecords.add(unitUsageRecordJson);
+
+        final SubscriptionUsageRecordJson subscriptionUsageRecordJson = new SubscriptionUsageRecordJson(subscriptionId, trackingId, unitUsageRecords);
+        Assert.assertEquals(subscriptionUsageRecordJson.getSubscriptionId(), subscriptionId);
+        Assert.assertEquals(subscriptionUsageRecordJson.getTrackingId(), trackingId);
+        Assert.assertEquals(subscriptionUsageRecordJson.getUnitUsageRecords().size(), 1);
+        Assert.assertEquals(subscriptionUsageRecordJson.getUnitUsageRecords().get(0).getUnitType(), "foo");
+        Assert.assertEquals(subscriptionUsageRecordJson.getUnitUsageRecords().get(0).getUsageRecords().size(), 1);
+        Assert.assertEquals(subscriptionUsageRecordJson.getUnitUsageRecords().get(0).getUsageRecords().get(0).getAmount(), new Long(5L));
+        Assert.assertEquals(subscriptionUsageRecordJson.getUnitUsageRecords().get(0).getUsageRecords().get(0).getRecordDate(), localDate);
+
+        final SubscriptionUsageRecord subscriptionUsageRecord = subscriptionUsageRecordJson.toSubscriptionUsageRecord();
+        Assert.assertEquals(subscriptionUsageRecord.getSubscriptionId().toString(), subscriptionId);
+        Assert.assertEquals(subscriptionUsageRecord.getTrackingId(), trackingId);
+        Assert.assertEquals(subscriptionUsageRecord.getUnitUsageRecord().size(), 1);
+        Assert.assertEquals(subscriptionUsageRecord.getUnitUsageRecord().get(0).getUnitType(), "foo");
+        Assert.assertEquals(subscriptionUsageRecord.getUnitUsageRecord().get(0).getDailyAmount().size(), 1);
+        Assert.assertEquals(subscriptionUsageRecord.getUnitUsageRecord().get(0).getDailyAmount().get(0).getAmount(), new Long(5L));
+        Assert.assertEquals(subscriptionUsageRecord.getUnitUsageRecord().get(0).getDailyAmount().get(0).getDate(), localDate);
+    }
+}
\ No newline at end of file
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
index ecf1d44..85195c0 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/resources/TestJaxRsResourceBase.java
@@ -64,7 +64,7 @@ public class TestJaxRsResourceBase extends JaxrsTestSuiteNoDB {
         private static final class JaxRsResourceBaseTest extends JaxRsResourceBase {
 
         public JaxRsResourceBaseTest() {
-            super(null, null, null, null, null, null, null, null);
+            super(null, null, null, null, null, null, null, null, null);
         }
     }
 }
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
index a09bf3f..f70b3e4 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/TestDateConversion.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -36,7 +38,7 @@ import org.killbill.billing.util.UUIDs;
 public class TestDateConversion extends JaxRsResourceBase {
 
     public TestDateConversion() throws AccountApiException {
-        super(null, null, null, null, Mockito.mock(AccountUserApi.class), null, new ClockMock(), null);
+        super(null, null, null, null, Mockito.mock(AccountUserApi.class), null, null, new ClockMock(), null);
     }
 
     public UUID setupAccount(DateTimeZone accountTimeZone) throws AccountApiException {
@@ -55,22 +57,18 @@ public class TestDateConversion extends JaxRsResourceBase {
     //
     // BASIC Tests to understand how toLocalDate converts different inputs (null, LocalDate, DateTime)
     //
-    @Test(groups = "fast")
-    public void testDateTimeConversion() throws AccountApiException {
-        final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
-        final String input = "2013-08-26T06:50:20Z";
-        final LocalDate result = toLocalDate(accountId, input, null);
-        Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
-    }
-
 
     @Test(groups = "fast")
     public void testNullConversion() throws AccountApiException {
+        final String input = null;
+
+        final LocalDate result = toLocalDate(input);
+        Assert.assertNull(result);
+
         final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
         ((ClockMock) clock).setTime(new DateTime("2013-08-26T06:50:20Z"));
-        final String input = null;
-        final LocalDate result = toLocalDate(accountId, input, null);
-        Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
+        final LocalDate result2 = toLocalDateDefaultToday(accountId, input, null);
+        Assert.assertTrue(result2.compareTo(new LocalDate(2013, 8, 25)) == 0);
         ((ClockMock) clock).resetDeltaFromReality();
     }
 
@@ -78,7 +76,7 @@ public class TestDateConversion extends JaxRsResourceBase {
     public void testLocalDateConversion() throws AccountApiException {
         final UUID accountId = setupAccount(DateTimeZone.forOffsetHours(-8));
         final String input = "2013-08-25";
-        final LocalDate result = toLocalDate(accountId, input, null);
+        final LocalDate result = toLocalDate(input);
         Assert.assertTrue(result.compareTo(new LocalDate(2013, 8, 25)) == 0);
     }
 

junction/pom.xml 37(+28 -9)

diff --git a/junction/pom.xml b/junction/pom.xml
index 343580e..6206cee 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>
@@ -50,10 +50,30 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-account</artifactId>
             <type>test-jar</type>
@@ -131,10 +151,6 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-clock</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.kill-bill.commons</groupId>
-            <artifactId>killbill-clock</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
@@ -146,6 +162,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -164,10 +187,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.skife.config</groupId>
-            <artifactId>config-magic</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index b43bd54..a03af67 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -37,6 +37,9 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.Days;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Catalog;
+import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -59,6 +62,7 @@ public class BlockingCalculator {
     private static final AtomicLong globaltotalOrder = new AtomicLong();
 
     private final BlockingInternalApi blockingApi;
+    private final CatalogService catalogService;
 
     protected static class DisabledDuration {
 
@@ -84,8 +88,9 @@ public class BlockingCalculator {
     }
 
     @Inject
-    public BlockingCalculator(final BlockingInternalApi blockingApi) {
+    public BlockingCalculator(final BlockingInternalApi blockingApi, final CatalogService catalogService) {
         this.blockingApi = blockingApi;
+        this.catalogService = catalogService;
     }
 
     /**
@@ -93,9 +98,9 @@ public class BlockingCalculator {
      *
      * @param billingEvents the original list of billing events to update (without overdue events)
      */
-    public void insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final InternalTenantContext context) {
+    public boolean insertBlockingEvents(final SortedSet<BillingEvent> billingEvents, final Set<UUID> skippedSubscriptions, final InternalTenantContext context) throws CatalogApiException {
         if (billingEvents.size() <= 0) {
-            return;
+            return false;
         }
 
         final Hashtable<UUID, List<SubscriptionBase>> bundleMap = createBundleSubscriptionMap(billingEvents);
@@ -129,7 +134,7 @@ public class BlockingCalculator {
                 final List<BlockingState> aggregateSubscriptionBlockingEvents = getAggregateBlockingEventsPerSubscription(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
                 final List<DisabledDuration> accountBlockingDurations = createBlockingDurations(aggregateSubscriptionBlockingEvents);
 
-                billingEventsToAdd.addAll(createNewEvents(accountBlockingDurations, billingEvents, subscription));
+                billingEventsToAdd.addAll(createNewEvents(accountBlockingDurations, billingEvents, subscription, context));
                 billingEventsToRemove.addAll(eventsToRemove(accountBlockingDurations, billingEvents, subscription));
             }
         }
@@ -141,6 +146,8 @@ public class BlockingCalculator {
         for (final BillingEvent eventToRemove : billingEventsToRemove) {
             billingEvents.remove(eventToRemove);
         }
+
+        return !(billingEventsToAdd.isEmpty() && billingEventsToRemove.isEmpty());
     }
 
     final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
@@ -187,8 +194,11 @@ public class BlockingCalculator {
         return result;
     }
 
-    protected SortedSet<BillingEvent> createNewEvents(final List<DisabledDuration> disabledDuration, final SortedSet<BillingEvent> billingEvents, final SubscriptionBase subscription) {
+    protected SortedSet<BillingEvent> createNewEvents(final List<DisabledDuration> disabledDuration, final SortedSet<BillingEvent> billingEvents, final SubscriptionBase subscription, final InternalTenantContext context) throws CatalogApiException {
+
         final SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
+
         for (final DisabledDuration duration : disabledDuration) {
             // The first one before the blocked duration
             final BillingEvent precedingInitialEvent = precedingBillingEventForSubscription(duration.getStart(), billingEvents, subscription);
@@ -196,12 +206,12 @@ public class BlockingCalculator {
             final BillingEvent precedingFinalEvent = precedingBillingEventForSubscription(duration.getEnd(), billingEvents, subscription);
 
             if (precedingInitialEvent != null) { // there is a preceding billing event
-                result.add(createNewDisableEvent(duration.getStart(), precedingInitialEvent));
+                result.add(createNewDisableEvent(duration.getStart(), precedingInitialEvent, catalog, context));
                 if (duration.getEnd() != null) { // no second event in the pair means they are still disabled (no re-enable)
-                    result.add(createNewReenableEvent(duration.getEnd(), precedingFinalEvent));
+                    result.add(createNewReenableEvent(duration.getEnd(), precedingFinalEvent, catalog, context));
                 }
             } else if (precedingFinalEvent != null) { // can happen - e.g. phase event
-                result.add(createNewReenableEvent(duration.getEnd(), precedingFinalEvent));
+                result.add(createNewReenableEvent(duration.getEnd(), precedingFinalEvent, catalog, context));
             }
             // N.B. if there's no precedingInitial and no precedingFinal then there's nothing to do
         }
@@ -241,7 +251,7 @@ public class BlockingCalculator {
         return result;
     }
 
-    protected BillingEvent createNewDisableEvent(final DateTime odEventTime, final BillingEvent previousEvent) {
+    protected BillingEvent createNewDisableEvent(final DateTime odEventTime, final BillingEvent previousEvent, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
         final int billCycleDay = previousEvent.getBillCycleDayLocal();
         final SubscriptionBase subscription = previousEvent.getSubscription();
         final DateTime effectiveDate = odEventTime;
@@ -251,7 +261,6 @@ public class BlockingCalculator {
         // Make sure to set the fixed price to null and the billing period to NO_BILLING_PERIOD,
         // which makes invoice disregard this event
         final BigDecimal fixedPrice = null;
-        final BigDecimal recurringPrice = null;
         final BillingPeriod billingPeriod = BillingPeriod.NO_BILLING_PERIOD;
 
         final Currency currency = previousEvent.getCurrency();
@@ -260,21 +269,20 @@ public class BlockingCalculator {
         final Long totalOrdering = globaltotalOrder.getAndIncrement();
         final DateTimeZone tz = previousEvent.getTimeZone();
 
-        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase,
-                                       fixedPrice, recurringPrice, currency,
+        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
+                                       currency,
                                        billingPeriod, billCycleDay,
-                                       description, totalOrdering, type, tz);
+                                       description, totalOrdering, type, tz, catalog, true);
     }
 
-    protected BillingEvent createNewReenableEvent(final DateTime odEventTime, final BillingEvent previousEvent) {
+    protected BillingEvent createNewReenableEvent(final DateTime odEventTime, final BillingEvent previousEvent, final Catalog catalog, final InternalTenantContext context) throws CatalogApiException {
         // All fields are populated with the event state from before the blocking period, for invoice to resume invoicing
         final int billCycleDay = previousEvent.getBillCycleDayLocal();
         final SubscriptionBase subscription = previousEvent.getSubscription();
         final DateTime effectiveDate = odEventTime;
         final PlanPhase planPhase = previousEvent.getPlanPhase();
-        final Plan plan = previousEvent.getPlan();
         final BigDecimal fixedPrice = previousEvent.getFixedPrice();
-        final BigDecimal recurringPrice = previousEvent.getRecurringPrice();
+        final Plan plan = previousEvent.getPlan();
         final Currency currency = previousEvent.getCurrency();
         final String description = "";
         final BillingPeriod billingPeriod = previousEvent.getBillingPeriod();
@@ -282,10 +290,10 @@ public class BlockingCalculator {
         final Long totalOrdering = globaltotalOrder.getAndIncrement();
         final DateTimeZone tz = previousEvent.getTimeZone();
 
-        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase,
-                                       fixedPrice, recurringPrice, currency,
+        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
+                                       currency,
                                        billingPeriod, billCycleDay,
-                                       description, totalOrdering, type, tz);
+                                       description, totalOrdering, type, tz, catalog, false);
     }
 
     protected Hashtable<UUID, List<SubscriptionBase>> createBundleSubscriptionMap(final SortedSet<BillingEvent> billingEvents) {
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
index cbf1076..7183e3c 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEvent.java
@@ -47,7 +47,6 @@ public class DefaultBillingEvent implements BillingEvent {
     private final PlanPhase planPhase;
     private final Plan plan;
     private final BigDecimal fixedPrice;
-    private final BigDecimal recurringPrice;
     private final Currency currency;
     private final String description;
     private final BillingPeriod billingPeriod;
@@ -57,8 +56,14 @@ public class DefaultBillingEvent implements BillingEvent {
 
     private final List<Usage> usages;
 
+    private final Catalog catalog;
+    private final boolean isDisableEvent;
+    private final PlanPhase nextPlanPhase;
+
     public DefaultBillingEvent(final ImmutableAccountData account, final EffectiveSubscriptionInternalEvent transition, final SubscriptionBase subscription, final int billCycleDayLocal, final Currency currency, final Catalog catalog) throws CatalogApiException {
 
+        this.catalog = catalog;
+
         final boolean isActive = transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL;
 
         this.billCycleDayLocal = billCycleDayLocal;
@@ -71,34 +76,35 @@ public class DefaultBillingEvent implements BillingEvent {
         this.plan = (planName != null) ? catalog.findPlan(planName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
 
         final String nextPhaseName = transition.getNextPhase();
-        final PlanPhase nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+        this.nextPlanPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
 
         final String prevPhaseName = transition.getPreviousPhase();
-        final PlanPhase prevPhase = (prevPhaseName != null) ? catalog.findPhase(prevPhaseName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
-
-        this.fixedPrice = getFixedPrice(nextPhase, currency);
-        this.recurringPrice = getRecurringPrice(nextPhase, currency);
+        final PlanPhase prevPlanPhase = (prevPhaseName != null) ? catalog.findPhase(prevPhaseName, transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
 
+        this.fixedPrice = transition.getTransitionType() != SubscriptionBaseTransitionType.BCD_CHANGE ? getFixedPrice(nextPlanPhase, currency) : null;
         this.currency = currency;
         this.description = transition.getTransitionType().toString();
-        this.billingPeriod = getRecurringBillingPeriod(isActive ? nextPhase : prevPhase);
+        this.billingPeriod = getRecurringBillingPeriod(isActive ? nextPlanPhase : prevPlanPhase);
         this.type = transition.getTransitionType();
         this.totalOrdering = transition.getTotalOrdering();
         this.timeZone = account.getTimeZone();
         this.usages = initializeUsage(isActive);
+        this.isDisableEvent = false;
     }
 
     public DefaultBillingEvent(final SubscriptionBase subscription, final DateTime effectiveDate, final boolean isActive,
-                               final Plan plan, final PlanPhase planPhase,
-                               final BigDecimal fixedPrice, final BigDecimal recurringPrice, final Currency currency,
+                               final Plan plan, final PlanPhase planPhase, final BigDecimal fixedPrice,
+                               final Currency currency,
                                final BillingPeriod billingPeriod, final int billCycleDayLocal,
-                               final String description, final long totalOrdering, final SubscriptionBaseTransitionType type, final DateTimeZone timeZone) {
+                               final String description, final long totalOrdering, final SubscriptionBaseTransitionType type, final DateTimeZone timeZone,
+                               final Catalog catalog,
+                               final boolean isDisableEvent) {
+        this.catalog = catalog;
         this.subscription = subscription;
         this.effectiveDate = effectiveDate;
         this.plan = plan;
         this.planPhase = planPhase;
         this.fixedPrice = fixedPrice;
-        this.recurringPrice = recurringPrice;
         this.currency = currency;
         this.billingPeriod = billingPeriod;
         this.billCycleDayLocal = billCycleDayLocal;
@@ -107,7 +113,8 @@ public class DefaultBillingEvent implements BillingEvent {
         this.totalOrdering = totalOrdering;
         this.timeZone = timeZone;
         this.usages = initializeUsage(isActive);
-
+        this.isDisableEvent = isDisableEvent;
+        this.nextPlanPhase = isDisableEvent ? null : planPhase;
     }
 
     @Override
@@ -197,8 +204,12 @@ public class DefaultBillingEvent implements BillingEvent {
     }
 
     @Override
-    public BigDecimal getRecurringPrice() {
-        return recurringPrice;
+    public BigDecimal getRecurringPrice(final DateTime effectiveDate) throws CatalogApiException {
+        if (isDisableEvent || nextPlanPhase == null) {
+            return null;
+        }
+        final PlanPhase effectivePlanPhase = effectiveDate != null ? catalog.findPhase(nextPlanPhase.getName(), effectiveDate, subscription.getStartDate()) : nextPlanPhase;
+        return getRecurringPrice(effectivePlanPhase, currency);
     }
 
     @Override
@@ -261,13 +272,13 @@ public class DefaultBillingEvent implements BillingEvent {
         if (currency != that.currency) {
             return false;
         }
-        if (description != null ? !description.equals(that.description) : that.description != null) {
+        if (fixedPrice != null ? !fixedPrice.equals(that.fixedPrice) : that.fixedPrice != null) {
             return false;
         }
-        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+        if (description != null ? !description.equals(that.description) : that.description != null) {
             return false;
         }
-        if (fixedPrice != null ? !fixedPrice.equals(that.fixedPrice) : that.fixedPrice != null) {
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
             return false;
         }
         if (plan != null ? !plan.equals(that.plan) : that.plan != null) {
@@ -276,9 +287,6 @@ public class DefaultBillingEvent implements BillingEvent {
         if (planPhase != null ? !planPhase.equals(that.planPhase) : that.planPhase != null) {
             return false;
         }
-        if (recurringPrice != null ? !recurringPrice.equals(that.recurringPrice) : that.recurringPrice != null) {
-            return false;
-        }
         if (subscription != null ? !subscription.equals(that.subscription) : that.subscription != null) {
             return false;
         }
@@ -299,11 +307,10 @@ public class DefaultBillingEvent implements BillingEvent {
     public int hashCode() {
         int result = 31 * billCycleDayLocal;
         result = 31 * result + (subscription != null ? subscription.hashCode() : 0);
+        result = 31 * result + (fixedPrice != null ? fixedPrice.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (planPhase != null ? planPhase.hashCode() : 0);
         result = 31 * result + (plan != null ? plan.hashCode() : 0);
-        result = 31 * result + (fixedPrice != null ? fixedPrice.hashCode() : 0);
-        result = 31 * result + (recurringPrice != null ? recurringPrice.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (description != null ? description.hashCode() : 0);
         result = 31 * result + (billingPeriod != null ? billingPeriod.hashCode() : 0);
@@ -333,7 +340,7 @@ public class DefaultBillingEvent implements BillingEvent {
         if (!isActive) {
             return result;
         }
-        if (planPhase != null) {
+        if (planPhase != null && planPhase.getUsages() != null) {
             result = Lists.newArrayList();
             for (Usage usage : planPhase.getUsages()) {
                 result.add(usage);
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index 55b746a..c971588 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -19,7 +19,6 @@
 package org.killbill.billing.junction.plumbing.billing;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -28,14 +27,10 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingMode;
 import org.killbill.billing.catalog.api.Usage;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BillingEventSet;
-import org.killbill.billing.util.AccountDateAndTimeZoneContext;
-import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
 
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
@@ -48,34 +43,14 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
     private final boolean accountAutoInvoiceOff;
     private final List<UUID> subscriptionIdsWithAutoInvoiceOff;
     private final BillingMode recurringBillingMode;
-    private final InternalTenantContext internalTenantContext;
 
-    private DefaultAccountDateAndTimeZoneContext dateTimeZoneContext;
-
-    public DefaultBillingEventSet(final boolean accountAutoInvoiceOff, final BillingMode recurringBillingMode, final InternalTenantContext internalTenantContext) {
+    public DefaultBillingEventSet(final boolean accountAutoInvoiceOff, final BillingMode recurringBillingMode) {
         this.accountAutoInvoiceOff = accountAutoInvoiceOff;
         this.recurringBillingMode = recurringBillingMode;
-        this.internalTenantContext = internalTenantContext;
         this.subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
     }
 
     @Override
-    public boolean add(final BillingEvent e) {
-        if (dateTimeZoneContext == null) {
-            this.dateTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(e.getEffectiveDate(), internalTenantContext);
-        }
-        return super.add(e);
-    }
-
-    @Override
-    public boolean addAll(final Collection<? extends BillingEvent> all) {
-        if (dateTimeZoneContext == null) {
-            this.dateTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(all.iterator().next().getEffectiveDate(), internalTenantContext);
-        }
-        return super.addAll(all);
-    }
-
-    @Override
     public boolean isAccountAutoInvoiceOff() {
         return accountAutoInvoiceOff;
     }
@@ -91,14 +66,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
     }
 
     @Override
-    public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
-        if (dateTimeZoneContext == null) {
-            throw new IllegalArgumentException("AccountDateAndTimeZoneContext is not initialized because there is no billing event");
-        }
-        return dateTimeZoneContext;
-    }
-
-    @Override
     public Map<String, Usage> getUsages() {
         final Iterable<Usage> allUsages = Iterables.concat(Iterables.transform(this, new Function<BillingEvent, List<Usage>>() {
             @Override
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index f263960..85691aa 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -31,9 +31,15 @@ import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.BillingAlignment;
+import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
+import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
@@ -47,9 +53,9 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
-import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -61,73 +67,71 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultInternalBillingApi.class);
     private final AccountInternalApi accountApi;
-    private final BillCycleDayCalculator bcdCalculator;
     private final SubscriptionBaseInternalApi subscriptionApi;
     private final CatalogService catalogService;
     private final BlockingCalculator blockCalculator;
     private final TagInternalApi tagApi;
-    private final Clock clock;
 
     @Inject
     public DefaultInternalBillingApi(final AccountInternalApi accountApi,
-                                     final BillCycleDayCalculator bcdCalculator,
                                      final SubscriptionBaseInternalApi subscriptionApi,
                                      final BlockingCalculator blockCalculator,
                                      final CatalogService catalogService,
-                                     final TagInternalApi tagApi,
-                                     final Clock clock) {
+                                     final TagInternalApi tagApi) {
         this.accountApi = accountApi;
-        this.bcdCalculator = bcdCalculator;
         this.subscriptionApi = subscriptionApi;
         this.catalogService = catalogService;
         this.blockCalculator = blockCalculator;
         this.tagApi = tagApi;
-        this.clock = clock;
     }
 
     @Override
-    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException {
-        final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
-        final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(context);
-
-        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
-        final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode(), context);
+    public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
 
+        final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(true, true, context);
 
+        // Check to see if billing is off for the account
+        final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
+        final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
 
         final Set<UUID> skippedSubscriptions = new HashSet<UUID>();
-        try {
-            // Check to see if billing is off for the account
-            final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
-            final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
-            if (found_AUTO_INVOICING_OFF) {
-                return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode(), context); // billing is off, we are done
-            }
+        final DefaultBillingEventSet result;
 
+        if (found_AUTO_INVOICING_OFF) {
+            result = new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode()); // billing is off, we are done
+        } else {
+            final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
+
+            final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
+            result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
             addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions);
-        } catch (SubscriptionBaseApiException e) {
-            log.warn("Failed while getting BillingEvent", e);
+        }
+
+        if (result.isEmpty()) {
+            log.info("No billing event for accountId='{}'", accountId);
+            return result;
         }
 
         // Pretty-print the events, before and after the blocking calculator does its magic
         final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
-        eventsToString(logStringBuilder, result, "\nBilling Events Raw");
-        blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context);
-        eventsToString(logStringBuilder, result, "\nBilling Events After Blocking");
+        eventsToString(logStringBuilder, result);
+        if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context)) {
+            logStringBuilder.append("\nBilling Events After Blocking");
+            eventsToString(logStringBuilder, result);
+        }
         log.info(logStringBuilder.toString());
 
         return result;
     }
 
-    private void eventsToString(final StringBuilder stringBuilder, final SortedSet<BillingEvent> events, final String title) {
-        stringBuilder.append(title);
+    private void eventsToString(final StringBuilder stringBuilder, final SortedSet<BillingEvent> events) {
         for (final BillingEvent event : events) {
             stringBuilder.append("\n").append(event.toString());
         }
     }
 
     private void addBillingEventsForBundles(final List<SubscriptionBaseBundle> bundles, final ImmutableAccountData account, final DryRunArguments dryRunArguments, final InternalCallContext context,
-                                            final DefaultBillingEventSet result, final Set<UUID> skipSubscriptionsSet) throws SubscriptionBaseApiException, AccountApiException {
+                                            final DefaultBillingEventSet result, final Set<UUID> skipSubscriptionsSet) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
 
         final boolean dryRunMode = dryRunArguments != null;
 
@@ -139,7 +143,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             final UUID fakeBundleId = UUIDs.randomUUID();
             final List<SubscriptionBase> subscriptions = subscriptionApi.getSubscriptionsForBundle(fakeBundleId, dryRunArguments, context);
 
-            addBillingEventsForSubscription(account, subscriptions, fakeBundleId, dryRunMode, context, result, skipSubscriptionsSet);
+            addBillingEventsForSubscription(account, subscriptions, null, dryRunMode, context, result, skipSubscriptionsSet);
 
         }
 
@@ -158,7 +162,8 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
                     result.getSubscriptionIdsWithAutoInvoiceOff().add(subscription.getId());
                 }
             } else { // billing is not off
-                addBillingEventsForSubscription(account, subscriptions, bundle.getId(), dryRunMode, context, result, skipSubscriptionsSet);
+                final SubscriptionBase baseSubscription = !subscriptions.isEmpty() ? subscriptions.get(0) : null;
+                addBillingEventsForSubscription(account, subscriptions, baseSubscription, dryRunMode, context, result, skipSubscriptionsSet);
             }
         }
     }
@@ -166,60 +171,80 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
 
     private void addBillingEventsForSubscription(final ImmutableAccountData account,
                                                  final List<SubscriptionBase> subscriptions,
-                                                 final UUID bundleId,
+                                                 final SubscriptionBase baseSubscription,
                                                  final boolean dryRunMode,
                                                  final InternalCallContext context,
                                                  final DefaultBillingEventSet result,
-                                                 final Set<UUID> skipSubscriptionsSet) throws AccountApiException {
+                                                 final Set<UUID> skipSubscriptionsSet) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
 
         // If dryRun is specified, we don't want to to update the account BCD value, so we initialize the flag updatedAccountBCD to true
         boolean updatedAccountBCD = dryRunMode;
 
-
-        int currentAccountBCD = accountApi.getBCD(account.getId(), context);
+        final int currentAccountBCD = accountApi.getBCD(account.getId(), context);
         for (final SubscriptionBase subscription : subscriptions) {
 
             // The subscription did not even start, so there is nothing to do yet, we can skip and avoid some NPE down the line when calculating the BCD
-            if (subscription.getState() == null) {
+            if (subscription.getState() == EntitlementState.PENDING) {
+                log.info("Skipping subscription id='{}', state = EntitlementState.PENDING", subscription.getId());
                 continue;
             }
 
             final List<EffectiveSubscriptionInternalEvent> billingTransitions = subscriptionApi.getBillingTransitions(subscription, context);
             if (billingTransitions.isEmpty() ||
                 (billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.CREATE &&
-                 billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.MIGRATE_BILLING &&
                  billingTransitions.get(0).getTransitionType() != SubscriptionBaseTransitionType.TRANSFER)) {
                 log.warn("Skipping billing events for subscription " + subscription.getId() + ": Does not start with a valid CREATE transition");
                 skipSubscriptionsSet.add(subscription.getId());
                 return;
             }
 
+            final Catalog catalog = catalogService.getFullCatalog(true, true, context);
 
+            Integer overridenBCD = null;
             for (final EffectiveSubscriptionInternalEvent transition : billingTransitions) {
-                try {
-                    final int bcdLocal = bcdCalculator.calculateBcd(account, currentAccountBCD, bundleId, subscription, transition, context);
-
-                    if (currentAccountBCD == 0 && !updatedAccountBCD) {
-                        accountApi.updateBCD(account.getExternalKey(), bcdLocal, context);
-                        updatedAccountBCD = true;
-                    }
-
-                    final BillingEvent event = new DefaultBillingEvent(account, transition, subscription, bcdLocal, account.getCurrency(), catalogService.getFullCatalog(context));
-                    result.add(event);
-                } catch (CatalogApiException e) {
-                    log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
-                              transition.getId().toString(), e);
-                } catch (AccountApiException e) {
-                    // This is unexpected  (failed to update BCD) but if this happens we don't want to ignore..
-                    throw e;
-                } catch (Exception e) {
-                    log.warn("Failed while getting BillingEvent", e);
+                //
+                // A BCD_CHANGE transition defines a new billCycleDayLocal for the subscription and this overrides whatever computation
+                // occurs below (which is based on billing alignment policy). Also multiple of those BCD_CHANGE transitions could occur,
+                // to define different intervals with different billing cycle days.
+                //
+                overridenBCD = transition.getNextBillCycleDayLocal() != null ? transition.getNextBillCycleDayLocal() : overridenBCD;
+                final int bcdLocal = overridenBCD != null ?
+                                     overridenBCD :
+                                     calculateBcdForTransition(catalog, baseSubscription, subscription, account, currentAccountBCD, transition);
+
+                if (currentAccountBCD == 0 && !updatedAccountBCD) {
+                    log.info("Setting account BCD='{}', accountId='{}'", bcdLocal, account.getId());
+                    accountApi.updateBCD(account.getExternalKey(), bcdLocal, context);
+                    updatedAccountBCD = true;
                 }
+
+                final BillingEvent event = new DefaultBillingEvent(account, transition, subscription, bcdLocal, account.getCurrency(), catalog);
+                result.add(event);
             }
         }
     }
 
-    private final boolean is_AUTO_INVOICING_OFF(final List<Tag> tags) {
+    private int calculateBcdForTransition(final Catalog catalog, final SubscriptionBase baseSubscription, final SubscriptionBase subscription, final ImmutableAccountData account, final int accountBillCycleDayLocal, final EffectiveSubscriptionInternalEvent transition)
+            throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final BillingAlignment alignment = catalog.billingAlignment(getPlanPhaseSpecifierFromTransition(catalog, transition), transition.getEffectiveTransitionTime());
+        return BillCycleDayCalculator.calculateBcdForAlignment(subscription, baseSubscription, alignment, account.getTimeZone(), accountBillCycleDayLocal);
+    }
+
+    private PlanPhaseSpecifier getPlanPhaseSpecifierFromTransition(final Catalog catalog, final EffectiveSubscriptionInternalEvent transition) throws CatalogApiException {
+        final Plan prevPlan = (transition.getPreviousPlan() != null) ? catalog.findPlan(transition.getPreviousPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+        final Plan nextPlan = (transition.getNextPlan() != null) ? catalog.findPlan(transition.getNextPlan(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+
+        final Plan plan = (transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL) ? nextPlan : prevPlan;
+
+        final PlanPhase prevPhase = (transition.getPreviousPhase() != null) ? catalog.findPhase(transition.getPreviousPhase(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+        final PlanPhase nextPhase = (transition.getNextPhase() != null) ? catalog.findPhase(transition.getNextPhase(), transition.getEffectiveTransitionTime(), transition.getSubscriptionStartDate()) : null;
+        final PlanPhase phase = (transition.getTransitionType() != SubscriptionBaseTransitionType.CANCEL) ? nextPhase : prevPhase;
+
+        return new PlanPhaseSpecifier(plan.getName(), phase.getPhaseType());
+
+    }
+
+    private boolean is_AUTO_INVOICING_OFF(final List<Tag> tags) {
         return ControlTagType.isAutoInvoicingOff(Collections2.transform(tags, new Function<Tag, UUID>() {
             @Nullable
             @Override
diff --git a/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModule.java
index d2bb7aa..803853a 100644
--- a/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/org/killbill/billing/junction/glue/TestJunctionModule.java
@@ -29,6 +29,7 @@ import org.killbill.billing.mock.glue.MockTenantModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
 import org.killbill.billing.util.glue.KillBillShiroModule;
 import org.killbill.billing.util.glue.SecurityModule;
@@ -44,6 +45,7 @@ public class TestJunctionModule extends DefaultJunctionModule {
         super.configure();
 
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new MockTenantModule(configSource));
         // Needed because Entitlement depends on Security
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
index f13d01e..4a84424 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteNoDB.java
@@ -16,21 +16,19 @@
 
 package org.killbill.billing.junction;
 
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-
 import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
-import org.killbill.bus.api.PersistentBus;
+import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.entitlement.dao.BlockingStateDao;
 import org.killbill.billing.junction.glue.TestJunctionModuleNoDB;
-import org.killbill.billing.junction.plumbing.billing.BillCycleDayCalculator;
 import org.killbill.billing.junction.plumbing.billing.BlockingCalculator;
-import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.tag.dao.TagDao;
+import org.killbill.bus.api.PersistentBus;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
@@ -41,8 +39,6 @@ public abstract class JunctionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected AccountInternalApi accountInternalApi;
     @Inject
-    protected BillCycleDayCalculator billCycleDayCalculator;
-    @Inject
     protected BillingInternalApi billingInternalApi;
     @Inject
     protected BlockingCalculator blockingCalculator;
diff --git a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 7c03083..4eced64 100644
--- a/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/org/killbill/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -23,7 +23,6 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
-import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
@@ -43,7 +42,6 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
 import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
-import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.bus.api.PersistentBus;
@@ -125,7 +123,7 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
 
     private Catalog initCatalog(final CatalogService catalogService) throws Exception {
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog catalog = catalogService.getFullCatalog(true, true, internalCallContext);
         assertNotNull(catalog);
         return catalog;
     }
@@ -216,9 +214,7 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
     protected Account createAccount(final AccountData accountData) throws AccountApiException {
         final Account account = accountApi.createAccount(accountData, callContext);
 
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        refreshCallContext(account.getId());
 
         return account;
     }
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
index 5b4748b..eddb672 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillingApi.java
@@ -53,6 +53,7 @@ import org.killbill.billing.mock.MockEffectiveSubscriptionEvent;
 import org.killbill.billing.mock.MockSubscription;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.tag.ControlTagType;
@@ -92,7 +93,7 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         effectiveSubscriptionTransitions = new LinkedList<EffectiveSubscriptionInternalEvent>();
 
         final DateTime subscriptionStartDate = clock.getUTCNow().minusDays(3);
-        subscription = new MockSubscription(subId, bunId, null, subscriptionStartDate, effectiveSubscriptionTransitions);
+        subscription = new MockSubscription(subId, bunId, null, subscriptionStartDate, subscriptionStartDate);
         final List<SubscriptionBase> subscriptions = ImmutableList.<SubscriptionBase>of(subscription);
 
         Mockito.when(subscriptionInternalApi.getBundlesForAccount(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(bundles);
@@ -103,9 +104,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         Mockito.when(subscriptionInternalApi.getBillingTransitions(Mockito.<SubscriptionBase>any(),  Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
         Mockito.when(subscriptionInternalApi.getAllTransitions(Mockito.<SubscriptionBase>any(), Mockito.<InternalTenantContext>any())).thenReturn(effectiveSubscriptionTransitions);
 
-        catalog = ((MockCatalog) catalogService.getCurrentCatalog(internalCallContext));
+        catalog = ((MockCatalog) catalogService.getCurrentCatalog(true, true, internalCallContext));
         // TODO The MockCatalog module returns two different things for full vs current catalog
-        Mockito.when(catalogService.getFullCatalog(internalCallContext)).thenReturn(catalog);
+        Mockito.when(catalogService.getFullCatalog(true, true, internalCallContext)).thenReturn(catalog);
         // Set a default alignment
         catalog.setBillingAlignment(BillingAlignment.ACCOUNT);
 
@@ -115,14 +116,14 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsEmpty() throws AccountApiException, CatalogApiException {
+    public void testBillingEventsEmpty() throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
         final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(new UUID(0L, 0L), null, internalCallContext);
         Assert.assertEquals(events.size(), 0);
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsNoBillingPeriod() throws CatalogApiException, AccountApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsNoBillingPeriod() throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         // The trial has no billing period
         final PlanPhase nextPhase = nextPlan.getAllPhases()[0];
         final DateTime now = createSubscriptionCreationEvent(nextPlan, nextPhase);
@@ -134,8 +135,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsSubscriptionAligned() throws CatalogApiException, AccountApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsSubscriptionAligned() throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         final DateTime now = createSubscriptionCreationEvent(nextPlan, nextPhase);
 
@@ -149,8 +150,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsAccountAligned() throws CatalogApiException, AccountApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsAccountAligned() throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         final DateTime now = createSubscriptionCreationEvent(nextPlan, nextPhase);
 
@@ -162,15 +163,15 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsBundleAligned() throws CatalogApiException, AccountApiException {
-        final Plan nextPlan = catalog.findPlan("Horn1USD", clock.getUTCNow());
+    public void testBillingEventsBundleAligned() throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("7-Horn1USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[0];
         final DateTime now = createSubscriptionCreationEvent(nextPlan, nextPhase);
 
         final Account account = createAccount(1);
 
         catalog.setBillingAlignment(BillingAlignment.BUNDLE);
-        ((MockSubscription) subscription).setPlan(catalog.findPlan("PickupTrialEvergreen10USD", now));
+        ((MockSubscription) subscription).setPlan(catalog.findPlan("3-PickupTrialEvergreen10USD", now));
 
         final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext);
         // The expected BCD is when the subscription started
@@ -178,8 +179,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsWithBlock() throws CatalogApiException, AccountApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsWithBlock() throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         final DateTime now = createSubscriptionCreationEvent(nextPlan, nextPhase);
 
@@ -200,8 +201,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsAutoInvoicingOffAccount() throws CatalogApiException, AccountApiException, TagApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsAutoInvoicingOffAccount() throws CatalogApiException, AccountApiException, TagApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         createSubscriptionCreationEvent(nextPlan, nextPhase);
 
@@ -216,8 +217,8 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testBillingEventsAutoInvoicingOffBundle() throws CatalogApiException, AccountApiException, TagApiException {
-        final Plan nextPlan = catalog.findPlan("PickupTrialEvergreen10USD", clock.getUTCNow());
+    public void testBillingEventsAutoInvoicingOffBundle() throws CatalogApiException, AccountApiException, TagApiException, SubscriptionBaseApiException {
+        final Plan nextPlan = catalog.findPlan("3-PickupTrialEvergreen10USD", clock.getUTCNow());
         final PlanPhase nextPhase = nextPlan.getAllPhases()[1];
         createSubscriptionCreationEvent(nextPlan, nextPhase);
 
@@ -247,9 +248,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         }
 
         if (recurringPrice != null) {
-            Assert.assertEquals(recurringPrice.getPrice(Currency.USD), event.getRecurringPrice());
+            Assert.assertEquals(recurringPrice.getPrice(Currency.USD), event.getRecurringPrice(null));
         } else {
-            assertNull(event.getRecurringPrice());
+            assertNull(event.getRecurringPrice(null));
         }
 
         Assert.assertEquals(BCD, event.getBillCycleDayLocal());
@@ -281,9 +282,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
         final PriceList nextPriceList = catalog.findPriceList(PriceListSet.DEFAULT_PRICELIST_NAME, now);
 
         final EffectiveSubscriptionInternalEvent t = new MockEffectiveSubscriptionEvent(
-                eventId, subId, bunId, then, now, null, null, null, null, EntitlementState.ACTIVE,
+                eventId, subId, bunId, then, now, null, null, null, null, null, EntitlementState.ACTIVE,
                 nextPlan.getName(), nextPhase.getName(),
-                nextPriceList.getName(), 1L,
+                nextPriceList.getName(), null, 1L,
                 SubscriptionBaseTransitionType.CREATE, 1, null, 1L, 2L, null);
 
         effectiveSubscriptionTransitions.add(t);
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 8c26b09..ebb438e 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -33,11 +33,13 @@ import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.MockPlan;
-import org.killbill.billing.catalog.MockPlanPhase;
 import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.InternationalPrice;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.Recurring;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.dao.MockBlockingStateDao;
@@ -48,6 +50,7 @@ import org.killbill.billing.junction.plumbing.billing.BlockingCalculator.Disable
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.mockito.Mockito;
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -104,7 +107,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     // S2 --B--[-------]--------------------------
     // S3 ------------------D---------------------
     @Test(groups = "fast")
-    public void testInsertBlockingEventsForBundle() {
+    public void testInsertBlockingEventsForBundle() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
 
         final BillingEvent A = createRealEvent(now.minusDays(1).minusHours(1), subscription1);
@@ -319,7 +322,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     // Open ended duration with a previous event
     // --X--[----------------------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsOpenPrev() {
+    public void testCreateNewEventsOpenPrev() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -327,12 +330,12 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, null));
         billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice());
+        assertNull(results.first().getRecurringPrice(null));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
     }
@@ -340,7 +343,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     // Open with previous and following events
     // --X--[----Y-----------------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsOpenPrevFollow() {
+    public void testCreateNewEventsOpenPrevFollow() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -349,12 +352,12 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice());
+        assertNull(results.first().getRecurringPrice(null));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
     }
@@ -362,7 +365,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     // Open with no previous event (only following)
     // -----[----X-----------------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsOpenFollow() {
+    public void testCreateNewEventsOpenFollow() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -370,7 +373,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, null));
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 0);
     }
@@ -378,7 +381,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     // Closed duration with a single previous event
     // --X--[------------]---------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedPrev() {
+    public void testCreateNewEventsClosedPrev() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -386,23 +389,24 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
         billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
+
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice());
+        assertNull(results.first().getRecurringPrice(null));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(), billingEvents.first().getRecurringPrice());
+        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
     // Closed duration with a previous event and in-between event
     // --X--[------Y-----]---------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedPrevBetw() {
+    public void testCreateNewEventsClosedPrevBetw() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -411,23 +415,23 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         billingEvents.add(createRealEvent(now.minusDays(1), subscription1));
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice());
+        assertNull(results.first().getRecurringPrice(null));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(), billingEvents.first().getRecurringPrice());
+        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
     // Closed duration with a previous event and in-between event and following
     // --X--[------Y-----]-------Z-------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedPrevBetwNext() {
+    public void testCreateNewEventsClosedPrevBetwNext() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -437,23 +441,23 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
         billingEvents.add(createRealEvent(now.plusDays(3), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 2);
         assertEquals(results.first().getEffectiveDate(), now);
         assertNull(results.first().getFixedPrice());
-        assertNull(results.first().getRecurringPrice());
+        assertNull(results.first().getRecurringPrice(null));
         assertEquals(results.first().getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
         assertEquals(results.first().getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(), billingEvents.first().getRecurringPrice());
+        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
     // Closed with no previous event but in-between events
     // -----[------Y-----]---------------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedBetwn() {
+    public void testCreateNewEventsClosedBetwn() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -461,18 +465,18 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(), billingEvents.first().getRecurringPrice());
+        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
     // Closed with no previous event but in-between events and following
     // -----[------Y-----]-------Z-------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedBetweenFollow() {
+    public void testCreateNewEventsClosedBetweenFollow() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -480,18 +484,18 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
         billingEvents.add(createRealEvent(now.plusDays(1), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 1);
         assertEquals(results.last().getEffectiveDate(), now.plusDays(2));
-        assertEquals(results.last().getRecurringPrice(), billingEvents.first().getRecurringPrice());
+        assertEquals(results.last().getRecurringPrice(null), billingEvents.first().getRecurringPrice(null));
         assertEquals(results.last().getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
     }
 
     // Closed duration with only following
     // -----[------------]-------Z-------------
     @Test(groups = "fast")
-    public void testCreateNewEventsClosedFollow() {
+    public void testCreateNewEventsClosedFollow() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final List<DisabledDuration> disabledDuration = new ArrayList<BlockingCalculator.DisabledDuration>();
         final SortedSet<BillingEvent> billingEvents = new TreeSet<BillingEvent>();
@@ -499,7 +503,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         disabledDuration.add(new DisabledDuration(now, now.plusDays(2)));
         billingEvents.add(createRealEvent(now.plusDays(3), subscription1));
 
-        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1);
+        final SortedSet<BillingEvent> results = blockingCalculator.createNewEvents(disabledDuration, billingEvents, subscription1, internalCallContext);
 
         assertEquals(results.size(), 0);
     }
@@ -528,23 +532,38 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         return createRealEvent(effectiveDate, subscription, SubscriptionBaseTransitionType.CHANGE);
     }
 
-    protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription, final SubscriptionBaseTransitionType type) {
-        final Account account = this.account;
-        final Integer billCycleDay = 1;
-        final PlanPhase planPhase = new MockPlanPhase();
-        final Plan plan = new MockPlan();
-        final BigDecimal fixedPrice = BigDecimal.TEN;
-        final BigDecimal recurringPrice = BigDecimal.TEN;
-        final Currency currency = Currency.USD;
-        final String description = "";
-        final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
-        final Long totalOrdering = 0L;
-        final DateTimeZone tz = DateTimeZone.UTC;
-
-        return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase,
-                                       fixedPrice, recurringPrice, currency,
-                                       billingPeriod, billCycleDay,
-                                       description, totalOrdering, type, tz);
+    protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription, final SubscriptionBaseTransitionType type)  {
+        try {
+
+            final Integer billCycleDay = 1;
+            final Plan plan = new MockPlan();
+            final Currency currency = Currency.USD;
+            final String description = "";
+            final BillingPeriod billingPeriod = BillingPeriod.MONTHLY;
+            final Long totalOrdering = 0L;
+            final DateTimeZone tz = DateTimeZone.UTC;
+
+
+            final PlanPhase planPhase = Mockito.mock(PlanPhase.class);
+
+            final InternationalPrice recurringPrice = Mockito.mock(InternationalPrice.class);
+
+            Mockito.when(recurringPrice.getPrice(Mockito.<Currency>any())).thenReturn(BigDecimal.TEN);
+            final Recurring recurring = Mockito.mock(Recurring.class);
+            Mockito.when(recurring.getRecurringPrice()).thenReturn(recurringPrice);
+            Mockito.when(planPhase.getRecurring()).thenReturn(recurring);
+
+            final BigDecimal fixedPrice = BigDecimal.TEN;
+
+            return new DefaultBillingEvent(subscription, effectiveDate, true, plan, planPhase, fixedPrice,
+                                           currency,
+                                           billingPeriod, billCycleDay,
+                                           description, totalOrdering, type, tz, null, false);
+
+        } catch (final CatalogApiException e) {
+            Assert.fail("", e);
+        }
+        throw new IllegalStateException();
     }
 
     @Test(groups = "fast")
@@ -569,17 +588,17 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testCreateNewDisableEvent() {
+    public void testCreateNewDisableEvent() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final BillingEvent event = new MockBillingEvent();
 
-        final BillingEvent result = blockingCalculator.createNewDisableEvent(now, event);
+        final BillingEvent result = blockingCalculator.createNewDisableEvent(now, event, null, internalCallContext);
         assertEquals(result.getBillCycleDayLocal(), event.getBillCycleDayLocal());
         assertEquals(result.getEffectiveDate(), now);
         assertEquals(result.getPlanPhase(), event.getPlanPhase());
         assertEquals(result.getPlan(), event.getPlan());
         assertNull(result.getFixedPrice());
-        assertNull(result.getRecurringPrice());
+        assertNull(result.getRecurringPrice(null));
         assertEquals(result.getCurrency(), event.getCurrency());
         assertEquals(result.getDescription(), "");
         assertEquals(result.getBillingPeriod(), BillingPeriod.NO_BILLING_PERIOD);
@@ -589,17 +608,17 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     }
 
     @Test(groups = "fast")
-    public void testCreateNewReenableEvent() {
+    public void testCreateNewReenableEvent() throws CatalogApiException {
         final DateTime now = clock.getUTCNow();
         final BillingEvent event = new MockBillingEvent();
 
-        final BillingEvent result = blockingCalculator.createNewReenableEvent(now, event);
+        final BillingEvent result = blockingCalculator.createNewReenableEvent(now, event, null, internalCallContext);
         assertEquals(result.getBillCycleDayLocal(), event.getBillCycleDayLocal());
         assertEquals(result.getEffectiveDate(), now);
         assertEquals(result.getPlanPhase(), event.getPlanPhase());
         assertEquals(result.getPlan(), event.getPlan());
         assertEquals(result.getFixedPrice(), event.getFixedPrice());
-        assertEquals(result.getRecurringPrice(), event.getRecurringPrice());
+        assertEquals(result.getRecurringPrice(null), event.getRecurringPrice(null));
         assertEquals(result.getCurrency(), event.getCurrency());
         assertEquals(result.getDescription(), "");
         assertEquals(result.getBillingPeriod(), event.getBillingPeriod());
@@ -611,8 +630,8 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     private class MockBillingEvent extends DefaultBillingEvent {
 
         public MockBillingEvent() {
-            super(subscription1, clock.getUTCNow(), true, null, null, BigDecimal.ZERO, BigDecimal.TEN, Currency.USD, BillingPeriod.ANNUAL,
-                  4, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
+            super(subscription1, clock.getUTCNow(), true, null, null, BigDecimal.ZERO, Currency.USD, BillingPeriod.ANNUAL,
+                  4, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC, null, false);
         }
     }
 
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
index aa47d70..98252a9 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultBillingEvent.java
@@ -136,7 +136,7 @@ public class TestDefaultBillingEvent extends JunctionTestSuiteNoDB {
     public void testEventTotalOrdering() {
         final BillingEvent event0 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CREATE, 1L);
         final BillingEvent event1 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CANCEL, 2L);
-        final BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.RE_CREATE, 3L);
+        final BillingEvent event2 = createEvent(subscription(ID_ZERO), new DateTime("2012-01-01T00:02:04.000Z"), SubscriptionBaseTransitionType.CANCEL, 3L);
 
         final SortedSet<BillingEvent> set = new TreeSet<BillingEvent>();
         set.add(event2);
@@ -187,9 +187,9 @@ public class TestDefaultBillingEvent extends JunctionTestSuiteNoDB {
 
         final Account account = new MockAccountBuilder().build();
         return new DefaultBillingEvent(sub, effectiveDate, true,
-                                       shotgun, shotgunMonthly,
-                                       BigDecimal.ZERO, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, billCycleDay,
-                                       "Test Event 1", totalOrdering, type, DateTimeZone.UTC);
+                                       shotgun, shotgunMonthly, BigDecimal.ZERO,
+                                       Currency.USD, BillingPeriod.NO_BILLING_PERIOD, billCycleDay,
+                                       "Test Event 1", totalOrdering, type, DateTimeZone.UTC, null, false);
     }
 
     private MockPlanPhase createMockMonthlyPlanPhase(@Nullable final BigDecimal recurringRate,
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
index d79b19d..f72fb2f 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -56,9 +56,9 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
         assertListenerStatus();
 
@@ -192,9 +192,9 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
 
         final Account account = createAccount(getAccountData(7));
 
-        testListener.pushExpectedEvent(NextEvent.CREATE);
-        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
-        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
         final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
         assertListenerStatus();
 

NEWS 29(+26 -3)

diff --git a/NEWS b/NEWS
index 614e626..bafdcf0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,29 @@
-0.16.9
-    Fix issues with payment transaction external keys (make sure transaction external keys aren't shared across payments)
-    Fix #613
+0.17.8
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.8
+
+0.17.7
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.7
+
+0.17.6
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.6
+
+0.17.5
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.5
+
+0.17.4
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.4
+
+0.17.3
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.3
+
+0.17.2
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.2
+
+0.17.1
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.1
+
+0.17.0
+    See https://github.com/killbill/killbill/releases/tag/killbill-0.17.0
 
 0.16.8
     See https://github.com/killbill/killbill/releases/tag/killbill-0.16.8

overdue/pom.xml 33(+32 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 9e2792a..2abb00d 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>
@@ -54,6 +54,21 @@
             <artifactId>jmustache</artifactId>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -63,6 +78,11 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi</artifactId>
         </dependency>
@@ -131,10 +151,21 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-locker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-queue</artifactId>
         </dependency>
         <dependency>
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
index a0294f3..05a52c9 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
@@ -17,29 +17,43 @@
 
 package org.killbill.billing.overdue.api;
 
+import java.util.UUID;
+
 import javax.inject.Inject;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.junction.BlockingInternalApi;
+import org.killbill.billing.overdue.OverdueService;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
+import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.config.api.OverdueStateSet;
+import org.killbill.billing.overdue.wrapper.OverdueWrapper;
 import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.xmlloader.XMLWriter;
 
 public class DefaultOverdueApi implements OverdueApi {
 
     private final OverdueConfigCache overdueConfigCache;
+    private final BlockingInternalApi blockingInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
     private final TenantUserApi tenantApi;
 
     @Inject
     public DefaultOverdueApi(final OverdueConfigCache overdueConfigCache,
                              final TenantUserApi tenantApi,
+                             final BlockingInternalApi blockingInternalApi,
                              final InternalCallContextFactory internalCallContextFactory) {
         this.overdueConfigCache = overdueConfigCache;
         this.tenantApi = tenantApi;
+        this.blockingInternalApi = blockingInternalApi;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
@@ -64,8 +78,28 @@ public class DefaultOverdueApi implements OverdueApi {
         }
     }
 
+    @Override
+    public void uploadOverdueConfig(final OverdueConfig overdueConfig, final CallContext callContext) throws OverdueApiException {
+        try {
+            final String overdueXML = XMLWriter.writeXML((DefaultOverdueConfig) overdueConfig, DefaultOverdueConfig.class);
+            uploadOverdueConfig(overdueXML, callContext);
+        } catch (final Exception e) {
+            throw new OverdueApiException(ErrorCode.OVERDUE_INVALID_FOR_TENANT, callContext.getTenantId());
+        }
+    }
+
+    @Override
+    public OverdueState getOverdueStateFor(final UUID accountId, final TenantContext tenantContext) throws OverdueApiException {
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+        final BlockingState blockingStateForService = blockingInternalApi.getBlockingStateForService(accountId, BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, internalTenantContext);
+        final String stateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
+        final OverdueConfig overdueConfig = overdueConfigCache.getOverdueConfig(internalTenantContext);
+        final OverdueStateSet states = ((DefaultOverdueConfig) overdueConfig).getOverdueStatesAccount();
+        return states.findState(stateName);
+    }
+
     private InternalTenantContext createInternalTenantContext(final TenantContext tenantContext) {
-        // Only tenantRecordId will be populated-- this is important to always create the (ehcache) key the same way
-        return internalCallContextFactory.createInternalTenantContext(tenantContext);
+        // Only tenantRecordId will be populated -- this is important to always create the (ehcache) key the same way
+        return internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext);
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index 22d0865..2d01711 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -86,7 +86,6 @@ public class OverdueStateApplicator {
     private static final Logger log = LoggerFactory.getLogger(OverdueStateApplicator.class);
 
     private final BlockingInternalApi blockingApi;
-    private final Clock clock;
     private final OverduePoster checkPoster;
     private final PersistentBus bus;
     private final AccountInternalApi accountApi;
@@ -102,7 +101,6 @@ public class OverdueStateApplicator {
                                   final AccountInternalApi accountApi,
                                   final EntitlementApi entitlementApi,
                                   final EntitlementInternalApi entitlementInternalApi,
-                                  final Clock clock,
                                   @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_CHECK_NAMED) final OverduePoster checkPoster,
                                   final OverdueEmailGenerator overdueEmailGenerator,
                                   final EmailConfig config,
@@ -114,7 +112,6 @@ public class OverdueStateApplicator {
         this.accountApi = accountApi;
         this.entitlementApi = entitlementApi;
         this.entitlementInternalApi = entitlementInternalApi;
-        this.clock = clock;
         this.checkPoster = checkPoster;
         this.overdueEmailGenerator = overdueEmailGenerator;
         this.tagApi = tagApi;
@@ -123,7 +120,7 @@ public class OverdueStateApplicator {
         this.bus = bus;
     }
 
-    public void apply(final OverdueStateSet overdueStateSet, final BillingState billingState,
+    public void apply(final DateTime effectiveDate, final OverdueStateSet overdueStateSet, final BillingState billingState,
                       final ImmutableAccountData account, final OverdueState previousOverdueState,
                       final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException, OverdueApiException {
         try {
@@ -132,7 +129,7 @@ public class OverdueStateApplicator {
                 return;
             }
 
-            log.debug("OverdueStateApplicator: time={}, previousState={}, nextState={}, billingState={}", clock.getUTCNow(), previousOverdueState, nextOverdueState, billingState);
+            log.debug("OverdueStateApplicator: time={}, previousState={}, nextState={}, billingState={}", effectiveDate, previousOverdueState, nextOverdueState, billingState);
 
             final OverdueState firstOverdueState = overdueStateSet.getFirstState();
             final boolean conditionForNextNotfication = !nextOverdueState.isClearState() ||
@@ -145,8 +142,8 @@ public class OverdueStateApplicator {
                 if (reevaluationInterval == null) {
                     log.debug("OverdueStateApplicator <notificationQ>: missing InitialReevaluationInterval from config, NOT inserting notification for account {}", account.getId());
                 } else {
-                    log.debug("OverdueStateApplicator <notificationQ>: inserting notification for account={}, time={}", account.getId(), clock.getUTCNow().plus(reevaluationInterval));
-                    createFutureNotification(account, clock.getUTCNow().plus(reevaluationInterval), context);
+                    log.debug("OverdueStateApplicator <notificationQ>: inserting notification for account={}, time={}", account.getId(), effectiveDate.plus(reevaluationInterval));
+                    createFutureNotification(account, effectiveDate.plus(reevaluationInterval), context);
                 }
             } else if (nextOverdueState.isClearState()) {
                 clearFutureNotification(account, context);
@@ -157,7 +154,7 @@ public class OverdueStateApplicator {
                 return;
             }
 
-            cancelSubscriptionsIfRequired(account, nextOverdueState, context);
+            cancelSubscriptionsIfRequired(effectiveDate, account, nextOverdueState, context);
 
             sendEmailIfRequired(account.getId(), billingState, nextOverdueState, context);
 
@@ -166,7 +163,7 @@ public class OverdueStateApplicator {
             // Make sure to store the new state last here: the entitlement DAO will send a BlockingTransitionInternalEvent
             // on the bus to which invoice will react. We need the latest state (including AUTO_INVOICE_OFF tag for example)
             // to be present in the database first.
-            storeNewState(account, nextOverdueState, context);
+            storeNewState(effectiveDate, account, nextOverdueState, context);
         } catch (final AccountApiException e) {
             throw new OverdueException(e);
         }
@@ -212,11 +209,11 @@ public class OverdueStateApplicator {
         }
     }
 
-    public void clear(final ImmutableAccountData account, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
+    public void clear(final DateTime effectiveDate, final ImmutableAccountData account, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
 
-        log.debug("OverdueStateApplicator:clear : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueState.getName());
+        log.debug("OverdueStateApplicator:clear : time = " + effectiveDate + ", previousState = " + previousOverdueState.getName());
 
-        storeNewState(account, clearState, context);
+        storeNewState(effectiveDate, account, clearState, context);
 
         clearFutureNotification(account, context);
 
@@ -248,7 +245,7 @@ public class OverdueStateApplicator {
                                              context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
     }
 
-    protected void storeNewState(final ImmutableAccountData blockable, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
+    protected void storeNewState(final DateTime effectiveDate, final ImmutableAccountData blockable, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
         try {
             blockingApi.setBlockingState(new DefaultBlockingState(blockable.getId(),
                                                                   BlockingStateType.ACCOUNT,
@@ -257,7 +254,7 @@ public class OverdueStateApplicator {
                                                                   blockChanges(nextOverdueState),
                                                                   blockEntitlement(nextOverdueState),
                                                                   blockBilling(nextOverdueState),
-                                                                  clock.getUTCNow()),
+                                                                  effectiveDate),
                                          context);
         } catch (final Exception e) {
             throw new OverdueException(e, ErrorCode.OVERDUE_CAT_ERROR_ENCOUNTERED, blockable.getId(), blockable.getClass().getName());
@@ -291,7 +288,7 @@ public class OverdueStateApplicator {
     }
 
     private boolean blockChanges(final OverdueState nextOverdueState) {
-        return nextOverdueState.isBlockChanges();
+        return nextOverdueState.isBlockChanges() || nextOverdueState.isDisableEntitlementAndChangesBlocked();
     }
 
     private boolean blockBilling(final OverdueState nextOverdueState) {
@@ -312,7 +309,7 @@ public class OverdueStateApplicator {
         checkPoster.clearOverdueCheckNotifications(account.getId(), OverdueCheckNotifier.OVERDUE_CHECK_NOTIFIER_QUEUE, OverdueCheckNotificationKey.class, context);
     }
 
-    private void cancelSubscriptionsIfRequired(final ImmutableAccountData account, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
+    private void cancelSubscriptionsIfRequired(final DateTime effectiveDate, final ImmutableAccountData account, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
         if (nextOverdueState.getOverdueCancellationPolicy() == OverdueCancellationPolicy.NONE) {
             return;
         }
@@ -334,7 +331,7 @@ public class OverdueStateApplicator {
             computeEntitlementsToCancel(account, toBeCancelled, callContext);
 
             try {
-                entitlementInternalApi.cancel(toBeCancelled, clock.getToday(account.getTimeZone()), actionPolicy, ImmutableList.<PluginProperty>of(), context);
+                entitlementInternalApi.cancel(toBeCancelled, context.toLocalDate(effectiveDate), actionPolicy, ImmutableList.<PluginProperty>of(), context);
             } catch (final EntitlementApiException e) {
                 throw new OverdueException(e);
             }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java b/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
index 7d594fc..d1bec8d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/calculator/BillingStateCalculator.java
@@ -19,6 +19,7 @@ package org.killbill.billing.overdue.calculator;
 import java.math.BigDecimal;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.SortedSet;
 import java.util.TreeSet;
@@ -26,6 +27,7 @@ import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.invoice.api.Invoice;
@@ -33,6 +35,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.overdue.config.api.BillingState;
 import org.killbill.billing.overdue.config.api.OverdueException;
 import org.killbill.billing.payment.api.PaymentResponse;
+import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
 
@@ -41,6 +44,7 @@ import com.google.inject.Inject;
 public class BillingStateCalculator {
 
     private final InvoiceInternalApi invoiceApi;
+    private final TagInternalApi tagApi;
     private final Clock clock;
 
     protected class InvoiceDateComparator implements Comparator<Invoice> {
@@ -57,9 +61,10 @@ public class BillingStateCalculator {
     }
 
     @Inject
-    public BillingStateCalculator(final InvoiceInternalApi invoiceApi, final Clock clock) {
+    public BillingStateCalculator(final InvoiceInternalApi invoiceApi, final Clock clock, final TagInternalApi tagApi) {
         this.invoiceApi = invoiceApi;
         this.clock = clock;
+        this.tagApi = tagApi;
     }
 
     public BillingState calculateBillingState(final ImmutableAccountData account, final InternalTenantContext context) throws OverdueException {
@@ -75,7 +80,8 @@ public class BillingStateCalculator {
             idOfEarliestUnpaidInvoice = invoice.getId();
         }
         final PaymentResponse responseForLastFailedPayment = PaymentResponse.INSUFFICIENT_FUNDS; //TODO MDW
-        final Tag[] tags = new Tag[]{}; //TODO MDW
+        final List<Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, context);
+        final Tag[] tags = accountTags.toArray(new Tag[accountTags.size()]);
 
         return new BillingState(account.getId(), numberOfUnpaidInvoices, unpaidInvoiceBalance, dateOfEarliestUnpaidInvoice, account.getTimeZone(), idOfEarliestUnpaidInvoice, responseForLastFailedPayment, tags);
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
index 5528e14..49396ea 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
@@ -23,6 +23,7 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.joda.time.Period;
 
 import org.killbill.billing.catalog.api.Duration;
@@ -62,13 +63,30 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
             case YEARS:
                 return dateTime.plusYears(number);
             case UNLIMITED:
-                return dateTime.plusYears(100);
             default:
-                return dateTime;
+                throw new  IllegalStateException("Unexpected duration unit " + unit);
         }
     }
 
     @Override
+    public LocalDate addToLocalDate(final LocalDate localDate) {
+        if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
+            return localDate;
+        }
+
+        switch (unit) {
+            case DAYS:
+                return localDate.plusDays(number);
+            case MONTHS:
+                return localDate.plusMonths(number);
+            case YEARS:
+                return localDate.plusYears(number);
+            case UNLIMITED:
+            default:
+                throw new  IllegalStateException("Unexpected duration unit " + unit);
+        }
+    }
+    @Override
     public Period toJodaPeriod() {
         if ((number == null) && (unit != TimeUnit.UNLIMITED)) {
             return new Period();
@@ -82,9 +100,8 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
             case YEARS:
                 return new Period().withYears(number);
             case UNLIMITED:
-                return new Period().withYears(100);
             default:
-                return new Period();
+                throw new  IllegalStateException("Unexpected duration unit " + unit);
         }
     }
 
@@ -94,12 +111,12 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
         return errors;
     }
 
-    protected DefaultDuration setUnit(final TimeUnit unit) {
+    public DefaultDuration setUnit(final TimeUnit unit) {
         this.unit = unit;
         return this;
     }
 
-    protected DefaultDuration setNumber(final Integer number) {
+    public DefaultDuration setNumber(final Integer number) {
         this.number = number;
         return this;
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
index ee30861..a4e4b51 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
@@ -57,8 +57,11 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
     @XmlElement(required = false, name = "response")
     private PaymentResponse[] responseForLastFailedPayment;
 
-    @XmlElement(required = false, name = "controlTag")
-    private ControlTagType controlTag;
+    @XmlElement(required = false, name = "controlTagInclusion")
+    private ControlTagType controlTagInclusion;
+
+    @XmlElement(required = false, name = "controlTagExclusion")
+    private ControlTagType controlTagExclusion;
 
     @Override
     public boolean evaluate(final BillingState state, final LocalDate date) {
@@ -73,7 +76,8 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
                 (timeSinceEarliestUnpaidInvoiceEqualsOrExceeds == null ||
                  (unpaidInvoiceTriggerDate != null && !unpaidInvoiceTriggerDate.isAfter(date))) &&
                 (responseForLastFailedPayment == null || responseIsIn(state.getResponseForLastFailedPayment(), responseForLastFailedPayment)) &&
-                (controlTag == null || isTagIn(controlTag, state.getTags()));
+                (controlTagInclusion == null || isTagIn(controlTagInclusion, state.getTags())) &&
+                (controlTagExclusion == null || isTagNotIn(controlTagExclusion, state.getTags()));
     }
 
     private boolean responseIsIn(final PaymentResponse actualResponse,
@@ -95,6 +99,15 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
         return false;
     }
 
+    private boolean isTagNotIn(final ControlTagType tagType, final Tag[] tags) {
+        for (final Tag t : tags) {
+            if (t.getTagDefinitionId().equals(tagType.getId())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Override
     public ValidationErrors validate(final DefaultOverdueConfig root,
                                      final ValidationErrors errors) {
@@ -135,8 +148,37 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
     }
 
     @Override
-    public ControlTagType getControlTagType() {
-        return controlTag;
+    public ControlTagType getInclusionControlTagType() {
+        return controlTagInclusion;
+    }
+
+    @Override
+    public ControlTagType getExclusionControlTagType() {
+        return controlTagExclusion;
+    }
+
+    public void setNumberOfUnpaidInvoicesEqualsOrExceeds(final Integer numberOfUnpaidInvoicesEqualsOrExceeds) {
+        this.numberOfUnpaidInvoicesEqualsOrExceeds = numberOfUnpaidInvoicesEqualsOrExceeds;
+    }
+
+    public void setTotalUnpaidInvoiceBalanceEqualsOrExceeds(final BigDecimal totalUnpaidInvoiceBalanceEqualsOrExceeds) {
+        this.totalUnpaidInvoiceBalanceEqualsOrExceeds = totalUnpaidInvoiceBalanceEqualsOrExceeds;
+    }
+
+    public void setTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds(final DefaultDuration timeSinceEarliestUnpaidInvoiceEqualsOrExceeds) {
+        this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+    }
+
+    public void setResponseForLastFailedPayment(final PaymentResponse[] responseForLastFailedPayment) {
+        this.responseForLastFailedPayment = responseForLastFailedPayment;
+    }
+
+    public void setControlTagInclusion(final ControlTagType controlTagInclusion) {
+        this.controlTagInclusion = controlTagInclusion;
+    }
+
+    public void setControlTagExclusion(final ControlTagType controlTagExclusion) {
+        this.controlTagExclusion = controlTagExclusion;
     }
 
     @Override
@@ -146,7 +188,8 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
         sb.append(", totalUnpaidInvoiceBalanceEqualsOrExceeds=").append(totalUnpaidInvoiceBalanceEqualsOrExceeds);
         sb.append(", timeSinceEarliestUnpaidInvoiceEqualsOrExceeds=").append(timeSinceEarliestUnpaidInvoiceEqualsOrExceeds);
         sb.append(", responseForLastFailedPayment=").append(Arrays.toString(responseForLastFailedPayment));
-        sb.append(", controlTag=").append(controlTag);
+        sb.append(", controlTagInclusion=").append(controlTagInclusion);
+        sb.append(", controlTagExclusion=").append(controlTagExclusion);
         sb.append('}');
         return sb.toString();
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
index 9d33ddd..1068745 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
@@ -98,7 +98,7 @@ public class DefaultOverdueState extends ValidatingConfig<DefaultOverdueConfig> 
 
     @Override
     public boolean isBlockChanges() {
-        return blockChanges || disableEntitlement;
+        return blockChanges;
     }
 
     @Override
@@ -119,22 +119,26 @@ public class DefaultOverdueState extends ValidatingConfig<DefaultOverdueConfig> 
         return autoReevaluationInterval.toJodaPeriod();
     }
 
-    protected DefaultOverdueState setName(final String name) {
+    public void setAutoReevaluationInterval(final DefaultDuration autoReevaluationInterval) {
+        this.autoReevaluationInterval = autoReevaluationInterval;
+    }
+
+    public DefaultOverdueState setName(final String name) {
         this.name = name;
         return this;
     }
 
-    protected DefaultOverdueState setClearState(final boolean isClearState) {
+    public DefaultOverdueState setClearState(final boolean isClearState) {
         this.isClearState = isClearState;
         return this;
     }
 
-    protected DefaultOverdueState setExternalMessage(final String externalMessage) {
+    public DefaultOverdueState setExternalMessage(final String externalMessage) {
         this.externalMessage = externalMessage;
         return this;
     }
 
-    protected DefaultOverdueState setDisableEntitlement(final boolean cancel) {
+    public DefaultOverdueState setDisableEntitlement(final boolean cancel) {
         this.disableEntitlement = cancel;
         return this;
     }
@@ -144,12 +148,12 @@ public class DefaultOverdueState extends ValidatingConfig<DefaultOverdueConfig> 
         return this;
     }
 
-    protected DefaultOverdueState setBlockChanges(final boolean cancel) {
+    public DefaultOverdueState setBlockChanges(final boolean cancel) {
         this.blockChanges = cancel;
         return this;
     }
 
-    protected DefaultOverdueState setCondition(final DefaultOverdueCondition condition) {
+    public DefaultOverdueState setCondition(final DefaultOverdueCondition condition) {
         this.condition = condition;
         return this;
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
index 0bd4e63..004fd2d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
@@ -45,12 +45,12 @@ public class DefaultOverdueStatesAccount extends DefaultOverdueStateSet implemen
         return initialReevaluationInterval.toJodaPeriod();
     }
 
-    protected DefaultOverdueStatesAccount setAccountOverdueStates(final DefaultOverdueState[] accountOverdueStates) {
+    public DefaultOverdueStatesAccount setAccountOverdueStates(final DefaultOverdueState[] accountOverdueStates) {
         this.accountOverdueStates = accountOverdueStates;
         return this;
     }
 
-    protected DefaultOverdueStatesAccount setInitialReevaluationInterval(final DefaultDuration initialReevaluationInterval) {
+    public DefaultOverdueStatesAccount setInitialReevaluationInterval(final DefaultDuration initialReevaluationInterval) {
         this.initialReevaluationInterval = initialReevaluationInterval;
         return this;
     }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
index eb71506..af4a0cd 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStateSet.java
@@ -48,7 +48,7 @@ public abstract class DefaultOverdueStateSet extends ValidatingConfig<DefaultOve
                 return state;
             }
         }
-        throw new OverdueApiException(ErrorCode.CAT_NO_SUCH_OVEDUE_STATE, stateName);
+        throw new OverdueApiException(ErrorCode.CAT_NO_SUCH_OVERDUE_STATE, stateName);
     }
 
     /* (non-Javadoc)
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
index 9c613e5..930c774 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
@@ -19,11 +19,9 @@
 package org.killbill.billing.overdue.glue;
 
 import org.killbill.billing.glue.OverdueModule;
-import org.killbill.billing.overdue.OverdueInternalApi;
 import org.killbill.billing.overdue.OverdueProperties;
 import org.killbill.billing.overdue.OverdueService;
 import org.killbill.billing.overdue.api.DefaultOverdueApi;
-import org.killbill.billing.overdue.api.DefaultOverdueInternalApi;
 import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.applicator.OverdueEmailGenerator;
 import org.killbill.billing.overdue.applicator.formatters.DefaultOverdueEmailFormatterFactory;
@@ -95,7 +93,6 @@ public class DefaultOverdueModule extends KillBillModule implements OverdueModul
 
     @Override
     public void installOverdueUserApi() {
-        bind(OverdueInternalApi.class).to(DefaultOverdueInternalApi.class).asEagerSingleton();
         bind(OverdueApi.class).to(DefaultOverdueApi.class).asEagerSingleton();
     }
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
index c631b72..461539e 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueDispatcher.java
@@ -18,6 +18,7 @@ package org.killbill.billing.overdue.listener;
 
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,25 +39,25 @@ public class OverdueDispatcher {
         this.factory = factory;
     }
 
-    public void processOverdueForAccount(final UUID accountId, final InternalCallContext context) {
-        processOverdue(accountId, context);
+    public void processOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
+        processOverdue(accountId, effectiveDate, context);
     }
 
-    public void clearOverdueForAccount(final UUID accountId, final InternalCallContext context) {
-        clearOverdue(accountId, context);
+    public void clearOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
+        clearOverdue(accountId, effectiveDate, context);
     }
 
-    private void processOverdue(final UUID accountId, final InternalCallContext context) {
+    private void processOverdue(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
         try {
-            factory.createOverdueWrapperFor(accountId, context).refresh(context);
+            factory.createOverdueWrapperFor(accountId, context).refresh(effectiveDate, context);
         } catch (BillingExceptionBase e) {
             log.warn("Error processing Overdue for accountId='{}'", accountId, e);
         }
     }
 
-    private void clearOverdue(final UUID accountId, final InternalCallContext context) {
+    private void clearOverdue(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
         try {
-            factory.createOverdueWrapperFor(accountId, context).clear(context);
+            factory.createOverdueWrapperFor(accountId, context).clear(effectiveDate, context);
         } catch (BillingExceptionBase e) {
             log.warn("Error processing Overdue for accountId='{}'", accountId, e);
         }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
index 47b4071..a0abc46 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/listener/OverdueListener.java
@@ -18,16 +18,31 @@
 
 package org.killbill.billing.overdue.listener;
 
+import java.util.List;
 import java.util.UUID;
 
+import javax.inject.Named;
+
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.events.ControlTagCreationInternalEvent;
 import org.killbill.billing.events.ControlTagDeletionInternalEvent;
 import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
 import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.InvoicePaymentInfoInternalEvent;
-import org.killbill.billing.overdue.OverdueInternalApi;
+import org.killbill.billing.overdue.api.OverdueApiException;
+import org.killbill.billing.overdue.api.OverdueConfig;
+import org.killbill.billing.overdue.caching.OverdueConfigCache;
+import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.config.DefaultOverdueState;
+import org.killbill.billing.overdue.glue.DefaultOverdueModule;
+import org.killbill.billing.overdue.notification.OverdueAsyncBusNotificationKey;
+import org.killbill.billing.overdue.notification.OverdueAsyncBusNotificationKey.OverdueAsyncBusNotificationAction;
+import org.killbill.billing.overdue.notification.OverdueAsyncBusNotifier;
+import org.killbill.billing.overdue.notification.OverduePoster;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallOrigin;
@@ -36,6 +51,7 @@ import org.killbill.billing.util.callcontext.UserType;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.bus.api.BusEvent;
+import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,39 +63,43 @@ public class OverdueListener {
 
     private static final Logger log = LoggerFactory.getLogger(OverdueListener.class);
 
-    private final OverdueInternalApi overdueInternalApi;
     private final InternalCallContextFactory internalCallContextFactory;
     private final CacheControllerDispatcher cacheControllerDispatcher;
-
+    private final Clock clock;
+    private final OverduePoster asyncPoster;
+    private final OverdueConfigCache overdueConfigCache;
     private final NonEntityDao nonEntityDao;
+    private final AccountInternalApi accountApi;
 
     @Inject
-    public OverdueListener(final OverdueInternalApi overdueInternalApi,
-                           final NonEntityDao nonEntityDao,
+    public OverdueListener(final NonEntityDao nonEntityDao,
                            final CacheControllerDispatcher cacheControllerDispatcher,
-                           final InternalCallContextFactory internalCallContextFactory) {
-        this.overdueInternalApi = overdueInternalApi;
+                           final Clock clock,
+                           @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_ASYNC_BUS_NAMED)  final OverduePoster asyncPoster,
+                           final OverdueConfigCache overdueConfigCache,
+                           final InternalCallContextFactory internalCallContextFactory,
+                           final AccountInternalApi accountApi) {
         this.nonEntityDao = nonEntityDao;
+        this.clock = clock;
+        this.asyncPoster = asyncPoster;
+        this.overdueConfigCache = overdueConfigCache;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.accountApi = accountApi;
     }
 
-
-
-
     @AllowConcurrentEvents
     @Subscribe
     public void handleTagInsert(final ControlTagCreationInternalEvent event) {
         if (event.getTagDefinition().getName().equals(ControlTagType.OVERDUE_ENFORCEMENT_OFF.toString()) && event.getObjectType() == ObjectType.ACCOUNT) {
-            final InternalCallContext callContext = createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2());
-            overdueInternalApi.scheduleOverdueClear(event.getObjectId(), callContext);
+            final InternalCallContext internalCallContext = createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2());
+            insertBusEventIntoNotificationQueue(event.getObjectId(), OverdueAsyncBusNotificationAction.CLEAR, internalCallContext);
         } else if (event.getTagDefinition().getName().equals(ControlTagType.WRITTEN_OFF.toString()) && event.getObjectType() == ObjectType.INVOICE) {
             final UUID accountId = nonEntityDao.retrieveIdFromObject(event.getSearchKey1(), ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.OBJECT_ID));
             insertBusEventIntoNotificationQueue(accountId, event);
         }
     }
 
-
     @AllowConcurrentEvents
     @Subscribe
     public void handleTagRemoval(final ControlTagDeletionInternalEvent event) {
@@ -113,11 +133,60 @@ public class OverdueListener {
     }
 
     private void insertBusEventIntoNotificationQueue(final UUID accountId, final BusEvent event) {
-        final InternalCallContext callContext = createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2());
-        overdueInternalApi.scheduleOverdueRefresh(accountId, callContext);
+        final InternalCallContext internalCallContext = createCallContext(event.getUserToken(), event.getSearchKey1(), event.getSearchKey2());
+        insertBusEventIntoNotificationQueue(accountId, OverdueAsyncBusNotificationAction.REFRESH, internalCallContext);
     }
 
     private InternalCallContext createCallContext(final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
         return internalCallContextFactory.createInternalCallContext(tenantRecordId, accountRecordId, "OverdueService", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
     }
+
+    private void insertBusEventIntoNotificationQueue(final UUID accountId, final OverdueAsyncBusNotificationAction action, final InternalCallContext callContext) {
+        final boolean shouldInsertNotification = shouldInsertNotification(callContext);
+
+        if (shouldInsertNotification) {
+            OverdueAsyncBusNotificationKey notificationKey = new OverdueAsyncBusNotificationKey(accountId, action);
+            asyncPoster.insertOverdueNotification(accountId, clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, callContext);
+
+            try {
+                final List<Account> childrenAccounts = accountApi.getChildrenAccounts(accountId, callContext);
+                if (childrenAccounts != null) {
+                    for (Account childAccount : childrenAccounts) {
+
+                        if (childAccount.isPaymentDelegatedToParent()) {
+                            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(childAccount.getId(), callContext);
+                            final InternalCallContext accountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), callContext);
+                            notificationKey = new OverdueAsyncBusNotificationKey(childAccount.getId(), action);
+                            asyncPoster.insertOverdueNotification(childAccount.getId(), clock.getUTCNow(), OverdueAsyncBusNotifier.OVERDUE_ASYNC_BUS_NOTIFIER_QUEUE, notificationKey, accountContext);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error("Error loading child accounts from account " + accountId);
+            }
+
+        }
+    }
+
+    // Optimization: don't bother running the Overdue machinery if it's disabled
+    private boolean shouldInsertNotification(final InternalTenantContext internalTenantContext) {
+        OverdueConfig overdueConfig;
+        try {
+            overdueConfig = overdueConfigCache.getOverdueConfig(internalTenantContext);
+        } catch (final OverdueApiException e) {
+            log.warn("Failed to extract overdue config for tenant " + internalTenantContext.getTenantRecordId());
+            overdueConfig = null;
+        }
+        if (overdueConfig == null || overdueConfig.getOverdueStatesAccount() == null || overdueConfig.getOverdueStatesAccount().getStates() == null) {
+            return false;
+        }
+
+        for (final DefaultOverdueState state : ((DefaultOverdueConfig) overdueConfig).getOverdueStatesAccount().getStates()) {
+            if (state.getConditionEvaluation() != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
index be98671..4d90ec1 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueAsyncBusNotifier.java
@@ -61,10 +61,10 @@ public class OverdueAsyncBusNotifier extends DefaultOverdueNotifierBase implemen
             final OverdueAsyncBusNotificationKey key = (OverdueAsyncBusNotificationKey) notificationKey;
             switch (key.getAction()) {
                 case CLEAR:
-                    dispatcher.clearOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+                    dispatcher.clearOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
                     break;
                 case REFRESH:
-                    dispatcher.processOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+                    dispatcher.processOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
                     break;
                 default:
                     throw new RuntimeException("Unexpected action " + key.getAction() + " for account " + key.getUuidKey());
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
index bd644dd..6fdb77d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/notification/OverdueCheckNotifier.java
@@ -59,7 +59,7 @@ public class OverdueCheckNotifier extends DefaultOverdueNotifierBase implements 
             }
 
             final OverdueCheckNotificationKey key = (OverdueCheckNotificationKey) notificationKey;
-            dispatcher.processOverdueForAccount(key.getUuidKey(), createCallContext(userToken, accountRecordId, tenantRecordId));
+            dispatcher.processOverdueForAccount(key.getUuidKey(), eventDate, createCallContext(userToken, accountRecordId, tenantRecordId));
         } catch (IllegalArgumentException e) {
             log.error("The key returned from the NextBillingNotificationQueue is not a valid UUID", e);
         }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/OverdueProperties.java b/overdue/src/main/java/org/killbill/billing/overdue/OverdueProperties.java
index 0957140..4b59b8b 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/OverdueProperties.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/OverdueProperties.java
@@ -20,7 +20,7 @@ import org.skife.config.Config;
 import org.skife.config.Default;
 import org.skife.config.Description;
 
-import org.killbill.billing.util.config.KillbillConfig;
+import org.killbill.billing.util.config.definition.KillbillConfig;
 
 public interface OverdueProperties extends KillbillConfig {
 
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
index 7743e65..0c1e151 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapper.java
@@ -18,6 +18,7 @@
 
 package org.killbill.billing.overdue.wrapper;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -32,6 +33,7 @@ import org.killbill.billing.overdue.calculator.BillingStateCalculator;
 import org.killbill.billing.overdue.config.api.BillingState;
 import org.killbill.billing.overdue.config.api.OverdueException;
 import org.killbill.billing.overdue.config.api.OverdueStateSet;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLock;
@@ -40,8 +42,6 @@ import org.killbill.commons.locker.LockFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.MoreObjects;
-
 public class OverdueWrapper {
 
 
@@ -59,6 +59,7 @@ public class OverdueWrapper {
     private final OverdueStateSet overdueStateSet;
     private final BillingStateCalculator billingStateCalcuator;
     private final OverdueStateApplicator overdueStateApplicator;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     public OverdueWrapper(final ImmutableAccountData overdueable,
                           final BlockingInternalApi api,
@@ -66,7 +67,8 @@ public class OverdueWrapper {
                           final GlobalLocker locker,
                           final Clock clock,
                           final BillingStateCalculator billingStateCalcuator,
-                          final OverdueStateApplicator overdueStateApplicator) {
+                          final OverdueStateApplicator overdueStateApplicator,
+                          final InternalCallContextFactory internalCallContextFactory) {
         this.overdueable = overdueable;
         this.overdueStateSet = overdueStateSet;
         this.api = api;
@@ -74,9 +76,10 @@ public class OverdueWrapper {
         this.clock = clock;
         this.billingStateCalcuator = billingStateCalcuator;
         this.overdueStateApplicator = overdueStateApplicator;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    public OverdueState refresh(final InternalCallContext context) throws OverdueException, OverdueApiException {
+    public OverdueState refresh(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
         if (overdueStateSet.size() < 1) { // No configuration available
             return overdueStateSet.getClearState();
         }
@@ -85,7 +88,7 @@ public class OverdueWrapper {
         try {
             lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES);
 
-            return refreshWithLock(context);
+            return refreshWithLock(effectiveDate, context);
         } catch (final LockFailedException e) {
             log.warn("Failed to process overdue for accountId='{}'", overdueable.getId(), e);
         } finally {
@@ -96,24 +99,24 @@ public class OverdueWrapper {
         return null;
     }
 
-    private OverdueState refreshWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
+    private OverdueState refreshWithLock(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
         final BillingState billingState = billingState(context);
         final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
         final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
         final OverdueState currentOverdueState = overdueStateSet.findState(previousOverdueStateName);
         final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
 
-        overdueStateApplicator.apply(overdueStateSet, billingState, overdueable, currentOverdueState, nextOverdueState, context);
+        overdueStateApplicator.apply(effectiveDate, overdueStateSet, billingState, overdueable, currentOverdueState, nextOverdueState, context);
 
         return nextOverdueState;
     }
 
-    public void clear(final InternalCallContext context) throws OverdueException, OverdueApiException {
+    public void clear(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
         GlobalLock lock = null;
         try {
             lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES);
 
-            clearWithLock(context);
+            clearWithLock(effectiveDate, context);
         } catch (final LockFailedException e) {
             log.warn("Failed to clear overdue for accountId='{}'", overdueable.getId(), e);
         } finally {
@@ -123,14 +126,20 @@ public class OverdueWrapper {
         }
     }
 
-    private void clearWithLock(final InternalCallContext context) throws OverdueException, OverdueApiException {
+    private void clearWithLock(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException {
         final BlockingState blockingStateForService = api.getBlockingStateForService(overdueable.getId(), BlockingStateType.ACCOUNT, OverdueService.OVERDUE_SERVICE_NAME, context);
         final String previousOverdueStateName = blockingStateForService != null ? blockingStateForService.getStateName() : OverdueWrapper.CLEAR_STATE_NAME;
         final OverdueState previousOverdueState = overdueStateSet.findState(previousOverdueStateName);
-        overdueStateApplicator.clear(overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
+        overdueStateApplicator.clear(effectiveDate, overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
     }
 
-    public BillingState billingState(final InternalTenantContext context) throws OverdueException {
+    public BillingState billingState(final InternalCallContext context) throws OverdueException {
+        if ((overdueable.getParentAccountId() != null) && (overdueable.isPaymentDelegatedToParent())) {
+            // calculate billing state from parent account
+            final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(overdueable.getParentAccountId(), context);
+            final InternalCallContext parentAccountContext = internalCallContextFactory.createInternalCallContext(internalTenantContext.getAccountRecordId(), context);
+            return billingStateCalcuator.calculateBillingState(overdueable, parentAccountContext);
+        }
         return billingStateCalcuator.calculateBillingState(overdueable, context);
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
index bec4c53..aa43f69 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/wrapper/OverdueWrapperFactory.java
@@ -36,6 +36,7 @@ import org.killbill.billing.overdue.config.DefaultOverdueState;
 import org.killbill.billing.overdue.config.DefaultOverdueStateSet;
 import org.killbill.billing.overdue.config.api.OverdueException;
 import org.killbill.billing.overdue.config.api.OverdueStateSet;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
@@ -54,6 +55,7 @@ public class OverdueWrapperFactory {
     private final GlobalLocker locker;
     private final Clock clock;
     private final OverdueConfigCache overdueConfigCache;
+    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public OverdueWrapperFactory(final BlockingInternalApi api,
@@ -62,7 +64,8 @@ public class OverdueWrapperFactory {
                                  final BillingStateCalculator billingStateCalculator,
                                  final OverdueStateApplicator overdueStateApplicatorBundle,
                                  final OverdueConfigCache overdueConfigCache,
-                                 final AccountInternalApi accountApi) {
+                                 final AccountInternalApi accountApi,
+                                 final InternalCallContextFactory internalCallContextFactory) {
         this.billingStateCalculator = billingStateCalculator;
         this.overdueStateApplicator = overdueStateApplicatorBundle;
         this.accountApi = accountApi;
@@ -70,16 +73,17 @@ public class OverdueWrapperFactory {
         this.locker = locker;
         this.clock = clock;
         this.overdueConfigCache = overdueConfigCache;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     public OverdueWrapper createOverdueWrapperFor(final ImmutableAccountData blockable, final InternalTenantContext context) throws OverdueException {
-        return new OverdueWrapper(blockable, api, getOverdueStateSet(context), locker, clock, billingStateCalculator, overdueStateApplicator);
+        return new OverdueWrapper(blockable, api, getOverdueStateSet(context), locker, clock, billingStateCalculator, overdueStateApplicator, internalCallContextFactory);
     }
 
     public OverdueWrapper createOverdueWrapperFor(final UUID id, final InternalTenantContext context) throws OverdueException {
         try {
             final ImmutableAccountData account = accountApi.getImmutableAccountDataById(id, context);
-            return new OverdueWrapper(account, api, getOverdueStateSet(context), locker, clock, billingStateCalculator, overdueStateApplicator);
+            return new OverdueWrapper(account, api, getOverdueStateSet(context), locker, clock, billingStateCalculator, overdueStateApplicator, internalCallContextFactory);
         } catch (final AccountApiException e) {
             throw new OverdueException(e);
         }
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
index 2222377..42e70ce 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -54,17 +54,17 @@ public class TestOverdueStateApplicator extends OverdueTestSuiteWithEmbeddedDB {
         OverdueState state;
 
         state = config.getOverdueStatesAccount().findState("OD1");
-        applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+        applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
         testOverdueHelper.checkStateApplied(state);
         checkBussEvent("OD1");
 
         state = config.getOverdueStatesAccount().findState("OD2");
-        applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+        applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
         testOverdueHelper.checkStateApplied(state);
         checkBussEvent("OD2");
 
         state = config.getOverdueStatesAccount().findState("OD3");
-        applicator.apply(overdueStateSet, null, account, clearState, state, internalCallContext);
+        applicator.apply(clock.getUTCNow(), overdueStateSet, null, account, clearState, state, internalCallContext);
         testOverdueHelper.checkStateApplied(state);
         checkBussEvent("OD3");
     }
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java b/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
index d8f7b9d..a5db764 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/calculator/TestBillingStateCalculator.java
@@ -60,7 +60,7 @@ public class TestBillingStateCalculator extends OverdueTestSuiteNoDB {
 
         Mockito.when(invoiceApi.getUnpaidInvoicesByAccountId(Mockito.<UUID>any(), Mockito.<LocalDate>any(), Mockito.<InternalTenantContext>any())).thenReturn(invoices);
 
-        return new BillingStateCalculator(invoiceApi, clock) {
+        return new BillingStateCalculator(invoiceApi, clock, tagInternalApi) {
             @Override
             public BillingState calculateBillingState(final ImmutableAccountData overdueable,
                                                       final InternalTenantContext context) {
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/config/TestCondition.java b/overdue/src/test/java/org/killbill/billing/overdue/config/TestCondition.java
index 1e6ee8c..99efef6 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/config/TestCondition.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/config/TestCondition.java
@@ -139,7 +139,7 @@ public class TestCondition extends OverdueTestSuiteNoDB {
     public void testHasControlTag() throws Exception {
         final String xml =
                 "<condition>" +
-                "	<controlTag>OVERDUE_ENFORCEMENT_OFF</controlTag>" +
+                "	<controlTagInclusion>OVERDUE_ENFORCEMENT_OFF</controlTagInclusion>" +
                 "</condition>";
         final InputStream is = new ByteArrayInputStream(xml.getBytes());
         final MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is, MockCondition.class);
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
index df5a469..8d603af 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
@@ -21,7 +21,6 @@ package org.killbill.billing.overdue.glue;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.api.BlockingState;
@@ -46,10 +45,9 @@ import org.killbill.billing.util.email.templates.TemplateModule;
 import org.killbill.billing.util.glue.AuditModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.MemoryGlobalLockerModule;
-import org.killbill.clock.Clock;
-import org.killbill.clock.ClockMock;
 
 import com.google.inject.name.Names;
 
@@ -65,13 +63,14 @@ public class TestOverdueModule extends DefaultOverdueModule {
 
         install(new AuditModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new CustomFieldModule(configSource));
         install(new EmailModule(configSource));
         install(new MockAccountModule(configSource));
         install(new MockEntitlementModule(configSource, new ApplicatorBlockingApi()));
         install(new MockInvoiceModule(configSource));
-        install(new MockTagModule(configSource));
+        install(new MockTagModule(configSource, true));
         install(new TemplateModule(configSource));
         install(new MockTenantModule(configSource));
         install(new MemoryGlobalLockerModule(configSource));
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
index b9da2b6..ee470c5 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/notification/TestOverdueCheckNotifier.java
@@ -53,7 +53,7 @@ public class TestOverdueCheckNotifier extends OverdueTestSuiteWithEmbeddedDB {
         }
 
         @Override
-        public void processOverdueForAccount(final UUID accountId, final InternalCallContext context) {
+        public void processOverdueForAccount(final UUID accountId, final DateTime effectiveDate, final InternalCallContext context) {
             eventCount++;
             latestAccountId = accountId;
         }
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
index 8e90891..d7d6462 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteNoDB.java
@@ -25,6 +25,7 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.lifecycle.api.BusService;
+import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.applicator.OverdueBusListenerTester;
 import org.killbill.billing.overdue.applicator.OverdueStateApplicator;
 import org.killbill.billing.overdue.caching.OverdueCacheInvalidationCallback;
@@ -36,6 +37,7 @@ import org.killbill.billing.overdue.notification.OverdueNotifier;
 import org.killbill.billing.overdue.notification.OverduePoster;
 import org.killbill.billing.overdue.service.DefaultOverdueService;
 import org.killbill.billing.overdue.wrapper.OverdueWrapperFactory;
+import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -86,7 +88,7 @@ public abstract class OverdueTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected OverdueStateApplicator applicator;
     @Inject
-    protected OverdueInternalApi overdueApi;
+    protected OverdueApi overdueApi;
     @Inject
     protected OverdueProperties overdueProperties;
     @Inject
@@ -101,6 +103,8 @@ public abstract class OverdueTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected OverdueCacheInvalidationCallback cacheInvalidationCallback;
     @Inject
     protected TenantInternalApi tenantInternalApi;
+    @Inject
+    protected TagInternalApi tagInternalApi;
 
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
index 1253410..f2b590d 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/OverdueTestSuiteWithEmbeddedDB.java
@@ -25,6 +25,7 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.lifecycle.api.BusService;
+import org.killbill.billing.overdue.api.OverdueApi;
 import org.killbill.billing.overdue.applicator.OverdueBusListenerTester;
 import org.killbill.billing.overdue.applicator.OverdueStateApplicator;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
@@ -87,7 +88,7 @@ public abstract class OverdueTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     @Inject
     protected OverdueStateApplicator applicator;
     @Inject
-    protected OverdueInternalApi overdueApi;
+    protected OverdueApi overdueApi;
     @Inject
     protected OverdueProperties overdueProperties;
     @Inject
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/TestOverdueHelper.java b/overdue/src/test/java/org/killbill/billing/overdue/TestOverdueHelper.java
index 3f09ee6..f805d5d 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/TestOverdueHelper.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/TestOverdueHelper.java
@@ -25,22 +25,24 @@ import java.util.UUID;
 
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceInternalApi;
+import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.overdue.api.OverdueState;
 import org.killbill.billing.overdue.glue.TestOverdueModule.ApplicatorBlockingApi;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
+import org.killbill.billing.tag.TagInternalApi;
+import org.killbill.billing.util.tag.Tag;
 import org.mockito.Mockito;
 import org.testng.Assert;
 
-import org.killbill.billing.account.api.AccountApiException;
-import org.killbill.billing.invoice.api.Invoice;
-import org.killbill.billing.invoice.api.InvoiceItem;
-import org.killbill.billing.entitlement.api.BlockingState;
-import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.junction.BlockingInternalApi;
-
 import com.google.inject.Inject;
 
 public class TestOverdueHelper {
@@ -79,6 +81,7 @@ public class TestOverdueHelper {
             "               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
             "                   <unit>DAYS</unit><number>30</number>" +
             "               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+            "               <controlTagInclusion>TEST</controlTagInclusion>" +
             "           </condition>" +
             "           <externalMessage>Reached OD1</externalMessage>" +
             "           <blockChanges>true</blockChanges>" +
@@ -93,14 +96,17 @@ public class TestOverdueHelper {
     private final AccountInternalApi accountInternalApi;
     private final InvoiceInternalApi invoiceInternalApi;
     private final BlockingInternalApi blockingInternalApi;
+    private final TagInternalApi tagInternalApi;
 
     @Inject
     public TestOverdueHelper(final AccountInternalApi accountInternalApi,
                              final InvoiceInternalApi invoiceInternalApi,
-                             final BlockingInternalApi blockingInternalApi) {
+                             final BlockingInternalApi blockingInternalApi,
+                             final TagInternalApi tagInternalApi) {
         this.accountInternalApi = accountInternalApi;
         this.invoiceInternalApi = invoiceInternalApi;
         this.blockingInternalApi = blockingInternalApi;
+        this.tagInternalApi = tagInternalApi;
     }
 
     public void checkStateApplied(final OverdueState state) {
@@ -138,6 +144,15 @@ public class TestOverdueHelper {
         invoices.add(invoice);
         Mockito.when(invoiceInternalApi.getUnpaidInvoicesByAccountId(Mockito.<UUID>any(), Mockito.<LocalDate>any(), Mockito.<InternalTenantContext>any())).thenReturn(invoices);
 
+        final Tag tag = Mockito.mock(Tag.class);
+        Mockito.when(tag.getObjectId()).thenReturn(accountId);
+        Mockito.when(tag.getObjectType()).thenReturn(ObjectType.ACCOUNT);
+        Mockito.when(tag.getTagDefinitionId()).thenReturn(new UUID(0, 6));
+        final List<Tag> tags = new ArrayList<Tag>();
+        tags.add(tag);
+        Mockito.when(tagInternalApi.getTags(Mockito.eq(account.getId()), Mockito.eq(ObjectType.ACCOUNT), Mockito.<InternalTenantContext>any()))
+               .thenReturn(tags);
+
         return account;
     }
 
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
index 2c22056..f495016 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/wrapper/TestOverdueWrapper.java
@@ -51,19 +51,19 @@ public class TestOverdueWrapper extends OverdueTestSuiteWithEmbeddedDB {
         state = config.getOverdueStatesAccount().findState("OD1");
         account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(31));
         wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
-        wrapper.refresh(internalCallContext);
+        wrapper.refresh(clock.getUTCNow(), internalCallContext);
         testOverdueHelper.checkStateApplied(state);
 
         state = config.getOverdueStatesAccount().findState("OD2");
         account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(41));
         wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
-        wrapper.refresh(internalCallContext);
+        wrapper.refresh(clock.getUTCNow(), internalCallContext);
         testOverdueHelper.checkStateApplied(state);
 
         state = config.getOverdueStatesAccount().findState("OD3");
         account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(51));
         wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
-        wrapper.refresh(internalCallContext);
+        wrapper.refresh(clock.getUTCNow(), internalCallContext);
         testOverdueHelper.checkStateApplied(state);
     }
 
@@ -79,7 +79,7 @@ public class TestOverdueWrapper extends OverdueTestSuiteWithEmbeddedDB {
         state = config.getOverdueStatesAccount().findState(OverdueWrapper.CLEAR_STATE_NAME);
         account = testOverdueHelper.createImmutableAccountData(clock.getUTCToday().minusDays(31));
         wrapper = overdueWrapperFactory.createOverdueWrapperFor(account, internalCallContext);
-        final OverdueState result = wrapper.refresh(internalCallContext);
+        final OverdueState result = wrapper.refresh(clock.getUTCNow(), internalCallContext);
 
         Assert.assertEquals(result.getName(), state.getName());
         Assert.assertEquals(result.isBlockChanges(), state.isBlockChanges());
diff --git a/overdue/src/test/resources/OverdueConfig3.xml b/overdue/src/test/resources/OverdueConfig3.xml
new file mode 100644
index 0000000..ddba32f
--- /dev/null
+++ b/overdue/src/test/resources/OverdueConfig3.xml
@@ -0,0 +1,71 @@
+<!--
+  ~ Copyright 2014-2016 Groupon, Inc
+  ~ Copyright 2014-2016 The Billing Project, LLC
+  ~
+  ~ The Billing Project 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.
+  -->
+
+<overdueConfig>
+    <accountOverdueStates>
+        <state name="OD4">
+            <condition>
+                <numberOfUnpaidInvoicesEqualsOrExceeds>5</numberOfUnpaidInvoicesEqualsOrExceeds>
+                <controlTagInclusion>AUTO_PAY_OFF</controlTagInclusion>
+            </condition>
+            <externalMessage>Reached OD3</externalMessage>
+            <blockChanges>true</blockChanges>
+            <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+            <autoReevaluationInterval>
+                <unit>DAYS</unit><number>5</number>
+            </autoReevaluationInterval>
+        </state>
+        <state name="OD3">
+            <condition>
+                <responseForLastFailedPaymentIn>
+                    <response>INVALID_CARD</response>
+                    <response>LOST_OR_STOLEN_CARD</response>
+                </responseForLastFailedPaymentIn>
+            </condition>
+            <externalMessage>Reached OD3</externalMessage>
+            <blockChanges>true</blockChanges>
+            <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+            <autoReevaluationInterval>
+                <unit>DAYS</unit><number>5</number>
+            </autoReevaluationInterval>
+        </state>
+        <state name="OD2">
+            <condition>
+                <totalUnpaidInvoiceBalanceEqualsOrExceeds>5.00</totalUnpaidInvoiceBalanceEqualsOrExceeds>
+            </condition>
+            <externalMessage>Reached OD2</externalMessage>
+            <blockChanges>true</blockChanges>
+            <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+            <autoReevaluationInterval>
+                <unit>DAYS</unit><number>5</number>
+            </autoReevaluationInterval>
+        </state>
+        <state name="OD1">
+            <condition>
+                <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                    <unit>DAYS</unit><number>30</number>
+                </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+            </condition>
+            <externalMessage>Reached OD1</externalMessage>
+            <blockChanges>true</blockChanges>
+            <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+            <autoReevaluationInterval>
+                <unit>DAYS</unit><number>5</number>
+            </autoReevaluationInterval>
+        </state>
+    </accountOverdueStates>
+</overdueConfig>
diff --git a/overdue/src/test/resources/OverdueConfigSchema.xsd b/overdue/src/test/resources/OverdueConfigSchema.xsd
index d8f014b..7b95f24 100644
--- a/overdue/src/test/resources/OverdueConfigSchema.xsd
+++ b/overdue/src/test/resources/OverdueConfigSchema.xsd
@@ -71,7 +71,8 @@
 </xs:sequence>
 </xs:complexType>
 </xs:element>
-<xs:element minOccurs="0" name="controlTag" type="controlTagType"/>
+<xs:element minOccurs="0" name="controlTagInclusion" type="controlTagType"/>
+<xs:element minOccurs="0" name="controlTagExclusion" type="controlTagType"/>
 </xs:sequence>
 </xs:extension>
 </xs:complexContent>

payment/pom.xml 61(+56 -5)

diff --git a/payment/pom.xml b/payment/pom.xml
index d6c8d92..07af64e 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
@@ -36,6 +36,10 @@
             <artifactId>jackson-core</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>jsr305</artifactId>
             <scope>provided</scope>
@@ -65,14 +69,46 @@
             <artifactId>compress-lzf</artifactId>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+        </dependency>
+        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.shiro</groupId>
             <artifactId>shiro-core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-account</artifactId>
             <scope>test</scope>
@@ -109,10 +145,6 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
-            <artifactId>killbill-platform-lifecycle</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-osgi-api</artifactId>
         </dependency>
         <dependency>
@@ -164,10 +196,25 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-locker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-queue</artifactId>
         </dependency>
         <dependency>
@@ -186,6 +233,10 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.skife.config</groupId>
+            <artifactId>config-magic</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
index 2e654e5..9058564 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultAdminPaymentApi.java
@@ -25,7 +25,7 @@ import org.killbill.billing.payment.core.PaymentTransactionInfoPluginConverter;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 public class DefaultAdminPaymentApi extends DefaultApiBase implements AdminPaymentApi {
 
@@ -34,7 +34,7 @@ public class DefaultAdminPaymentApi extends DefaultApiBase implements AdminPayme
 
     @Inject
     public DefaultAdminPaymentApi(final PaymentConfig paymentConfig, final PaymentDao paymentDao, final InternalCallContextFactory internalCallContextFactory) {
-        super(paymentConfig);
+        super(paymentConfig, internalCallContextFactory);
         this.paymentDao = paymentDao;
         this.internalCallContextFactory = internalCallContextFactory;
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
index 671a9d1..e2bcac5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultApiBase.java
@@ -21,32 +21,40 @@ import java.util.LinkedList;
 import java.util.List;
 
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.google.common.collect.ImmutableList;
 
 public class DefaultApiBase {
 
     private final PaymentConfig paymentConfig;
+    protected final InternalCallContextFactory internalCallContextFactory;
 
-    public DefaultApiBase(final PaymentConfig paymentConfig) {
+    public DefaultApiBase(final PaymentConfig paymentConfig, final InternalCallContextFactory internalCallContextFactory) {
         this.paymentConfig = paymentConfig;
+        this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    protected List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions) {
+    protected List<String> toPaymentControlPluginNames(final PaymentOptions paymentOptions, final CallContext callContext) {
+        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(callContext);
+
         // Special path for JAX-RS InvoicePayment endpoints (see JaxRsResourceBase)
-        if (paymentConfig.getPaymentControlPluginNames() != null &&
+        final List<String> controlPluginNames = paymentConfig.getPaymentControlPluginNames(internalTenantContext);
+        if (controlPluginNames != null &&
             paymentOptions.getPaymentControlPluginNames() != null &&
             paymentOptions.getPaymentControlPluginNames().size() == 1 &&
             InvoicePaymentControlPluginApi.PLUGIN_NAME.equals(paymentOptions.getPaymentControlPluginNames().get(0))) {
             final List<String> paymentControlPluginNames = new LinkedList<String>(paymentOptions.getPaymentControlPluginNames());
-            paymentControlPluginNames.addAll(paymentConfig.getPaymentControlPluginNames());
+            paymentControlPluginNames.addAll(controlPluginNames);
             return paymentControlPluginNames;
         } else if (paymentOptions.getPaymentControlPluginNames() != null && !paymentOptions.getPaymentControlPluginNames().isEmpty()) {
             return paymentOptions.getPaymentControlPluginNames();
-        } else if (paymentConfig.getPaymentControlPluginNames() != null && !paymentConfig.getPaymentControlPluginNames().isEmpty()) {
-            return paymentConfig.getPaymentControlPluginNames();
+        } else if (controlPluginNames != null && !controlPluginNames.isEmpty()) {
+            return controlPluginNames;
         } else {
             return ImmutableList.<String>of();
         }
@@ -57,4 +65,10 @@ public class DefaultApiBase {
             throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, parameterName, "should not be null");
         }
     }
+
+    protected void checkExternalKeyLength(final String externalKey) throws PaymentApiException {
+        if (null != externalKey && externalKey.length() > 255) {
+            throw new PaymentApiException(ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED);
+        }
+    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
index 562889a..b16ba6b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPayment.java
@@ -53,18 +53,21 @@ public class DefaultPayment extends EntityBase implements Payment {
 
     private final Currency currency;
     private final List<PaymentTransaction> transactions;
+    private final List<PaymentAttempt> paymentAttempts;
 
     public DefaultPayment(final UUID id, @Nullable final DateTime createdDate, @Nullable final DateTime updatedDate, final UUID accountId,
                           final UUID paymentMethodId,
                           final Integer paymentNumber,
                           final String externalKey,
-                          final List<PaymentTransaction> transactions) {
+                          final List<PaymentTransaction> transactions,
+                          final List<PaymentAttempt> paymentAttempts) {
         super(id, createdDate, updatedDate);
         this.accountId = accountId;
         this.paymentMethodId = paymentMethodId;
         this.paymentNumber = paymentNumber;
         this.externalKey = externalKey;
         this.transactions = transactions;
+        this.paymentAttempts = paymentAttempts;
 
         final Collection<PaymentTransaction> voidedTransactions = new LinkedList<PaymentTransaction>();
         final Collection<PaymentTransaction> nonVoidedTransactions = new LinkedList<PaymentTransaction>();
@@ -350,6 +353,9 @@ public class DefaultPayment extends EntityBase implements Payment {
     }
 
     @Override
+    public List<PaymentAttempt> getPaymentAttempts() { return paymentAttempts; }
+
+    @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("DefaultPayment{");
         sb.append("accountId=").append(accountId);
@@ -362,6 +368,7 @@ public class DefaultPayment extends EntityBase implements Payment {
         sb.append(", refundAmount=").append(refundAmount);
         sb.append(", currency=").append(currency);
         sb.append(", transactions=").append(transactions);
+        sb.append(", paymentAttempts=").append(paymentAttempts);
         sb.append('}');
         return sb.toString();
     }
@@ -410,6 +417,9 @@ public class DefaultPayment extends EntityBase implements Payment {
         if (transactions != null ? !transactions.equals(that.transactions) : that.transactions != null) {
             return false;
         }
+        if (paymentAttempts != null ? !paymentAttempts.equals(that.paymentAttempts) : that.paymentAttempts != null) {
+            return false;
+        }
 
         return true;
     }
@@ -427,6 +437,7 @@ public class DefaultPayment extends EntityBase implements Payment {
         result = 31 * result + (refundAmount != null ? refundAmount.hashCode() : 0);
         result = 31 * result + (currency != null ? currency.hashCode() : 0);
         result = 31 * result + (transactions != null ? transactions.hashCode() : 0);
+        result = 31 * result + (paymentAttempts != null ? paymentAttempts.hashCode() : 0);
         return result;
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
index ace4fef..b4bb5f4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentApi.java
@@ -37,19 +37,19 @@ import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.Pagination;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
-public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
 
-    private static final Joiner JOINER = Joiner.on(",");
+public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
 
     private static final boolean SHOULD_LOCK_ACCOUNT = true;
     private static final boolean IS_API_PAYMENT = true;
@@ -60,15 +60,13 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     private final PaymentProcessor paymentProcessor;
     private final PaymentMethodProcessor paymentMethodProcessor;
     private final PluginControlPaymentProcessor pluginControlPaymentProcessor;
-    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public DefaultPaymentApi(final PaymentConfig paymentConfig, final PaymentProcessor paymentProcessor, final PaymentMethodProcessor paymentMethodProcessor, final PluginControlPaymentProcessor pluginControlPaymentProcessor, final InternalCallContextFactory internalCallContextFactory) {
-        super(paymentConfig);
+        super(paymentConfig, internalCallContextFactory);
         this.paymentProcessor = paymentProcessor;
         this.paymentMethodProcessor = paymentMethodProcessor;
         this.pluginControlPaymentProcessor = pluginControlPaymentProcessor;
-        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
@@ -81,12 +79,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentExternalKey);
 
         final String transactionType = TransactionType.AUTHORIZE.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createAuthorization(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -95,8 +95,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -106,7 +110,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
@@ -114,7 +119,7 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     public Payment createAuthorizationWithPaymentControl(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                                          @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                                          final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createAuthorization(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
         }
@@ -126,23 +131,29 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentExternalKey);
 
         final String transactionType = TransactionType.AUTHORIZE.name();
 
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = pluginControlPaymentProcessor.createAuthorization(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
-                                                                                      properties, paymentControlPluginNames, callContext, internalCallContext);
+                                                                        properties, paymentControlPluginNames, callContext, internalCallContext);
 
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -152,7 +163,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
@@ -165,12 +177,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CAPTURE.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createCapture(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -179,8 +193,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -190,14 +208,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
     @Override
     public Payment createCaptureWithPaymentControl(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentTransactionExternalKey,
                                                    final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createCapture(account, paymentId, amount, currency, paymentTransactionExternalKey, properties, callContext);
         }
@@ -207,12 +226,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CAPTURE.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = pluginControlPaymentProcessor.createCapture(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -221,8 +242,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -232,7 +257,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
 
     }
@@ -247,12 +273,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.PURCHASE.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createPurchase(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
@@ -261,8 +289,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -272,14 +304,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
     @Override
     public Payment createPurchaseWithPaymentControl(final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                                                     final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createPurchase(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
         }
@@ -289,32 +322,41 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(amount, "amount");
             checkNotNullParameter(currency, "currency");
         }
-        checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
+
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         if (paymentMethodId == null && !paymentOptions.isExternalPayment()) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_INVALID_PARAMETER, "paymentMethodId", "should not be null");
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, "paymentMethodId", "should not be null");
         }
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-        final UUID nonNulPaymentMethodId = (paymentMethodId != null) ?
+
+        // TODO validate if the code is located properly here
+        // The code should understand that the external payment method needs to be created
+        // if it doesn't exist yet and trigger a CREDIT using the (built-in) external payment plugin.
+        final UUID nonNullPaymentMethodId = (paymentMethodId != null) ?
                                            paymentMethodId :
                                            paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
 
         final String transactionType = TransactionType.PURCHASE.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
-
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
-            payment = pluginControlPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNulPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+            payment = pluginControlPaymentProcessor.createPurchase(IS_API_PAYMENT, account, nonNullPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
                                                                                  properties, paymentControlPluginNames, callContext, internalCallContext);
 
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -324,7 +366,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
@@ -335,12 +378,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.VOID.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createVoid(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey,
@@ -349,8 +394,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -360,14 +409,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
 
     }
 
     @Override
     public Payment createVoidWithPaymentControl(final Account account, final UUID paymentId, final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createVoid(account, paymentId, paymentTransactionExternalKey, properties, callContext);
         }
@@ -375,12 +425,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.VOID.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = pluginControlPaymentProcessor.createVoid(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey,
@@ -389,8 +441,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -400,7 +456,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
@@ -413,12 +470,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         }
         checkNotNullParameter(paymentId, "paymentId");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.REFUND.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createRefund(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -427,8 +486,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -438,14 +501,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
     @Override
     public Payment createRefundWithPaymentControl(final Account account, @Nullable final UUID paymentId, @Nullable final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final Iterable<PluginProperty> properties,
                                                   final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createRefund(account, paymentId, amount, currency, paymentTransactionExternalKey, properties, callContext);
         }
@@ -455,14 +519,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(paymentId, "paymentId");
-        checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.REFUND.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = pluginControlPaymentProcessor.createRefund(IS_API_PAYMENT, account, paymentId, amount, currency, paymentTransactionExternalKey,
@@ -471,8 +536,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -482,7 +551,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
@@ -491,28 +561,37 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                                 @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                 final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
-        checkNotNullParameter(paymentMethodId, "paymentMethodId");
         if (paymentId == null) {
             checkNotNullParameter(amount, "amount");
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CREDIT.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-            payment = paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+            final UUID nonNullPaymentMethodId = (paymentMethodId != null) ?
+                                               paymentMethodId :
+                                               paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
+
+            payment = paymentProcessor.createCredit(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, nonNullPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
                                                                   SHOULD_LOCK_ACCOUNT, properties, callContext, internalCallContext);
 
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -522,7 +601,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
@@ -530,34 +610,47 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     public Payment createCreditWithPaymentControl(final Account account, final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
                                                   @Nullable final String paymentExternalKey, @Nullable final String paymentTransactionExternalKey,
                                                   final Iterable<PluginProperty> properties, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createCredit(account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey, properties, callContext);
         }
 
         checkNotNullParameter(account, "account");
-        checkNotNullParameter(paymentMethodId, "paymentMethodId");
         if (paymentId == null) {
             checkNotNullParameter(amount, "amount");
             checkNotNullParameter(currency, "currency");
         }
         checkNotNullParameter(properties, "plugin properties");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CREDIT.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, paymentMethodId, paymentId, null, amount, currency, paymentExternalKey, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
-            payment = pluginControlPaymentProcessor.createCredit(IS_API_PAYMENT, account, paymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+
+            // TODO validate if the code is located properly here
+            // The code should understand that the external payment method needs to be created
+            // if it doesn't exist yet and trigger a CREDIT using the (built-in) external payment plugin.
+            final UUID nonNullPaymentMethodId = (paymentMethodId != null) ?
+                                               paymentMethodId :
+                                               paymentMethodProcessor.createOrGetExternalPaymentMethod(UUIDs.randomUUID().toString(), account, properties, callContext, internalCallContext);
+
+            payment = pluginControlPaymentProcessor.createCredit(IS_API_PAYMENT, account, nonNullPaymentMethodId, paymentId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
                                                                                properties, paymentControlPluginNames, callContext, internalCallContext);
 
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -567,11 +660,24 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
     @Override
+    public void cancelScheduledPaymentTransaction(final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
+        checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
+        paymentProcessor.cancelScheduledPaymentTransaction(null, paymentTransactionExternalKey, callContext);
+    }
+
+    @Override
+    public void cancelScheduledPaymentTransaction(final UUID paymentTransactionId, final CallContext callContext) throws PaymentApiException {
+        checkNotNullParameter(paymentTransactionId, "paymentTransactionId");
+        paymentProcessor.cancelScheduledPaymentTransaction(paymentTransactionId, null, callContext);
+    }
+
+    @Override
     public Payment notifyPendingTransactionOfStateChanged(final Account account, final UUID paymentTransactionId, final boolean isSuccess, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentTransactionId, "paymentTransactionId");
@@ -579,8 +685,9 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         final String transactionType = "NOTIFY_STATE_CHANGE";
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, null, paymentTransactionId, null, null, null, null, null, null);
+            logEnterAPICall(log, transactionType, account, null, null, paymentTransactionId, null, null, null, null, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.notifyPendingPaymentOfStateChanged(account, paymentTransactionId, isSuccess, callContext, internalCallContext);
@@ -593,8 +700,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                                                                                                     }
                                                                                                 }).orNull();
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -604,7 +715,8 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
@@ -619,12 +731,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         checkNotNullParameter(amount, "amount");
         checkNotNullParameter(currency, "currency");
         checkNotNullParameter(paymentId, "paymentId");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CHARGEBACK.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createChargeback(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, amount, currency, true,
@@ -633,8 +747,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -644,13 +762,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           null,
+                           exception);
         }
     }
 
     @Override
     public Payment createChargebackWithPaymentControl(final Account account, final UUID paymentId, final BigDecimal amount, final Currency currency, final String paymentTransactionExternalKey, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return createChargeback(account, paymentId, amount, currency, paymentTransactionExternalKey, callContext);
         }
@@ -663,8 +782,9 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         final String transactionType = TransactionType.CHARGEBACK.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, amount, currency, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = pluginControlPaymentProcessor.createChargeback(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, amount, currency,
@@ -673,8 +793,12 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -684,21 +808,23 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           paymentControlPluginNames);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
-    //@Override TODO 0.17
+    @Override
     public Payment createChargebackReversal(final Account account, final UUID paymentId, final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
         checkNotNullParameter(account, "account");
         checkNotNullParameter(paymentId, "paymentId");
-        checkNotNullParameter(paymentTransactionExternalKey, "paymentTransactionExternalKey");
+        checkExternalKeyLength(paymentTransactionExternalKey);
 
         final String transactionType = TransactionType.CHARGEBACK.name();
         Payment payment = null;
         PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
         try {
-            logEnterAPICall(transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, null);
 
             final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
             payment = paymentProcessor.createChargebackReversal(IS_API_PAYMENT, NULL_ATTEMPT_ID, account, paymentId, paymentTransactionExternalKey, null, null, true, callContext, internalCallContext);
@@ -706,8 +832,63 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
             paymentTransaction = findPaymentTransaction(payment, paymentTransactionExternalKey);
 
             return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
+        } finally {
+            logExitAPICall(log,
+                           transactionType,
+                           account,
+                           payment != null ? payment.getPaymentMethodId() : null,
+                           payment != null ? payment.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+                           payment != null ? payment.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+                           null,
+                           exception);
+        }
+    }
+
+    @Override
+    public Payment createChargebackReversalWithPaymentControl(final Account account, final UUID paymentId, final String paymentTransactionExternalKey, final PaymentOptions paymentOptions, final CallContext callContext) throws PaymentApiException {
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
+        if (paymentControlPluginNames.isEmpty()) {
+            return createChargebackReversal(account, paymentId, paymentTransactionExternalKey, callContext);
+        }
+
+        checkNotNullParameter(account, "account");
+        checkNotNullParameter(paymentId, "paymentId");
+        checkExternalKeyLength(paymentTransactionExternalKey);
+
+        final String transactionType = TransactionType.CHARGEBACK.name();
+        Payment payment = null;
+        PaymentTransaction paymentTransaction = null;
+        PaymentApiException exception = null;
+        try {
+            logEnterAPICall(log, transactionType, account, null, paymentId, null, null, null, null, paymentTransactionExternalKey, null, paymentControlPluginNames);
+
+            final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+            payment = pluginControlPaymentProcessor.createChargebackReversal(IS_API_PAYMENT, account, paymentId, paymentTransactionExternalKey, paymentControlPluginNames, callContext, internalCallContext);
+
+            // See https://github.com/killbill/killbill/issues/552
+            paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+                                                                    new Predicate<PaymentTransaction>() {
+                                                                        @Override
+                                                                        public boolean apply(final PaymentTransaction input) {
+                                                                            return paymentTransactionExternalKey.equals(input.getExternalKey());
+                                                                        }
+                                                                    });
+
+            return payment;
+        } catch (PaymentApiException e) {
+            exception = e;
+            throw e;
         } finally {
-            logExitAPICall(transactionType,
+            logExitAPICall(log,
+                           transactionType,
                            account,
                            payment != null ? payment.getPaymentMethodId() : null,
                            payment != null ? payment.getId() : null,
@@ -717,28 +898,29 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                            payment != null ? payment.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
                            paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
-                           null);
+                           paymentControlPluginNames,
+                           exception);
         }
     }
 
     @Override
-    public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return paymentProcessor.getAccountPayments(accountId, withPluginInfo, tenantContext, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
+    public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
+        return paymentProcessor.getAccountPayments(accountId, withPluginInfo, withAttempts, tenantContext, internalCallContextFactory.createInternalTenantContext(accountId, tenantContext));
     }
 
     @Override
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentProcessor.getPayments(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) {
+        return paymentProcessor.getPayments(offset, limit, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
-        return paymentProcessor.getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext) throws PaymentApiException {
+        return paymentProcessor.getPayments(offset, limit, pluginName, withPluginInfo, withAttempts, properties, tenantContext, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
     }
 
     @Override
-    public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        final Payment payment = paymentProcessor.getPayment(paymentId, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, context));
+    public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        final Payment payment = paymentProcessor.getPayment(paymentId, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContext(paymentId, ObjectType.PAYMENT, context));
         if (payment == null) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentId);
         }
@@ -746,9 +928,9 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     }
 
     @Override
-    public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext)
+    public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext)
             throws PaymentApiException {
-        final Payment payment = paymentProcessor.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, properties, tenantContext, internalCallContextFactory.createInternalTenantContext(tenantContext));
+        final Payment payment = paymentProcessor.getPaymentByExternalKey(paymentExternalKey, withPluginInfo, withAttempts, properties, tenantContext, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
         if (payment == null) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, paymentExternalKey);
         }
@@ -756,15 +938,14 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     }
 
     @Override
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentProcessor.searchPayments(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) {
+        return paymentProcessor.searchPayments(searchKey, offset, limit, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        return paymentProcessor.searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
-
     @Override
     public UUID addPaymentMethod(final Account account, final String paymentMethodExternalKey, final String pluginName,
                                  final boolean setDefault, final PaymentMethodPlugin paymentMethodInfo,
@@ -789,33 +970,33 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
     @Override
     public PaymentMethod getPaymentMethodByExternalKey(final String paymentMethodExternalKey, final boolean includedInactive, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context)
             throws PaymentApiException {
-        return paymentMethodProcessor.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedInactive, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.getPaymentMethodByExternalKey(paymentMethodExternalKey, includedInactive, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
     public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentMethodProcessor.getPaymentMethods(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.getPaymentMethods(offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
     public Pagination<PaymentMethod> getPaymentMethods(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.getPaymentMethods(offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) {
-        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
     public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
-        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContext(context));
+        return paymentMethodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, properties, context, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     @Override
-    public void deletePaymentMethod(final Account account, final UUID paymentMethodId, final boolean deleteDefaultPaymentMethodWithAutoPayOff, final Iterable<PluginProperty> properties, final CallContext context)
+    public void deletePaymentMethod(final Account account, final UUID paymentMethodId, final boolean deleteDefaultPaymentMethodWithAutoPayOff, final boolean forceDefaultPaymentMethodDeletion, final Iterable<PluginProperty> properties, final CallContext context)
             throws PaymentApiException {
-        paymentMethodProcessor.deletedPaymentMethod(account, paymentMethodId, deleteDefaultPaymentMethodWithAutoPayOff, properties, context, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+        paymentMethodProcessor.deletedPaymentMethod(account, paymentMethodId, deleteDefaultPaymentMethodWithAutoPayOff, forceDefaultPaymentMethodDeletion, properties, context, internalCallContextFactory.createInternalCallContext(account.getId(), context));
     }
 
     @Override
@@ -843,6 +1024,15 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
         return paymentMethods;
     }
 
+    @Override
+    public Payment getPaymentByTransactionId(final UUID transactionId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context) throws PaymentApiException {
+        final Payment payment = paymentProcessor.getPaymentByTransactionId(transactionId, withPluginInfo, withAttempts, properties, context, internalCallContextFactory.createInternalTenantContext(transactionId, ObjectType.PAYMENT, context));
+        if (payment == null) {
+            throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT, transactionId);
+        }
+        return payment;
+    }
+
     private PaymentTransaction findPaymentTransaction(final Payment payment, @Nullable final String paymentTransactionExternalKey) {
         // By design, the payment transactions are already correctly sorted (by effective date asc)
         if (paymentTransactionExternalKey == null) {
@@ -857,122 +1047,4 @@ public class DefaultPaymentApi extends DefaultApiBase implements PaymentApi {
                                                       });
         }
     }
-
-    private void logEnterAPICall(final String transactionType,
-                                   final Account account,
-                                   @Nullable final UUID paymentMethodId,
-                                   @Nullable final UUID paymentId,
-                                   @Nullable final UUID transactionId,
-                                   @Nullable final BigDecimal amount,
-                                   @Nullable final Currency currency,
-                                   @Nullable final String paymentExternalKey,
-                                   @Nullable final String paymentTransactionExternalKey,
-                                   @Nullable final TransactionStatus transactionStatus,
-                                   @Nullable final List<String> paymentControlPluginNames) {
-        logAPICallInternal("ENTERING ",
-                           transactionType,
-                           account,
-                           paymentMethodId,
-                           paymentId,
-                           transactionId,
-                           amount,
-                           currency,
-                           paymentExternalKey,
-                           paymentTransactionExternalKey,
-                           transactionStatus,
-                           paymentControlPluginNames);
-    }
-
-    private void logExitAPICall(final String transactionType,
-                                  final Account account,
-                                  @Nullable final UUID paymentMethodId,
-                                  @Nullable final UUID paymentId,
-                                  @Nullable final UUID transactionId,
-                                  @Nullable final BigDecimal amount,
-                                  @Nullable final Currency currency,
-                                  @Nullable final String paymentExternalKey,
-                                  @Nullable final String paymentTransactionExternalKey,
-                                  @Nullable final TransactionStatus transactionStatus,
-                                  @Nullable final List<String> paymentControlPluginNames) {
-        logAPICallInternal("EXITING ",
-                           transactionType,
-                           account,
-                           paymentMethodId,
-                           paymentId,
-                           transactionId,
-                           amount,
-                           currency,
-                           paymentExternalKey,
-                           paymentTransactionExternalKey,
-                           transactionStatus,
-                           paymentControlPluginNames);
-    }
-
-    private void logAPICallInternal(final String prefixMsg,
-                                    final String transactionType,
-                                    final Account account,
-                                    final UUID paymentMethodId,
-                                    @Nullable final UUID paymentId,
-                                    @Nullable final UUID transactionId,
-                                    @Nullable final BigDecimal amount,
-                                    @Nullable final Currency currency,
-                                    @Nullable final String paymentExternalKey,
-                                    @Nullable final String paymentTransactionExternalKey,
-                                    @Nullable final TransactionStatus transactionStatus,
-                                    @Nullable final List<String> paymentControlPluginNames) {
-        if (log.isInfoEnabled()) {
-            final StringBuilder logLine = new StringBuilder(prefixMsg);
-            logLine.append("PaymentApi: transactionType='")
-                   .append(transactionType)
-                   .append("', accountId='")
-                   .append(account.getId())
-                   .append("'");
-            if (paymentMethodId != null) {
-                logLine.append(", paymentMethodId='")
-                       .append(paymentMethodId)
-                       .append("'");
-            }
-            if (paymentExternalKey != null) {
-                logLine.append(", paymentExternalKey='")
-                       .append(paymentExternalKey)
-                       .append("'");
-            }
-            if (paymentTransactionExternalKey != null) {
-                logLine.append(", paymentTransactionExternalKey='")
-                       .append(paymentTransactionExternalKey)
-                       .append("'");
-            }
-            if (paymentId != null) {
-                logLine.append(", paymentId='")
-                       .append(paymentId)
-                       .append("'");
-            }
-            if (transactionId != null) {
-                logLine.append(", transactionId='")
-                       .append(transactionId)
-                       .append("'");
-            }
-            if (amount != null) {
-                logLine.append(", amount='")
-                       .append(amount)
-                       .append("'");
-            }
-            if (currency != null) {
-                logLine.append(", currency='")
-                       .append(currency)
-                       .append("'");
-            }
-            if (transactionStatus != null) {
-                logLine.append(", transactionStatus='")
-                       .append(transactionStatus)
-                       .append("'");
-            }
-            if (paymentControlPluginNames != null) {
-                logLine.append(", paymentControlPluginNames='")
-                       .append(JOINER.join(paymentControlPluginNames))
-                       .append("'");
-            }
-            log.info(logLine.toString());
-        }
-    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentAttempt.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentAttempt.java
new file mode 100644
index 0000000..31384e6
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentAttempt.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.payment.api;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.entity.EntityBase;
+
+public class DefaultPaymentAttempt extends EntityBase implements PaymentAttempt {
+
+    private final UUID accountId;
+    private final UUID paymentMethodId;
+    private final String paymentExternalKey;
+    private final UUID transactionId;
+    private final String transactionExternalKey;
+    private final TransactionType transactionType;
+    private final DateTime effectiveDate;
+    private final String stateName;
+    private final BigDecimal amount;
+    private final Currency currency;
+    private final String pluginName;
+    private final List<PluginProperty> pluginProperties;
+
+    public DefaultPaymentAttempt(final UUID accountId, final UUID paymentMethodId, final UUID id, final DateTime createdDate, final DateTime updatedDate,
+                                 final DateTime effectiveDate, final String paymentExternalKey, final UUID transactionId, final String transactionExternalKey,
+                                 final TransactionType transactionType, final String stateName, final BigDecimal amount, final Currency currency,
+                                 final String pluginName, final List<PluginProperty> pluginProperties) {
+        super(id, createdDate, updatedDate);
+        this.accountId = accountId;
+        this.paymentMethodId = paymentMethodId;
+        this.paymentExternalKey = paymentExternalKey;
+        this.transactionId = transactionId;
+        this.transactionExternalKey = transactionExternalKey;
+        this.transactionType = transactionType;
+        this.effectiveDate = effectiveDate;
+        this.stateName = stateName;
+        this.amount = amount;
+        this.currency = currency;
+        this.pluginName = pluginName;
+        this.pluginProperties = pluginProperties;
+    }
+
+    @Override
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    @Override
+    public UUID getPaymentMethodId() {
+        return paymentMethodId;
+    }
+
+    @Override
+    public String getPaymentExternalKey() {
+        return paymentExternalKey;
+    }
+
+    @Override
+    public UUID getTransactionId() {
+        return transactionId;
+    }
+
+    @Override
+    public String getTransactionExternalKey() {
+        return transactionExternalKey;
+    }
+
+    @Override
+    public TransactionType getTransactionType() {
+        return transactionType;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() { return effectiveDate; }
+
+    @Override
+    public String getStateName() {
+        return stateName;
+    }
+
+    @Override
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
+    @Override
+    public Currency getCurrency() {
+        return currency;
+    }
+
+    @Override
+    public String getPluginName() {
+        return pluginName;
+    }
+
+    @Override
+    public List<PluginProperty> getPluginProperties() {
+        return pluginProperties;
+    }
+
+    @Override
+    public String toString() {
+        return "DefaultPaymentAttempt{" +
+               "accountId=" + accountId +
+               ", paymentMethodId=" + paymentMethodId +
+               ", paymentExternalKey='" + paymentExternalKey + '\'' +
+               ", transactionId=" + transactionId +
+               ", transactionExternalKey='" + transactionExternalKey + '\'' +
+               ", transactionType=" + transactionType +
+               ", effectiveDate=" + effectiveDate +
+               ", stateName=" + stateName +
+               ", amount=" + amount +
+               ", currency=" + currency +
+               ", pluginName='" + pluginName + '\'' +
+               ", pluginProperties=" + pluginProperties +
+               '}';
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final DefaultPaymentAttempt that = (DefaultPaymentAttempt) o;
+
+        if (accountId != null ? !accountId.equals(that.accountId) : that.accountId != null) {
+            return false;
+        }
+        if (paymentMethodId != null ? !paymentMethodId.equals(that.paymentMethodId) : that.paymentMethodId != null) {
+            return false;
+        }
+        if (paymentExternalKey != null ? !paymentExternalKey.equals(that.paymentExternalKey) : that.paymentExternalKey != null) {
+            return false;
+        }
+        if (transactionId != null ? !transactionId.equals(that.transactionId) : that.transactionId != null) {
+            return false;
+        }
+        if (transactionExternalKey != null ? !transactionExternalKey.equals(that.transactionExternalKey) : that.transactionExternalKey != null) {
+            return false;
+        }
+        if (transactionType != that.transactionType) {
+            return false;
+        }
+        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+            return false;
+        }
+        if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+            return false;
+        }
+        if (amount != null ? !amount.equals(that.amount) : that.amount != null) {
+            return false;
+        }
+        if (currency != that.currency) {
+            return false;
+        }
+        if (pluginName != null ? !pluginName.equals(that.pluginName) : that.pluginName != null) {
+            return false;
+        }
+        return pluginProperties != null ? pluginProperties.equals(that.pluginProperties) : that.pluginProperties == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (accountId != null ? accountId.hashCode() : 0);
+        result = 31 * result + (paymentMethodId != null ? paymentMethodId.hashCode() : 0);
+        result = 31 * result + (paymentExternalKey != null ? paymentExternalKey.hashCode() : 0);
+        result = 31 * result + (transactionId != null ? transactionId.hashCode() : 0);
+        result = 31 * result + (transactionExternalKey != null ? transactionExternalKey.hashCode() : 0);
+        result = 31 * result + (transactionType != null ? transactionType.hashCode() : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+        result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (currency != null ? currency.hashCode() : 0);
+        result = 31 * result + (pluginName != null ? pluginName.hashCode() : 0);
+        result = 31 * result + (pluginProperties != null ? pluginProperties.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
index 09685fa..20a0655 100644
--- a/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/api/DefaultPaymentGatewayApi.java
@@ -42,7 +42,7 @@ import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
 import org.killbill.billing.util.PluginProperties;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.google.common.base.Joiner;
 
@@ -56,7 +56,6 @@ public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentG
     private final ControlPluginRunner controlPluginRunner;
     private final PluginDispatcher<HostedPaymentPageFormDescriptor> paymentPluginFormDispatcher;
     private final PluginDispatcher<GatewayNotification> paymentPluginNotificationDispatcher;
-    private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public DefaultPaymentGatewayApi(final PaymentConfig paymentConfig,
@@ -64,13 +63,12 @@ public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentG
                                     final ControlPluginRunner controlPluginRunner,
                                     final PaymentExecutors executors,
                                     final InternalCallContextFactory internalCallContextFactory) {
-        super(paymentConfig);
+        super(paymentConfig, internalCallContextFactory);
         this.paymentGatewayProcessor = paymentGatewayProcessor;
         this.controlPluginRunner = controlPluginRunner;
         final long paymentPluginTimeoutSec = TimeUnit.SECONDS.convert(paymentConfig.getPaymentPluginTimeout().getPeriod(), paymentConfig.getPaymentPluginTimeout().getUnit());
         this.paymentPluginFormDispatcher = new PluginDispatcher<HostedPaymentPageFormDescriptor>(paymentPluginTimeoutSec, executors);
         this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executors);
-        this.internalCallContextFactory = internalCallContextFactory;
     }
 
     @Override
@@ -129,7 +127,7 @@ public class DefaultPaymentGatewayApi extends DefaultApiBase implements PaymentG
                                             final CallContext callContext,
                                             final PluginDispatcher<T> pluginDispatcher,
                                             final WithPaymentControlCallback<T> callback) throws PaymentApiException {
-        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions);
+        final List<String> paymentControlPluginNames = toPaymentControlPluginNames(paymentOptions, callContext);
         if (paymentControlPluginNames.isEmpty()) {
             return callback.doPaymentGatewayApiOperation(paymentMethodId, properties);
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
index 36f3382..58f91ca 100644
--- a/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
+++ b/payment/src/main/java/org/killbill/billing/payment/bus/PaymentBusEventHandler.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -20,6 +20,7 @@ package org.killbill.billing.payment.bus;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -30,8 +31,11 @@ import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.events.PaymentInternalEvent;
+import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
 import org.killbill.billing.payment.core.janitor.Janitor;
 import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
@@ -40,14 +44,20 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.eventbus.AllowConcurrentEvents;
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
+
 public class PaymentBusEventHandler {
 
     private static final Logger log = LoggerFactory.getLogger(PaymentBusEventHandler.class);
@@ -82,22 +92,49 @@ public class PaymentBusEventHandler {
     public void processInvoiceEvent(final InvoiceCreationInternalEvent event) {
         log.info("Received invoice creation notification for accountId='{}', invoiceId='{}'", event.getAccountId(), event.getInvoiceId());
 
-        final Account account;
-        try {
-            final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
-            account = accountApi.getAccountById(event.getAccountId(), internalContext);
+        final Collection<PluginProperty> properties = new ArrayList<PluginProperty>();
+        final PluginProperty propertyInvoiceId = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
+        properties.add(propertyInvoiceId);
+
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(event.getSearchKey2(), event.getSearchKey1(), "PaymentRequestProcessor", CallOrigin.INTERNAL, UserType.SYSTEM, event.getUserToken());
+        final CallContext callContext = internalCallContextFactory.createCallContext(internalContext);
 
-            final List<PluginProperty> properties = new ArrayList<PluginProperty>();
-            final PluginProperty prop1 = new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, event.getInvoiceId().toString(), false);
-            properties.add(prop1);
+        final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
+        final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames(internalContext) != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames(internalContext)) : new LinkedList<String>();
+        paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
 
-            final CallContext callContext = internalCallContextFactory.createCallContext(internalContext);
+        final String paymentExternalKey = UUIDs.randomUUID().toString();
+        final String paymentTransactionExternalKey = UUIDs.randomUUID().toString();
+
+        final String transactionType = TransactionType.PURCHASE.name();
+        Account account = null;
+        Payment payment = null;
+        PaymentTransaction paymentTransaction = null;
+        try {
+            account = accountApi.getAccountById(event.getAccountId(), internalContext);
 
-            final BigDecimal amountToBePaid = null; // We let the plugin compute how much should be paid
-            final List<String> paymentControlPluginNames = paymentConfig.getPaymentControlPluginNames() != null ? new LinkedList<String>(paymentConfig.getPaymentControlPluginNames()) : new LinkedList<String>();
-            paymentControlPluginNames.add(InvoicePaymentControlPluginApi.PLUGIN_NAME);
-            pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), UUIDs.randomUUID().toString(), UUIDs.randomUUID().toString(),
-                                                         properties, paymentControlPluginNames, callContext, internalContext);
+            logEnterAPICall(log,
+                            transactionType,
+                            account,
+                            account.getPaymentMethodId(),
+                            null,
+                            null,
+                            amountToBePaid,
+                            account.getCurrency(),
+                            paymentExternalKey,
+                            paymentTransactionExternalKey,
+                            null,
+                            paymentControlPluginNames);
+
+            payment = pluginControlPaymentProcessor.createPurchase(false, account, account.getPaymentMethodId(), null, amountToBePaid, account.getCurrency(), paymentExternalKey, paymentTransactionExternalKey, properties, paymentControlPluginNames, callContext, internalContext);
+
+            paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+                                                                    new Predicate<PaymentTransaction>() {
+                                                                        @Override
+                                                                        public boolean apply(final PaymentTransaction input) {
+                                                                            return paymentTransactionExternalKey.equals(input.getExternalKey());
+                                                                        }
+                                                                    });
         } catch (final AccountApiException e) {
             log.warn("Failed to process invoice payment", e);
         } catch (final PaymentApiException e) {
@@ -105,6 +142,20 @@ public class PaymentBusEventHandler {
             if (e.getCode() != ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode()) {
                 log.warn("Failed to process invoice payment {}", e.toString());
             }
+        } finally {
+            logExitAPICall(log,
+                           transactionType,
+                           account,
+                           payment != null ? payment.getPaymentMethodId() : null,
+                           payment != null ? payment.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+                           payment != null ? payment.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+                           paymentControlPluginNames,
+                           null);
         }
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/config/MultiTenantPaymentConfig.java b/payment/src/main/java/org/killbill/billing/payment/config/MultiTenantPaymentConfig.java
new file mode 100644
index 0000000..6e56c52
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/config/MultiTenantPaymentConfig.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.payment.config;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.payment.glue.PaymentModule;
+import org.killbill.billing.util.config.definition.KillbillConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
+import org.killbill.billing.util.config.tenant.CacheConfig;
+import org.killbill.billing.util.config.tenant.MultiTenantConfigBase;
+import org.skife.config.Param;
+import org.skife.config.TimeSpan;
+
+public class MultiTenantPaymentConfig extends MultiTenantConfigBase implements PaymentConfig {
+
+    private final PaymentConfig staticConfig;
+
+    @Inject
+    public MultiTenantPaymentConfig(@Named(PaymentModule.STATIC_CONFIG) final PaymentConfig staticConfig, final CacheConfig cacheConfig) {
+        super(cacheConfig);
+        this.staticConfig = staticConfig;
+    }
+
+    @Override
+    public List<Integer> getPaymentFailureRetryDays() {
+        return staticConfig.getPaymentFailureRetryDays();
+    }
+
+    @Override
+    public List<Integer> getPaymentFailureRetryDays(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPaymentFailureRetryDays", tenantContext);
+        if (result != null) {
+            return convertToListInteger(result, "getPaymentFailureRetryDays");
+        }
+        return getPaymentFailureRetryDays();
+    }
+
+    @Override
+    public int getPluginFailureInitialRetryInSec() {
+        return staticConfig.getPluginFailureInitialRetryInSec();
+    }
+
+    @Override
+    public int getPluginFailureInitialRetryInSec(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPluginFailureInitialRetryInSec", tenantContext);
+        if (result != null) {
+            return Integer.parseInt(result);
+        }
+        return getPluginFailureInitialRetryInSec();
+    }
+
+    @Override
+    public int getPluginFailureRetryMultiplier() {
+        return staticConfig.getPluginFailureRetryMultiplier();
+    }
+
+    @Override
+    public int getPluginFailureRetryMultiplier(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPluginFailureRetryMultiplier", tenantContext);
+        if (result != null) {
+            return Integer.parseInt(result);
+        }
+        return getPluginFailureRetryMultiplier();
+    }
+
+    @Override
+    public List<TimeSpan> getIncompleteTransactionsRetries() {
+        return staticConfig.getIncompleteTransactionsRetries();
+    }
+
+    @Override
+    public List<TimeSpan> getIncompleteTransactionsRetries(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getIncompleteTransactionsRetries", tenantContext);
+        if (result != null) {
+            return convertToListTimeSpan(result, "getIncompleteTransactionsRetries");
+        }
+        return getIncompleteTransactionsRetries();
+    }
+
+    @Override
+    public int getPluginFailureRetryMaxAttempts() {
+        return staticConfig.getPluginFailureRetryMaxAttempts();
+    }
+
+    @Override
+    public int getPluginFailureRetryMaxAttempts(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPluginFailureRetryMaxAttempts", tenantContext);
+        if (result != null) {
+            return Integer.parseInt(result);
+        }
+        return getPluginFailureRetryMaxAttempts();
+    }
+
+    @Override
+    public List<String> getPaymentControlPluginNames() {
+        return staticConfig.getPaymentControlPluginNames();
+    }
+
+    @Override
+    public List<String> getPaymentControlPluginNames(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPaymentControlPluginNames", tenantContext);
+        if (result != null) {
+            return convertToListString(result, "getPaymentControlPluginNames");
+        }
+        return getPaymentControlPluginNames();
+    }
+
+    @Override
+    public TimeSpan getJanitorRunningRate() {
+        return staticConfig.getJanitorRunningRate();
+    }
+
+    @Override
+    public TimeSpan getIncompleteAttemptsTimeSpanDelay() {
+        return staticConfig.getIncompleteAttemptsTimeSpanDelay();
+    }
+
+    @Override
+    public String getDefaultPaymentProvider() {
+        return staticConfig.getDefaultPaymentProvider();
+    }
+
+    @Override
+    public TimeSpan getPaymentPluginTimeout() {
+        return staticConfig.getPaymentPluginTimeout();
+    }
+
+    @Override
+    public int getPaymentPluginThreadNb() {
+        return staticConfig.getPaymentPluginThreadNb();
+    }
+
+    @Override
+    public int getMaxGlobalLockRetries() {
+        return staticConfig.getMaxGlobalLockRetries();
+    }
+
+    @Override
+    protected Class<? extends KillbillConfig> getConfigClass() {
+        return PaymentConfig.class;
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
index 0444d7f..a6b0780 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/CompletionTaskBase.java
@@ -36,7 +36,7 @@ import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.callcontext.UserType;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLock;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
index 0e96443..a33e4e0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentAttemptTask.java
@@ -43,7 +43,7 @@ import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertyS
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
index 592dcd5..5fd6dd2 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/IncompletePaymentTransactionTask.java
@@ -48,7 +48,7 @@ import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.notificationq.api.NotificationEvent;
@@ -261,9 +261,9 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
     }
 
     @VisibleForTesting
-    DateTime getNextNotificationTime(final Integer attemptNumber) {
+    DateTime getNextNotificationTime(final Integer attemptNumber, final InternalTenantContext tenantContext) {
 
-        final List<TimeSpan> retries = paymentConfig.getIncompleteTransactionsRetries();
+        final List<TimeSpan> retries = paymentConfig.getIncompleteTransactionsRetries(tenantContext);
         if (attemptNumber > retries.size()) {
             return null;
         }
@@ -277,10 +277,12 @@ public class IncompletePaymentTransactionTask extends CompletionTaskBase<Payment
             return;
         }
 
+        final InternalTenantContext tenantContext = internalCallContextFactory.createInternalTenantContext(tenantRecordId, accountRecordId);
+
         // Increment value before we insert
         final Integer newAttemptNumber = attemptNumber.intValue() + 1;
         final NotificationEvent key = new JanitorNotificationKey(paymentTransactionId, IncompletePaymentTransactionTask.class.toString(), newAttemptNumber);
-        final DateTime notificationTime = getNextNotificationTime(newAttemptNumber);
+        final DateTime notificationTime = getNextNotificationTime(newAttemptNumber, tenantContext);
         // Will be null in the GET path or when we run out opf attempts..
         if (notificationTime != null) {
             try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
index c17f17c..30456c4 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/janitor/Janitor.java
@@ -35,7 +35,7 @@ import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.glue.DefaultPaymentService;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.notificationq.api.NotificationEvent;
@@ -108,25 +108,6 @@ public class Janitor {
 
     }
 
-    /*
-        public IncompletePaymentAttemptTask(final InternalCallContextFactory internalCallContextFactory,
-                                        final PaymentConfig paymentConfig,
-                                        final PaymentDao paymentDao,
-                                        final Clock clock,
-                                        final PaymentStateMachineHelper paymentStateMachineHelper,
-                                        final PaymentControlStateMachineHelper retrySMHelper,
-                                        final AccountInternalApi accountInternalApi,
-                                        final PluginControlPaymentAutomatonRunner pluginControlledPaymentAutomatonRunner,
-                                        final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
-                                        final GlobalLocker locker) {
-
-
-    public IncompletePaymentTransactionTask(final InternalCallContextFactory internalCallContextFactory, final PaymentConfig paymentConfig,
-                                            final PaymentDao paymentDao, final Clock clock,
-                                            final PaymentStateMachineHelper paymentStateMachineHelper, final PaymentControlStateMachineHelper retrySMHelper, final AccountInternalApi accountInternalApi,
-                                            final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry, final GlobalLocker locker) {
-
-     */
 
     public void initialize() throws NotificationQueueAlreadyExists {
         janitorQueue = notificationQueueService.createNotificationQueue(DefaultPaymentService.SERVICE_NAME,
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
index d58c304..afef161 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
@@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.concurrent.Executors;
 import org.killbill.commons.concurrent.WithProfilingThreadPoolExecutor;
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index 53ffd63..ea40106 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -44,7 +44,7 @@ import org.killbill.billing.payment.provider.DefaultNoOpHostedPaymentPageFormDes
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index e8324fb..34e881b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -55,7 +55,7 @@ import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPaginationBuilder;
@@ -399,6 +399,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
 
     public void deletedPaymentMethod(final Account account, final UUID paymentMethodId,
                                      final boolean deleteDefaultPaymentMethodWithAutoPayOff,
+                                     final boolean forceDefaultPaymentMethodDeletion,
                                      final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context)
             throws PaymentApiException {
         try {
@@ -414,11 +415,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     try {
                         // Note: account.getPaymentMethodId() may be null
                         if (paymentMethodId.equals(account.getPaymentMethodId())) {
-                            if (!deleteDefaultPaymentMethodWithAutoPayOff) {
+                            if (!deleteDefaultPaymentMethodWithAutoPayOff && !forceDefaultPaymentMethodDeletion) {
                                 throw new PaymentApiException(ErrorCode.PAYMENT_DEL_DEFAULT_PAYMENT_METHOD, account.getId());
                             } else {
-                                final boolean isAccountAutoPayOff = isAccountAutoPayOff(account.getId(), context);
-                                if (!isAccountAutoPayOff) {
+                                if (deleteDefaultPaymentMethodWithAutoPayOff && !isAccountAutoPayOff(account.getId(), context)) {
                                     log.info("Setting AUTO_PAY_OFF on accountId='{}' because of default payment method deletion", account.getId());
                                     setAccountAutoPayOff(account.getId(), context);
                                 }
@@ -454,7 +454,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     }
 
                     if (!paymentMethodModel.getAccountId().equals(account.getId())) {
-                        throw new PaymentApiException(ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID, paymentMethodId);
+                        throw new PaymentApiException(ErrorCode.PAYMENT_METHOD_DIFFERENT_ACCOUNT_ID, paymentMethodId);
                     }
 
                     try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
index 4cd0f16..ce07214 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentProcessor.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.payment.core;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -42,9 +43,11 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.DefaultPayment;
+import org.killbill.billing.payment.api.DefaultPaymentAttempt;
 import org.killbill.billing.payment.api.DefaultPaymentTransaction;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentAttempt;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
@@ -57,9 +60,14 @@ import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+import org.killbill.billing.payment.dao.PluginPropertySerializer;
+import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
+import org.killbill.billing.payment.glue.DefaultPaymentService;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.payment.retry.DefaultRetryService;
+import org.killbill.billing.payment.retry.PaymentRetryNotificationKey;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
@@ -70,6 +78,11 @@ import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPagina
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
+import org.killbill.notificationq.api.NotificationEvent;
+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;
 
@@ -91,9 +104,13 @@ public class PaymentProcessor extends ProcessorBase {
 
     private final PaymentAutomatonRunner paymentAutomatonRunner;
     private final IncompletePaymentTransactionTask incompletePaymentTransactionTask;
+    private final NotificationQueueService notificationQueueService;
 
     private static final Logger log = LoggerFactory.getLogger(PaymentProcessor.class);
 
+    public static final String SCHEDULED = "SCHEDULED";
+
+
     @Inject
     public PaymentProcessor(final OSGIServiceRegistration<PaymentPluginApi> pluginRegistry,
                             final AccountInternalApi accountUserApi,
@@ -104,10 +121,12 @@ public class PaymentProcessor extends ProcessorBase {
                             final GlobalLocker locker,
                             final PaymentAutomatonRunner paymentAutomatonRunner,
                             final IncompletePaymentTransactionTask incompletePaymentTransactionTask,
+                            final NotificationQueueService notificationQueueService,
                             final Clock clock) {
         super(pluginRegistry, accountUserApi, paymentDao, tagUserApi, locker, internalCallContextFactory, invoiceApi, clock);
         this.paymentAutomatonRunner = paymentAutomatonRunner;
         this.incompletePaymentTransactionTask = incompletePaymentTransactionTask;
+        this.notificationQueueService = notificationQueueService;
     }
 
     public Payment createAuthorization(final boolean isApiPayment, @Nullable final UUID attemptId, final Account account, @Nullable final UUID paymentMethodId, @Nullable final UUID paymentId, final BigDecimal amount, final Currency currency,
@@ -163,12 +182,13 @@ public class PaymentProcessor extends ProcessorBase {
 
         final OperationResult overridePluginResult = isSuccess ? OperationResult.SUCCESS : OperationResult.FAILURE;
 
-        return performOperation(true, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(),
+        final boolean runJanitor = false;
+        return performOperation(true, runJanitor, null, transactionModelDao.getTransactionType(), account, null, transactionModelDao.getPaymentId(),
                                 transactionModelDao.getId(), transactionModelDao.getAmount(), transactionModelDao.getCurrency(), null, transactionModelDao.getTransactionExternalKey(), true,
                                 overridePluginResult, PLUGIN_PROPERTIES, callContext, internalCallContext);
     }
 
-    public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
+    public List<Payment> getAccountPayments(final UUID accountId, final boolean withPluginInfo, final boolean withAttempts, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
         final List<PaymentModelDao> paymentsModelDao = paymentDao.getPaymentsForAccount(accountId, tenantContext);
         final List<PaymentTransactionModelDao> transactionsModelDao = paymentDao.getTransactionsForAccount(accountId, tenantContext);
 
@@ -195,7 +215,7 @@ public class PaymentProcessor extends ProcessorBase {
                                                                                                         pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, context);
                                                                                                     }
 
-                                                                                                    return toPayment(paymentModelDao, transactionsModelDao, pluginInfo, tenantContext);
+                                                                                                    return toPayment(paymentModelDao, transactionsModelDao, pluginInfo, withAttempts, tenantContext);
                                                                                                 }
                                                                                             });
 
@@ -203,24 +223,24 @@ public class PaymentProcessor extends ProcessorBase {
         return ImmutableList.<Payment>copyOf(transformedPayments);
     }
 
-    public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+    public Payment getPayment(final UUID paymentId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
         final PaymentModelDao paymentModelDao = paymentDao.getPayment(paymentId, internalTenantContext);
         if (paymentModelDao == null) {
             return null;
         }
-        return toPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
+        return toPayment(paymentModelDao, withPluginInfo, withAttempts, properties, tenantContext, internalTenantContext);
     }
 
-    public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+    public Payment getPaymentByExternalKey(final String paymentExternalKey, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
         final PaymentModelDao paymentModelDao = paymentDao.getPaymentByExternalKey(paymentExternalKey, internalTenantContext);
         if (paymentModelDao == null) {
             return null;
         }
-        return toPayment(paymentModelDao, withPluginInfo, properties, tenantContext, internalTenantContext);
+        return toPayment(paymentModelDao, withPluginInfo, withAttempts, properties, tenantContext, internalTenantContext);
     }
 
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties,
-                                           final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final boolean withPluginInfo, final boolean withAttempts,
+                                           final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         return getEntityPaginationFromPlugins(true,
                                               getAvailablePlugins(),
                                               offset,
@@ -228,13 +248,13 @@ public class PaymentProcessor extends ProcessorBase {
                                               new EntityPaginationBuilder<Payment, PaymentApiException>() {
                                                   @Override
                                                   public Pagination<Payment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                      return getPayments(offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
+                                                      return getPayments(offset, limit, pluginName, withPluginInfo, withAttempts, properties, tenantContext, internalTenantContext);
                                                   }
                                               }
                                              );
     }
 
-    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+    public Pagination<Payment> getPayments(final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
         final PaymentPluginApi pluginApi = withPluginInfo ? getPaymentPluginApi(pluginName) : null;
 
         return getEntityPagination(limit,
@@ -249,13 +269,13 @@ public class PaymentProcessor extends ProcessorBase {
                                        @Override
                                        public Payment apply(final PaymentModelDao paymentModelDao) {
                                            final List<PaymentTransactionInfoPlugin> pluginInfo = getPaymentTransactionInfoPluginsIfNeeded(pluginApi, paymentModelDao, tenantContext);
-                                           return toPayment(paymentModelDao.getId(), pluginInfo, internalTenantContext);
+                                           return toPayment(paymentModelDao.getId(), pluginInfo, withAttempts, internalTenantContext);
                                        }
                                    }
                                   );
     }
 
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) {
         if (withPluginInfo) {
             return getEntityPaginationFromPlugins(false,
                                                   getAvailablePlugins(),
@@ -264,7 +284,7 @@ public class PaymentProcessor extends ProcessorBase {
                                                   new EntityPaginationBuilder<Payment, PaymentApiException>() {
                                                       @Override
                                                       public Pagination<Payment> build(final Long offset, final Long limit, final String pluginName) throws PaymentApiException {
-                                                          return searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, properties, tenantContext, internalTenantContext);
+                                                          return searchPayments(searchKey, offset, limit, pluginName, withPluginInfo, withAttempts, properties, tenantContext, internalTenantContext);
                                                       }
                                                   }
                                                  );
@@ -280,7 +300,7 @@ public class PaymentProcessor extends ProcessorBase {
                                            new Function<PaymentModelDao, Payment>() {
                                                @Override
                                                public Payment apply(final PaymentModelDao paymentModelDao) {
-                                                   return toPayment(paymentModelDao.getId(), null, internalTenantContext);
+                                                   return toPayment(paymentModelDao.getId(), null, withAttempts, internalTenantContext);
                                                }
                                            }
                                           );
@@ -291,7 +311,8 @@ public class PaymentProcessor extends ProcessorBase {
         }
     }
 
-    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+    public Pagination<Payment> searchPayments(final String searchKey, final Long offset, final Long limit, final String pluginName, final boolean withPluginInfo,
+                                              final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
         final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
 
         return getEntityPagination(limit,
@@ -322,7 +343,7 @@ public class PaymentProcessor extends ProcessorBase {
                                                cachedPaymentTransactions.add(pluginTransaction);
                                                return null;
                                            } else {
-                                               final Payment result = toPayment(pluginTransaction.getKbPaymentId(), withPluginInfo ? ImmutableList.<PaymentTransactionInfoPlugin>copyOf(cachedPaymentTransactions) : ImmutableList.<PaymentTransactionInfoPlugin>of(), internalTenantContext);
+                                               final Payment result = toPayment(pluginTransaction.getKbPaymentId(), withPluginInfo ? ImmutableList.<PaymentTransactionInfoPlugin>copyOf(cachedPaymentTransactions) : ImmutableList.<PaymentTransactionInfoPlugin>of(), withAttempts, internalTenantContext);
                                                cachedPaymentTransactions.clear();
                                                cachedPaymentTransactions.add(pluginTransaction);
                                                return result;
@@ -332,7 +353,75 @@ public class PaymentProcessor extends ProcessorBase {
                                   );
     }
 
+    public void cancelScheduledPaymentTransaction(@Nullable final UUID paymentTransactionId, @Nullable final String paymentTransactionExternalKey, final CallContext callContext) throws PaymentApiException {
+
+        final InternalCallContext internalCallContextWithoutAccountRecordId = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(callContext);
+        final String effectivePaymentTransactionExternalKey;
+        if (paymentTransactionExternalKey == null) {
+            final PaymentTransactionModelDao transaction = paymentDao.getPaymentTransaction(paymentTransactionId, internalCallContextWithoutAccountRecordId);
+            effectivePaymentTransactionExternalKey = transaction.getTransactionExternalKey();
+        } else {
+            effectivePaymentTransactionExternalKey = paymentTransactionExternalKey;
+        }
+
+        final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttemptByTransactionExternalKey(effectivePaymentTransactionExternalKey, internalCallContextWithoutAccountRecordId);
+        if (attempts.isEmpty()) {
+            return;
+        }
+
+        final PaymentAttemptModelDao lastPaymentAttempt =  attempts.get(attempts.size() - 1);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(lastPaymentAttempt.getAccountId(), callContext);
+
+        try {
+            final NotificationQueue retryQueue = notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, DefaultRetryService.QUEUE_NAME);
+            final List<NotificationEventWithMetadata<NotificationEvent>> notificationEventWithMetadatas =
+                    retryQueue.getFutureNotificationForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
+
+            for (final NotificationEventWithMetadata<NotificationEvent> notificationEvent : notificationEventWithMetadatas) {
+                if (((PaymentRetryNotificationKey) notificationEvent.getEvent()).getAttemptId().equals(lastPaymentAttempt.getId())) {
+                    retryQueue.removeNotification(notificationEvent.getRecordId());
+                    break;
+                }
+            }
+        } catch (final NoSuchNotificationQueue noSuchNotificationQueue) {
+            log.error("ERROR Loading Notification Queue - " + noSuchNotificationQueue.getMessage());
+            throw new IllegalStateException(noSuchNotificationQueue);
+        }
+    }
+
+    public Payment getPaymentByTransactionId(final UUID transactionId, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+        final PaymentTransactionModelDao paymentTransactionDao = paymentDao.getPaymentTransaction(transactionId, internalTenantContext);
+        if (null != paymentTransactionDao) {
+            PaymentModelDao paymentModelDao = paymentDao.getPayment(paymentTransactionDao.getPaymentId(), internalTenantContext);
+            return toPayment(paymentModelDao, withPluginInfo, withAttempts, properties, tenantContext, internalTenantContext);
+        }
+        return null;
+    }
+
+    private Payment performOperation(final boolean isApiPayment,
+                                     @Nullable final UUID attemptId,
+                                     final TransactionType transactionType,
+                                     final Account account,
+                                     @Nullable final UUID paymentMethodId,
+                                     @Nullable final UUID paymentId,
+                                     @Nullable final UUID transactionId,
+                                     @Nullable final BigDecimal amount,
+                                     @Nullable final Currency currency,
+                                     @Nullable final String paymentExternalKey,
+                                     @Nullable final String paymentTransactionExternalKey,
+                                     final boolean shouldLockAccountAndDispatch,
+                                     @Nullable final OperationResult overridePluginOperationResult,
+                                     final Iterable<PluginProperty> properties,
+                                     final CallContext callContext,
+                                     final InternalCallContext internalCallContext) throws PaymentApiException {
+        boolean runJanitor = true;
+        return performOperation(isApiPayment, runJanitor, attemptId, transactionType, account, paymentMethodId, paymentId,
+                                transactionId, amount, currency, paymentExternalKey, paymentTransactionExternalKey,
+                                shouldLockAccountAndDispatch, overridePluginOperationResult, properties, callContext, internalCallContext);
+    }
+
     private Payment performOperation(final boolean isApiPayment,
+                                     final boolean runJanitor,
                                      @Nullable final UUID attemptId,
                                      final TransactionType transactionType,
                                      final Account account,
@@ -372,8 +461,7 @@ public class PaymentProcessor extends ProcessorBase {
 
             // Sanity: verify the payment belongs to the right account (in case it was looked-up by payment or transaction external key)
             if (!paymentModelDao.getAccountRecordId().equals(internalCallContext.getAccountRecordId())) {
-                // TODO 0.17.x New ErrorCode (it's not necessarily the transaction external key that matches)
-                throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, paymentStateContext.getPaymentTransactionExternalKey());
+                throw new PaymentApiException(ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID, paymentStateContext.getPaymentId());
             }
 
             if (paymentStateContext.getPaymentTransactionExternalKey() != null) {
@@ -389,9 +477,11 @@ public class PaymentProcessor extends ProcessorBase {
                 if (transactionToComplete != null) {
                     // For completion calls, always invoke the Janitor first to get the latest state. The state machine will then
                     // prevent disallowed transitions in case the state couldn't be fixed (or if it's already in a final state).
-                    final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), internalCallContext);
-                    final List<PaymentTransactionInfoPlugin> pluginTransactions = getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
-                    paymentModelDao = invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, internalCallContext);
+                    if (runJanitor) {
+                        final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), internalCallContext);
+                        final List<PaymentTransactionInfoPlugin> pluginTransactions = getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, callContext);
+                        paymentModelDao = invokeJanitor(paymentModelDao, paymentTransactionsForCurrentPayment, pluginTransactions, internalCallContext);
+                    }
 
                     final UUID transactionToCompleteId = transactionToComplete.getId();
                     transactionToComplete = Iterables.<PaymentTransactionModelDao>find(paymentTransactionsForCurrentPayment,
@@ -417,14 +507,9 @@ public class PaymentProcessor extends ProcessorBase {
             currentStateName = paymentModelDao.getLastSuccessStateName();
         }
 
-        // Sanity: no paymentMethodId was passed through API and account does not have a default paymentMethodId
-        if (paymentStateContext.getPaymentMethodId() == null) {
-            throw new PaymentApiException(ErrorCode.PAYMENT_NO_DEFAULT_PAYMENT_METHOD, paymentStateContext.getAccount().getId());
-        }
-
         final UUID nonNullPaymentId = paymentAutomatonRunner.run(paymentStateContext, daoHelper, currentStateName, transactionType);
 
-        return getPayment(nonNullPaymentId, true, properties, callContext, internalCallContext);
+        return getPayment(nonNullPaymentId, true, false, properties, callContext, internalCallContext);
     }
 
     private void runSanityOnTransactionExternalKey(final Iterable<PaymentTransactionModelDao> allPaymentTransactionsForKey,
@@ -447,7 +532,7 @@ public class PaymentProcessor extends ProcessorBase {
                     log.warn("Unable to retrieve account", e);
                     accountId = null;
                 }
-                throw new PaymentApiException(ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS, accountId);
+                throw new PaymentApiException(ErrorCode.PAYMENT_TRANSACTION_DIFFERENT_ACCOUNT_ID, accountId);
             }
         }
     }
@@ -510,29 +595,30 @@ public class PaymentProcessor extends ProcessorBase {
     }
 
     // Used in bulk get APIs (getPayments / searchPayments)
-    private Payment toPayment(final UUID paymentId, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext tenantContext) {
+    private Payment toPayment(final UUID paymentId, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final boolean withAttempts, final InternalTenantContext tenantContext) {
         final PaymentModelDao paymentModelDao = paymentDao.getPayment(paymentId, tenantContext);
         if (paymentModelDao == null) {
             log.warn("Unable to find payment id " + paymentId);
             return null;
         }
 
-        return toPayment(paymentModelDao, pluginTransactions, tenantContext);
+        return toPayment(paymentModelDao, pluginTransactions, withAttempts, tenantContext);
     }
 
     // Used in single get APIs (getPayment / getPaymentByExternalKey)
-    private Payment toPayment(final PaymentModelDao paymentModelDao, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
+    private Payment toPayment(final PaymentModelDao paymentModelDao, final boolean withPluginInfo, final boolean withAttempts, final Iterable<PluginProperty> properties, final TenantContext context, final InternalTenantContext tenantContext) throws PaymentApiException {
         final PaymentPluginApi plugin = getPaymentProviderPlugin(paymentModelDao.getPaymentMethodId(), tenantContext);
         final List<PaymentTransactionInfoPlugin> pluginTransactions = withPluginInfo ? getPaymentTransactionInfoPlugins(plugin, paymentModelDao, properties, context) : null;
 
-        return toPayment(paymentModelDao, pluginTransactions, tenantContext);
+        return toPayment(paymentModelDao, pluginTransactions, withAttempts, tenantContext);
     }
 
-    private Payment toPayment(final PaymentModelDao paymentModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext tenantContext) {
+    private Payment toPayment(final PaymentModelDao paymentModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions,
+                              final boolean withAttempts, final InternalTenantContext tenantContext) {
         final InternalTenantContext tenantContextWithAccountRecordId = getInternalTenantContextWithAccountRecordId(paymentModelDao.getAccountId(), tenantContext);
         final List<PaymentTransactionModelDao> transactionsForPayment = paymentDao.getTransactionsForPayment(paymentModelDao.getId(), tenantContextWithAccountRecordId);
 
-        return toPayment(paymentModelDao, transactionsForPayment, pluginTransactions, tenantContextWithAccountRecordId);
+        return toPayment(paymentModelDao, transactionsForPayment, pluginTransactions, withAttempts, tenantContextWithAccountRecordId);
     }
 
     private PaymentModelDao invokeJanitor(final PaymentModelDao curPaymentModelDao, final Collection<PaymentTransactionModelDao> curTransactionsModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext internalTenantContext) {
@@ -570,9 +656,9 @@ public class PaymentProcessor extends ProcessorBase {
     }
 
     // Used in bulk get API (getAccountPayments)
-    private Payment toPayment(final PaymentModelDao curPaymentModelDao, final Collection<PaymentTransactionModelDao> curTransactionsModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final InternalTenantContext internalTenantContext) {
+    private Payment toPayment(final PaymentModelDao curPaymentModelDao, final Collection<PaymentTransactionModelDao> curTransactionsModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions, final boolean withAttempts, final InternalTenantContext internalTenantContext) {
         final Collection<PaymentTransactionModelDao> transactionsModelDao = new LinkedList<PaymentTransactionModelDao>(curTransactionsModelDao);
-        final PaymentModelDao newPaymentModelDao = invokeJanitor(curPaymentModelDao, transactionsModelDao, pluginTransactions, internalTenantContext);
+        invokeJanitor(curPaymentModelDao, transactionsModelDao, pluginTransactions, internalTenantContext);
 
         final Collection<PaymentTransaction> transactions = new LinkedList<PaymentTransaction>();
         for (final PaymentTransactionModelDao newPaymentTransactionModelDao : transactionsModelDao) {
@@ -603,15 +689,103 @@ public class PaymentProcessor extends ProcessorBase {
             }
         });
         final List<PaymentTransaction> sortedTransactions = perPaymentTransactionOrdering.immutableSortedCopy(transactions);
+        return new DefaultPayment(curPaymentModelDao.getId(),
+                                  curPaymentModelDao.getCreatedDate(),
+                                  curPaymentModelDao.getUpdatedDate(),
+                                  curPaymentModelDao.getAccountId(),
+                                  curPaymentModelDao.getPaymentMethodId(),
+                                  curPaymentModelDao.getPaymentNumber(),
+                                  curPaymentModelDao.getExternalKey(),
+                                  sortedTransactions,
+                                  (withAttempts && !sortedTransactions.isEmpty()) ?
+                                  getPaymentAttempts(paymentDao.getPaymentAttempts(curPaymentModelDao.getExternalKey(), internalTenantContext),
+                                                     internalTenantContext) : null
+                                  );
+    }
+
+    private List<PaymentAttempt> getPaymentAttempts(final List<PaymentAttemptModelDao> pastPaymentAttempts,
+                                                    final InternalTenantContext internalTenantContext) {
+
+        List<PaymentAttempt> paymentAttempts = new ArrayList<PaymentAttempt>();
+
+        // Add Past Payment Attempts
+        for (PaymentAttemptModelDao pastPaymentAttempt : pastPaymentAttempts) {
+            DefaultPaymentAttempt paymentAttempt = new DefaultPaymentAttempt(
+                    pastPaymentAttempt.getAccountId(),
+                    pastPaymentAttempt.getPaymentMethodId(),
+                    pastPaymentAttempt.getId(),
+                    pastPaymentAttempt.getCreatedDate(),
+                    pastPaymentAttempt.getUpdatedDate(),
+                    pastPaymentAttempt.getCreatedDate(),
+                    pastPaymentAttempt.getPaymentExternalKey(),
+                    pastPaymentAttempt.getTransactionId(),
+                    pastPaymentAttempt.getTransactionExternalKey(),
+                    pastPaymentAttempt.getTransactionType(),
+                    pastPaymentAttempt.getStateName(),
+                    pastPaymentAttempt.getAmount(),
+                    pastPaymentAttempt.getCurrency(),
+                    pastPaymentAttempt.getPluginName(),
+                    buildPluginProperties(pastPaymentAttempt));
+            paymentAttempts.add(paymentAttempt);
+        }
 
-        return new DefaultPayment(newPaymentModelDao.getId(),
-                                  newPaymentModelDao.getCreatedDate(),
-                                  newPaymentModelDao.getUpdatedDate(),
-                                  newPaymentModelDao.getAccountId(),
-                                  newPaymentModelDao.getPaymentMethodId(),
-                                  newPaymentModelDao.getPaymentNumber(),
-                                  newPaymentModelDao.getExternalKey(),
-                                  sortedTransactions);
+        // Get Future Payment Attempts from Notification Queue and add them to the list
+        try {
+            final NotificationQueue retryQueue = notificationQueueService.getNotificationQueue(DefaultPaymentService.SERVICE_NAME, DefaultRetryService.QUEUE_NAME);
+            final List<NotificationEventWithMetadata<NotificationEvent>> notificationEventWithMetadatas =
+                    retryQueue.getFutureNotificationForSearchKeys(internalTenantContext.getAccountRecordId(), internalTenantContext.getTenantRecordId());
+
+            for (final NotificationEventWithMetadata<NotificationEvent> notificationEvent : notificationEventWithMetadatas) {
+                // Last Attempt
+                PaymentAttemptModelDao lastPaymentAttempt = getLastPaymentAttempt(pastPaymentAttempts,
+                                                                                  ((PaymentRetryNotificationKey) notificationEvent.getEvent()).getAttemptId());
+
+                if (lastPaymentAttempt != null) {
+                    DefaultPaymentAttempt futurePaymentAttempt = new DefaultPaymentAttempt(
+                            lastPaymentAttempt.getAccountId(), // accountId
+                            lastPaymentAttempt.getPaymentMethodId(), // paymentMethodId
+                            ((PaymentRetryNotificationKey) notificationEvent.getEvent()).getAttemptId(), // id
+                            null, // createdDate
+                            null, // updatedDate
+                            notificationEvent.getEffectiveDate(), // effectiveDate
+                            lastPaymentAttempt.getPaymentExternalKey(), // paymentExternalKey
+                            null, // transactionId
+                            lastPaymentAttempt.getTransactionExternalKey(), // transactionExternalKey
+                            lastPaymentAttempt.getTransactionType(), // transactionType
+                            SCHEDULED, // stateName
+                            lastPaymentAttempt.getAmount(), // amount
+                            lastPaymentAttempt.getCurrency(), // currency
+                            ((PaymentRetryNotificationKey) notificationEvent.getEvent()).getPaymentControlPluginNames().get(0), // pluginName,
+                            buildPluginProperties(lastPaymentAttempt)); // pluginProperties
+                    paymentAttempts.add(futurePaymentAttempt);
+                }
+            }
+        } catch (NoSuchNotificationQueue noSuchNotificationQueue) {
+            log.error("ERROR Loading Notification Queue - " + noSuchNotificationQueue.getMessage());
+        }
+        return paymentAttempts;
+    }
+
+    private PaymentAttemptModelDao getLastPaymentAttempt(final List<PaymentAttemptModelDao> pastPaymentAttempts, final UUID attemptId) {
+        if (!pastPaymentAttempts.isEmpty()) {
+            for (int i = pastPaymentAttempts.size() - 1; i >= 0; i--) {
+                if (pastPaymentAttempts.get(i).getId().equals(attemptId)) {
+                    return pastPaymentAttempts.get(i);
+                }
+            }
+        }
+        return null;
+    }
+
+    private List<PluginProperty> buildPluginProperties(final PaymentAttemptModelDao pastPaymentAttempt) {
+        if (pastPaymentAttempt.getPluginProperties() != null) {
+            try {
+                return Lists.newArrayList(PluginPropertySerializer.deserialize(pastPaymentAttempt.getPluginProperties()));
+            } catch (PluginPropertySerializerException e) {
+                log.error("ERROR Deserializing Plugin Properties - " + e.getMessage());
+            }
+        }
+        return null;
     }
 
     private PaymentTransactionInfoPlugin findPaymentTransactionInfoPlugin(final PaymentTransactionModelDao paymentTransactionModelDao, @Nullable final Iterable<PaymentTransactionInfoPlugin> pluginTransactions) {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
index 17e87d8..22e12ff 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PluginControlPaymentProcessor.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -27,6 +27,7 @@ import javax.inject.Inject;
 
 import org.killbill.automaton.MissingEntryException;
 import org.killbill.automaton.State;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
@@ -36,15 +37,18 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.sm.PaymentControlStateMachineHelper;
 import org.killbill.billing.payment.core.sm.PluginControlPaymentAutomatonRunner;
+import org.killbill.billing.payment.core.sm.PluginControlPaymentAutomatonRunner.ControlOperation;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PluginPropertySerializer;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
+import org.killbill.billing.payment.invoice.InvoicePaymentControlPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.callcontext.CallContext;
@@ -55,7 +59,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logEnterAPICall;
+import static org.killbill.billing.payment.logging.PaymentLoggingHelper.logExitAPICall;
 
 public class PluginControlPaymentProcessor extends ProcessorBase {
 
@@ -86,6 +96,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                        final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.AUTHORIZE,
+                                                          ControlOperation.AUTHORIZE,
                                                           account,
                                                           paymentMethodId,
                                                           paymentId,
@@ -104,6 +115,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                  final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.CAPTURE,
+                                                          ControlOperation.CAPTURE,
                                                           account,
                                                           null,
                                                           paymentId,
@@ -121,6 +133,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                   final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.PURCHASE,
+                                                          ControlOperation.PURCHASE,
                                                           account,
                                                           paymentMethodId,
                                                           paymentId,
@@ -137,6 +150,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                               final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.VOID,
+                                                          ControlOperation.VOID,
                                                           account,
                                                           null,
                                                           paymentId,
@@ -153,6 +167,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                 final Iterable<PluginProperty> properties, final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.REFUND,
+                                                          ControlOperation.REFUND,
                                                           account,
                                                           null,
                                                           paymentId,
@@ -170,6 +185,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
 
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.CREDIT,
+                                                          ControlOperation.CREDIT,
                                                           account,
                                                           paymentMethodId,
                                                           paymentId,
@@ -186,6 +202,7 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                     final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
                                                           TransactionType.CHARGEBACK,
+                                                          ControlOperation.CHARGEBACK,
                                                           account,
                                                           null,
                                                           paymentId,
@@ -198,44 +215,109 @@ public class PluginControlPaymentProcessor extends ProcessorBase {
                                                           callContext, internalCallContext);
     }
 
+    public Payment createChargebackReversal(final boolean isApiPayment, final Account account, final UUID paymentId, final String transactionExternalKey,
+                                            final List<String> paymentControlPluginNames, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
+        return pluginControlledPaymentAutomatonRunner.run(isApiPayment,
+                                                          TransactionType.CHARGEBACK,
+                                                          ControlOperation.CHARGEBACK_REVERSAL,
+                                                          account,
+                                                          null,
+                                                          paymentId,
+                                                          null,
+                                                          transactionExternalKey,
+                                                          null,
+                                                          null,
+                                                          ImmutableList.<PluginProperty>of(),
+                                                          paymentControlPluginNames,
+                                                          callContext,
+                                                          internalCallContext);
+    }
+
     public void retryPaymentTransaction(final UUID attemptId, final List<String> paymentControlPluginNames, final InternalCallContext internalCallContext) {
-        try {
-            final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
-            final PaymentModelDao payment = paymentDao.getPaymentByExternalKey(attempt.getPaymentExternalKey(), internalCallContext);
-            final UUID paymentId = payment != null ? payment.getId() : null;
+        final PaymentAttemptModelDao attempt = paymentDao.getPaymentAttempt(attemptId, internalCallContext);
+        log.info("Retrying attemptId='{}', paymentExternalKey='{}', transactionExternalKey='{}'. paymentControlPluginNames='{}'",
+                 attemptId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), paymentControlPluginNames);
 
-            final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
-            final Account account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
-            final CallContext callContext = buildCallContext(internalCallContext);
+        final PaymentModelDao paymentModelDao = paymentDao.getPaymentByExternalKey(attempt.getPaymentExternalKey(), internalCallContext);
+        final UUID paymentId = paymentModelDao != null ? paymentModelDao.getId() : null;
 
+        final CallContext callContext = buildCallContext(internalCallContext);
+
+        final String transactionType = TransactionType.PURCHASE.name();
+        Account account = null;
+        Payment payment = null;
+        PaymentTransaction paymentTransaction = null;
+        try {
+            account = accountInternalApi.getAccountById(attempt.getAccountId(), internalCallContext);
             final State state = paymentControlStateMachineHelper.getState(attempt.getStateName());
+            final Iterable<PluginProperty> pluginProperties = PluginPropertySerializer.deserialize(attempt.getPluginProperties());
 
-            log.debug("Retrying attemptId={}, paymentExternalKey={}, transactionExternalKey={}. paymentControlPluginNames={}, now={}",
-                      attemptId, attempt.getPaymentExternalKey(), attempt.getTransactionExternalKey(), paymentControlPluginNames, clock.getUTCNow());
-
-            pluginControlledPaymentAutomatonRunner.run(state,
-                                                       false,
-                                                       attempt.getTransactionType(),
-                                                       account,
-                                                       attempt.getPaymentMethodId(),
-                                                       paymentId,
-                                                       attempt.getPaymentExternalKey(),
-                                                       attempt.getTransactionExternalKey(),
-                                                       attempt.getAmount(),
-                                                       attempt.getCurrency(),
-                                                       pluginProperties,
-                                                       paymentControlPluginNames,
-                                                       callContext,
-                                                       internalCallContext);
+            logEnterAPICall(log,
+                            transactionType,
+                            account,
+                            attempt.getPaymentMethodId(),
+                            paymentId,
+                            null,
+                            attempt.getAmount(),
+                            attempt.getCurrency(),
+                            attempt.getPaymentExternalKey(),
+                            attempt.getTransactionExternalKey(),
+                            null,
+                            paymentControlPluginNames);
 
+            payment = pluginControlledPaymentAutomatonRunner.run(state,
+                                                                 false,
+                                                                 attempt.getTransactionType(),
+                                                                 ControlOperation.valueOf(attempt.getTransactionType().toString()),
+                                                                 account,
+                                                                 attempt.getPaymentMethodId(),
+                                                                 paymentId,
+                                                                 attempt.getPaymentExternalKey(),
+                                                                 attempt.getTransactionExternalKey(),
+                                                                 attempt.getAmount(),
+                                                                 attempt.getCurrency(),
+                                                                 pluginProperties,
+                                                                 paymentControlPluginNames,
+                                                                 callContext,
+                                                                 internalCallContext);
+
+            paymentTransaction = Iterables.<PaymentTransaction>find(Lists.<PaymentTransaction>reverse(payment.getTransactions()),
+                                                                    new Predicate<PaymentTransaction>() {
+                                                                        @Override
+                                                                        public boolean apply(final PaymentTransaction input) {
+                                                                            return attempt.getTransactionExternalKey().equals(input.getExternalKey());
+                                                                        }
+                                                                    });
         } catch (final AccountApiException e) {
             log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         } catch (final PaymentApiException e) {
-            log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
+            // Log exception unless nothing left to be paid
+            if (e.getCode() == ErrorCode.PAYMENT_PLUGIN_API_ABORTED.getCode() &&
+                paymentControlPluginNames != null &&
+                paymentControlPluginNames.size() == 1 &&
+                InvoicePaymentControlPluginApi.PLUGIN_NAME.equals(paymentControlPluginNames.get(0))) {
+                log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'. Invoice has already been paid", attemptId, toPluginNamesOnError(paymentControlPluginNames));
+            } else {
+                log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
+            }
         } catch (final PluginPropertySerializerException e) {
             log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
         } catch (final MissingEntryException e) {
             log.warn("Failed to retry attemptId='{}', paymentControlPlugins='{}'", attemptId, toPluginNamesOnError(paymentControlPluginNames), e);
+        } finally {
+            logExitAPICall(log,
+                           transactionType,
+                           account,
+                           payment != null ? payment.getPaymentMethodId() : null,
+                           payment != null ? payment.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getId() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedAmount() : null,
+                           paymentTransaction != null ? paymentTransaction.getProcessedCurrency() : null,
+                           payment != null ? payment.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getExternalKey() : null,
+                           paymentTransaction != null ? paymentTransaction.getTransactionStatus() : null,
+                           paymentControlPluginNames,
+                           null);
         }
     }
 
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 31ac463..a18340b 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
@@ -43,7 +43,7 @@ import org.killbill.billing.util.api.TagApiException;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.globallocker.LockerType;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
index 4f05f5e..8da4e82 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/AuthorizeControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class AuthorizeControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
index 54f133f..36776b3 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CaptureControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class CaptureControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
index 4836a2b..99e6a97 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackControlOperation.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -18,13 +18,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class ChargebackControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java
new file mode 100644
index 0000000..a0af618
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/ChargebackReversalControlOperation.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.payment.core.sm.control;
+
+import org.killbill.automaton.OperationResult;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.core.PaymentProcessor;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.util.config.definition.PaymentConfig;
+import org.killbill.commons.locker.GlobalLocker;
+
+public class ChargebackReversalControlOperation extends OperationControlCallback {
+
+    public ChargebackReversalControlOperation(final GlobalLocker locker,
+                                              final PluginDispatcher<OperationResult> paymentPluginDispatcher,
+                                              final PaymentConfig paymentConfig,
+                                              final PaymentStateControlContext paymentStateContext,
+                                              final PaymentProcessor paymentProcessor,
+                                              final ControlPluginRunner controlPluginRunner) {
+        super(locker, paymentPluginDispatcher, paymentStateContext, paymentProcessor, paymentConfig, controlPluginRunner);
+    }
+
+    @Override
+    protected Payment doCallSpecificOperationCallback() throws PaymentApiException {
+        return paymentProcessor.createChargebackReversal(paymentStateControlContext.isApiPayment(),
+                                                         paymentStateControlContext.getAttemptId(),
+                                                         paymentStateControlContext.getAccount(),
+                                                         paymentStateControlContext.getPaymentId(),
+                                                         paymentStateControlContext.getPaymentTransactionExternalKey(),
+                                                         paymentStateControlContext.getAmount(),
+                                                         paymentStateControlContext.getCurrency(),
+                                                         false,
+                                                         paymentStateControlContext.getCallContext(),
+                                                         paymentStateControlContext.getInternalCallContext());
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
index c8bbf8f..fd8eed7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CompletionControlOperation.java
@@ -31,7 +31,7 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.control.plugin.api.PaymentControlContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 import com.google.common.base.Joiner;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
index 5d2c643..9e91396 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/CreditControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class CreditControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
index 13ad2dc..4f05018 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/OperationControlCallback.java
@@ -44,7 +44,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.core.sm.control.ControlPluginRunner.DefaultPaymentControlContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
index 4a766c7..401b780 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/PurchaseControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class PurchaseControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
index 4c60dd3..64fda3f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/RefundControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class RefundControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
index db74fa9..f600e8b 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/control/VoidControlOperation.java
@@ -17,13 +17,11 @@
 package org.killbill.billing.payment.core.sm.control;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 
 public class VoidControlOperation extends OperationControlCallback {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
index 7eb4821..39060ad 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
@@ -28,7 +28,7 @@ import org.killbill.billing.payment.core.ProcessorBase.DispatcherCallback;
 import org.killbill.billing.payment.dispatcher.PaymentPluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
index fa55ff0..c546b0c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonDAOHelper.java
@@ -149,13 +149,14 @@ public class PaymentAutomatonDAOHelper {
         paymentStateContext.setPaymentTransactionModelDao(paymentDao.getPaymentTransaction(paymentStateContext.getPaymentTransactionModelDao().getId(), internalCallContext));
     }
 
-    public String getPaymentProviderPluginName() throws PaymentApiException {
+    public String getPaymentProviderPluginName(final boolean includeDeteled) throws PaymentApiException {
         if (pluginName != null) {
             return pluginName;
         }
 
         final UUID paymentMethodId = paymentStateContext.getPaymentMethodId();
-        final PaymentMethodModelDao methodDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, internalCallContext);
+        final PaymentMethodModelDao methodDao = (includeDeteled) ? paymentDao.getPaymentMethodIncludedDeleted(paymentMethodId, internalCallContext) :
+                paymentDao.getPaymentMethod(paymentMethodId, internalCallContext);
         if (methodDao == null) {
             throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
         }
@@ -164,7 +165,7 @@ public class PaymentAutomatonDAOHelper {
     }
 
     public PaymentPluginApi getPaymentPluginApi() throws PaymentApiException {
-        final String pluginName = getPaymentProviderPluginName();
+        final String pluginName = getPaymentProviderPluginName(false);
         return getPaymentPluginApi(pluginName);
     }
 
@@ -228,4 +229,8 @@ public class PaymentAutomatonDAOHelper {
                                               gatewayErrorCode,
                                               gatewayErrorMsg);
     }
+
+    public String getPluginName() {
+        return pluginName;
+    }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
index 04809ca..c1304a7 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentAutomatonRunner.java
@@ -73,7 +73,7 @@ import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
@@ -128,6 +128,7 @@ public class PaymentAutomatonRunner {
                                                         final Iterable<PluginProperty> properties,
                                                         final CallContext callContext,
                                                         final InternalCallContext internalCallContext) throws PaymentApiException {
+
         // Retrieve the payment id from the payment external key if needed
         final UUID effectivePaymentId = paymentId != null ? paymentId : retrievePaymentId(paymentExternalKey, paymentTransactionExternalKey, internalCallContext);
 
@@ -165,6 +166,7 @@ public class PaymentAutomatonRunner {
         final OperationCallback operationCallback;
         final LeavingStateCallback leavingStateCallback;
         final EnteringStateCallback enteringStateCallback;
+        Boolean includeDeletedPaymentMethod = Boolean.FALSE;
         switch (transactionType) {
             case PURCHASE:
                 operationCallback = new PurchaseOperation(daoHelper, locker, paymentPluginDispatcher, paymentConfig, paymentStateContext);
@@ -200,12 +202,13 @@ public class PaymentAutomatonRunner {
                 operationCallback = new ChargebackOperation(daoHelper, locker, paymentPluginDispatcher, paymentConfig, paymentStateContext);
                 leavingStateCallback = new ChargebackInitiated(daoHelper, paymentStateContext);
                 enteringStateCallback = new ChargebackCompleted(daoHelper, paymentStateContext);
+                includeDeletedPaymentMethod = Boolean.TRUE;
                 break;
             default:
                 throw new IllegalStateException("Unsupported transaction type " + transactionType);
         }
 
-        runStateMachineOperation(currentStateName, transactionType, leavingStateCallback, operationCallback, enteringStateCallback, paymentStateContext, daoHelper);
+        runStateMachineOperation(currentStateName, transactionType, leavingStateCallback, operationCallback, enteringStateCallback, includeDeletedPaymentMethod, paymentStateContext, daoHelper);
 
         return paymentStateContext.getPaymentId();
     }
@@ -226,10 +229,11 @@ public class PaymentAutomatonRunner {
                                           final LeavingStateCallback leavingStateCallback,
                                           final OperationCallback operationCallback,
                                           final EnteringStateCallback enteringStateCallback,
+                                          final Boolean includeDeletedPaymentMethod,
                                           final PaymentStateContext paymentStateContext,
                                           final PaymentAutomatonDAOHelper daoHelper) throws PaymentApiException {
         try {
-            final StateMachineConfig stateMachineConfig = paymentSMHelper.getStateMachineConfig(daoHelper.getPaymentProviderPluginName(), paymentStateContext.getInternalCallContext());
+            final StateMachineConfig stateMachineConfig = paymentSMHelper.getStateMachineConfig(daoHelper.getPaymentProviderPluginName(includeDeletedPaymentMethod), paymentStateContext.getInternalCallContext());
             final StateMachine initialStateMachine = stateMachineConfig.getStateMachineForState(initialStateName);
             final State initialState = initialStateMachine.getState(initialStateName);
             final Operation operation = paymentSMHelper.getOperationForTransaction(stateMachineConfig, transactionType);
@@ -284,4 +288,5 @@ public class PaymentAutomatonRunner {
 
         return paymentIdCandidate;
     }
+
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/AuthorizeOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/AuthorizeOperation.java
index d8eb405..d8cf528 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/AuthorizeOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/AuthorizeOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +44,13 @@ public class AuthorizeOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting AUTHORIZE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.authorizePayment(paymentStateContext.getAccount().getId(),
-                                       paymentStateContext.getPaymentId(),
-                                       paymentStateContext.getTransactionId(),
-                                       paymentStateContext.getPaymentMethodId(),
-                                       paymentStateContext.getAmount(),
-                                       paymentStateContext.getCurrency(),
-                                       paymentStateContext.getProperties(),
-                                       paymentStateContext.getCallContext());
+        return paymentPluginApi.authorizePayment(paymentStateContext.getAccount().getId(),
+                                                 paymentStateContext.getPaymentId(),
+                                                 paymentStateContext.getTransactionId(),
+                                                 paymentStateContext.getPaymentMethodId(),
+                                                 paymentStateContext.getAmount(),
+                                                 paymentStateContext.getCurrency(),
+                                                 paymentStateContext.getProperties(),
+                                                 paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CaptureOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CaptureOperation.java
index e6b67c5..6f4440d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CaptureOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CaptureOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +44,13 @@ public class CaptureOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting CAPTURE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.capturePayment(paymentStateContext.getAccount().getId(),
-                                     paymentStateContext.getPaymentId(),
-                                     paymentStateContext.getTransactionId(),
-                                     paymentStateContext.getPaymentMethodId(),
-                                     paymentStateContext.getAmount(),
-                                     paymentStateContext.getCurrency(),
-                                     paymentStateContext.getProperties(),
-                                     paymentStateContext.getCallContext());
+        return paymentPluginApi.capturePayment(paymentStateContext.getAccount().getId(),
+                                               paymentStateContext.getPaymentId(),
+                                               paymentStateContext.getTransactionId(),
+                                               paymentStateContext.getPaymentMethodId(),
+                                               paymentStateContext.getAmount(),
+                                               paymentStateContext.getCurrency(),
+                                               paymentStateContext.getProperties(),
+                                               paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
index cf477d5..230e73a 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/ChargebackOperation.java
@@ -27,7 +27,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CreditOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CreditOperation.java
index 53b5634..f98908e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CreditOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/CreditOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +44,13 @@ public class CreditOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting CREDIT for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.creditPayment(paymentStateContext.getAccount().getId(),
-                                    paymentStateContext.getPaymentId(),
-                                    paymentStateContext.getTransactionId(),
-                                    paymentStateContext.getPaymentMethodId(),
-                                    paymentStateContext.getAmount(),
-                                    paymentStateContext.getCurrency(),
-                                    paymentStateContext.getProperties(),
-                                    paymentStateContext.getCallContext());
+        return paymentPluginApi.creditPayment(paymentStateContext.getAccount().getId(),
+                                              paymentStateContext.getPaymentId(),
+                                              paymentStateContext.getTransactionId(),
+                                              paymentStateContext.getPaymentMethodId(),
+                                              paymentStateContext.getAmount(),
+                                              paymentStateContext.getCurrency(),
+                                              paymentStateContext.getProperties(),
+                                              paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
index 434fd89..cd77f0c 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PaymentOperation.java
@@ -40,7 +40,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.payment.provider.DefaultNoOpPaymentInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,7 +55,7 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
     private final Logger logger = LoggerFactory.getLogger(PaymentOperation.class);
 
     protected final PaymentAutomatonDAOHelper daoHelper;
-    protected PaymentPluginApi plugin;
+    protected PaymentPluginApi paymentPluginApi;
 
     protected PaymentOperation(final GlobalLocker locker,
                                final PaymentAutomatonDAOHelper daoHelper,
@@ -68,17 +68,15 @@ public abstract class PaymentOperation extends OperationCallbackBase<PaymentTran
 
     @Override
     public OperationResult doOperationCallback() throws OperationException {
-        final String pluginName;
         try {
-            pluginName = daoHelper.getPaymentProviderPluginName();
-            this.plugin = daoHelper.getPaymentPluginApi();
+            this.paymentPluginApi = daoHelper.getPaymentPluginApi();
         } catch (final PaymentApiException e) {
             throw convertToUnknownTransactionStatusAndErroredPaymentState(e);
         }
 
         if (paymentStateContext.shouldLockAccountAndDispatch()) {
             // This will already call unwrapExceptionFromDispatchedTask
-            return doOperationCallbackWithDispatchAndAccountLock(pluginName);
+            return doOperationCallbackWithDispatchAndAccountLock(daoHelper.getPluginName());
         } else {
             try {
                 return doSimpleOperationCallback();
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PurchaseOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PurchaseOperation.java
index 1ddf4b1..aff762f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PurchaseOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/PurchaseOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +44,13 @@ public class PurchaseOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting PURCHASE for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.purchasePayment(paymentStateContext.getAccount().getId(),
-                                      paymentStateContext.getPaymentId(),
-                                      paymentStateContext.getTransactionId(),
-                                      paymentStateContext.getPaymentMethodId(),
-                                      paymentStateContext.getAmount(),
-                                      paymentStateContext.getCurrency(),
-                                      paymentStateContext.getProperties(),
-                                      paymentStateContext.getCallContext());
+        return paymentPluginApi.purchasePayment(paymentStateContext.getAccount().getId(),
+                                                paymentStateContext.getPaymentId(),
+                                                paymentStateContext.getTransactionId(),
+                                                paymentStateContext.getPaymentMethodId(),
+                                                paymentStateContext.getAmount(),
+                                                paymentStateContext.getCurrency(),
+                                                paymentStateContext.getProperties(),
+                                                paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/RefundOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/RefundOperation.java
index 22d9239..1d86abb 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/RefundOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/RefundOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,13 +44,13 @@ public class RefundOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting REFUND for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.refundPayment(paymentStateContext.getAccount().getId(),
-                                    paymentStateContext.getPaymentId(),
-                                    paymentStateContext.getTransactionId(),
-                                    paymentStateContext.getPaymentMethodId(),
-                                    paymentStateContext.getAmount(),
-                                    paymentStateContext.getCurrency(),
-                                    paymentStateContext.getProperties(),
-                                    paymentStateContext.getCallContext());
+        return paymentPluginApi.refundPayment(paymentStateContext.getAccount().getId(),
+                                              paymentStateContext.getPaymentId(),
+                                              paymentStateContext.getTransactionId(),
+                                              paymentStateContext.getPaymentMethodId(),
+                                              paymentStateContext.getAmount(),
+                                              paymentStateContext.getCurrency(),
+                                              paymentStateContext.getProperties(),
+                                              paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/VoidOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/VoidOperation.java
index 0d776b6..c932c59 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/VoidOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/payments/VoidOperation.java
@@ -24,7 +24,7 @@ import org.killbill.billing.payment.core.sm.PaymentStateContext;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,11 +44,11 @@ public class VoidOperation extends PaymentOperation {
     @Override
     protected PaymentTransactionInfoPlugin doCallSpecificOperationCallback() throws PaymentPluginApiException {
         logger.debug("Starting VOID for payment {} ({} {})", paymentStateContext.getPaymentId(), paymentStateContext.getAmount(), paymentStateContext.getCurrency());
-        return plugin.voidPayment(paymentStateContext.getAccount().getId(),
-                                  paymentStateContext.getPaymentId(),
-                                  paymentStateContext.getTransactionId(),
-                                  paymentStateContext.getPaymentMethodId(),
-                                  paymentStateContext.getProperties(),
-                                  paymentStateContext.getCallContext());
+        return paymentPluginApi.voidPayment(paymentStateContext.getAccount().getId(),
+                                            paymentStateContext.getPaymentId(),
+                                            paymentStateContext.getTransactionId(),
+                                            paymentStateContext.getPaymentMethodId(),
+                                            paymentStateContext.getProperties(),
+                                            paymentStateContext.getCallContext());
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
index 5d95ed7..975c4b8 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PluginControlPaymentAutomatonRunner.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -31,7 +31,6 @@ import org.killbill.automaton.OperationException;
 import org.killbill.automaton.State;
 import org.killbill.automaton.State.EnteringStateCallback;
 import org.killbill.automaton.State.LeavingStateCallback;
-import org.killbill.billing.BillingExceptionBase;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -48,6 +47,7 @@ import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
 import org.killbill.billing.payment.core.sm.control.CaptureControlOperation;
 import org.killbill.billing.payment.core.sm.control.ChargebackControlOperation;
+import org.killbill.billing.payment.core.sm.control.ChargebackReversalControlOperation;
 import org.killbill.billing.payment.core.sm.control.CompletionControlOperation;
 import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
 import org.killbill.billing.payment.core.sm.control.CreditControlOperation;
@@ -62,7 +62,7 @@ import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
@@ -75,6 +75,17 @@ import static org.killbill.billing.payment.glue.PaymentModule.RETRYABLE_NAMED;
 
 public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner {
 
+    public enum ControlOperation {
+        AUTHORIZE,
+        CAPTURE,
+        CHARGEBACK,
+        CHARGEBACK_REVERSAL,
+        CREDIT,
+        PURCHASE,
+        REFUND,
+        VOID
+    }
+
     protected final OSGIServiceRegistration<PaymentControlPluginApi> paymentControlPluginRegistry;
     private final PaymentProcessor paymentProcessor;
     private final RetryServiceScheduler retryServiceScheduler;
@@ -96,16 +107,16 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner 
         this.paymentConfig = paymentConfig;
     }
 
-    public Payment run(final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
+    public Payment run(final boolean isApiPayment, final TransactionType transactionType, final ControlOperation controlOperation, final Account account, @Nullable final UUID paymentMethodId,
                        @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                        @Nullable final BigDecimal amount, @Nullable final Currency currency,
                        final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
                        final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
-        return run(paymentControlStateMachineHelper.getInitialState(), isApiPayment, transactionType, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
+        return run(paymentControlStateMachineHelper.getInitialState(), isApiPayment, transactionType, controlOperation, account, paymentMethodId, paymentId, paymentExternalKey, paymentTransactionExternalKey,
                    amount, currency, properties, paymentControlPluginNames, callContext, internalCallContext);
     }
 
-    public Payment run(final State state, final boolean isApiPayment, final TransactionType transactionType, final Account account, @Nullable final UUID paymentMethodId,
+    public Payment run(final State state, final boolean isApiPayment, final TransactionType transactionType, final ControlOperation controlOperation, final Account account, @Nullable final UUID paymentMethodId,
                        @Nullable final UUID paymentId, @Nullable final String paymentExternalKey, final String paymentTransactionExternalKey,
                        @Nullable final BigDecimal amount, @Nullable final Currency currency,
                        final Iterable<PluginProperty> properties, @Nullable final List<String> paymentControlPluginNames,
@@ -116,7 +127,7 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner 
                                                                              amount, currency,
                                                                              properties, paymentControlPluginNames, callContext, internalCallContext);
         try {
-            final OperationCallback callback = createOperationCallback(transactionType, paymentStateContext);
+            final OperationCallback callback = createOperationCallback(controlOperation, paymentStateContext);
             final LeavingStateCallback leavingStateCallback = new DefaultControlInitiated(this, paymentStateContext, paymentDao, paymentControlStateMachineHelper.getInitialState(), paymentControlStateMachineHelper.getRetriedState(), transactionType);
             final EnteringStateCallback enteringStateCallback = new DefaultControlCompleted(this, paymentStateContext, paymentControlStateMachineHelper.getRetriedState(), retryServiceScheduler);
 
@@ -169,9 +180,9 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner 
     }
 
     @VisibleForTesting
-    OperationCallback createOperationCallback(final TransactionType transactionType, final PaymentStateControlContext paymentStateContext) {
+    OperationCallback createOperationCallback(final ControlOperation controlOperation, final PaymentStateControlContext paymentStateContext) {
         final OperationCallback callback;
-        switch (transactionType) {
+        switch (controlOperation) {
             case AUTHORIZE:
                 callback = new AuthorizeControlOperation(locker, paymentPluginDispatcher, paymentConfig, paymentStateContext, paymentProcessor, controlPluginRunner);
                 break;
@@ -193,8 +204,11 @@ public class PluginControlPaymentAutomatonRunner extends PaymentAutomatonRunner 
             case CHARGEBACK:
                 callback = new ChargebackControlOperation(locker, paymentPluginDispatcher, paymentConfig, paymentStateContext, paymentProcessor, controlPluginRunner);
                 break;
+            case CHARGEBACK_REVERSAL:
+                callback = new ChargebackReversalControlOperation(locker, paymentPluginDispatcher, paymentConfig, paymentStateContext, paymentProcessor, controlPluginRunner);
+                break;
             default:
-                throw new IllegalStateException("Unsupported transaction type " + transactionType);
+                throw new IllegalStateException("Unsupported control operation " + controlOperation);
         }
         return callback;
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
index dc4cd18..91d7de3 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/DefaultPaymentProviderPluginRegistryProvider.java
@@ -19,7 +19,7 @@ package org.killbill.billing.payment.glue;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.payment.provider.DefaultPaymentProviderPluginRegistry;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 0730b62..9b066d3 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -32,6 +32,7 @@ import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentGatewayApi;
 import org.killbill.billing.payment.api.PaymentService;
 import org.killbill.billing.payment.bus.PaymentBusEventHandler;
+import org.killbill.billing.payment.config.MultiTenantPaymentConfig;
 import org.killbill.billing.payment.caching.EhCacheStateMachineConfigCache;
 import org.killbill.billing.payment.caching.StateMachineConfigCache;
 import org.killbill.billing.payment.caching.StateMachineConfigCacheInvalidationCallback;
@@ -55,8 +56,8 @@ import org.killbill.billing.payment.retry.DefaultRetryService;
 import org.killbill.billing.payment.retry.DefaultRetryService.DefaultRetryServiceScheduler;
 import org.killbill.billing.payment.retry.RetryService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
-import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.killbill.xmlloader.XMLLoader;
 import org.skife.config.ConfigurationObjectFactory;
@@ -69,6 +70,8 @@ import com.google.inject.name.Names;
 
 public class PaymentModule extends KillBillModule {
 
+    public static final String STATIC_CONFIG = "StaticConfig";
+
     public static final String RETRYABLE_NAMED = "Retryable";
 
     public static final String STATE_MACHINE_RETRY = "RetryStateMachine";
@@ -134,8 +137,9 @@ public class PaymentModule extends KillBillModule {
     protected void configure() {
         final ConfigurationObjectFactory factory = new ConfigurationObjectFactory(skifeConfigSource);
         final PaymentConfig paymentConfig = factory.build(PaymentConfig.class);
+        bind(PaymentConfig.class).annotatedWith(Names.named(STATIC_CONFIG)).toInstance(paymentConfig);
+        bind(PaymentConfig.class).to(MultiTenantPaymentConfig.class).asEagerSingleton();
 
-        bind(PaymentConfig.class).toInstance(paymentConfig);
         bind(new TypeLiteral<OSGIServiceRegistration<PaymentPluginApi>>() {}).toProvider(DefaultPaymentProviderPluginRegistryProvider.class).asEagerSingleton();
         bind(new TypeLiteral<OSGIServiceRegistration<PaymentControlPluginApi>>() {}).toProvider(DefaultPaymentControlProviderPluginRegistryProvider.class).asEagerSingleton();
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index 1bf13ea..b8bc30f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -31,6 +31,9 @@ import javax.inject.Named;
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
@@ -47,6 +50,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
@@ -64,7 +68,7 @@ import org.killbill.billing.payment.retry.DefaultPriorPaymentControlResult;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
@@ -89,6 +93,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     public static final String PROP_IPCD_INVOICE_ID = "IPCD_INVOICE_ID";
     public static final String PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY = "IPCD_REFUND_IDS_AMOUNTS";
     public static final String PROP_IPCD_REFUND_WITH_ADJUSTMENTS = "IPCD_REFUND_WITH_ADJUSTMENTS";
+    public static final String PROP_IPCD_PAYMENT_ID = "IPCD_PAYMENT_ID";
 
     private final PaymentConfig paymentConfig;
     private final InvoiceInternalApi invoiceApi;
@@ -98,6 +103,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
     private final RetryServiceScheduler retryServiceScheduler;
     private final InternalCallContextFactory internalCallContextFactory;
     private final Clock clock;
+    private final AccountInternalApi accountApi;
 
     private final Logger log = LoggerFactory.getLogger(InvoicePaymentControlPluginApi.class);
 
@@ -106,7 +112,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                                           final InvoiceInternalApi invoiceApi, final TagUserApi tagApi,
                                           final PaymentDao paymentDao, final InvoicePaymentControlDao invoicePaymentControlDao,
                                           @Named(PaymentModule.RETRYABLE_NAMED) final RetryServiceScheduler retryServiceScheduler,
-                                          final InternalCallContextFactory internalCallContextFactory, final Clock clock) {
+                                          final InternalCallContextFactory internalCallContextFactory, final Clock clock,
+                                          final AccountInternalApi accountApi) {
         this.paymentConfig = paymentConfig;
         this.invoiceApi = invoiceApi;
         this.tagApi = tagApi;
@@ -115,6 +122,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         this.retryServiceScheduler = retryServiceScheduler;
         this.internalCallContextFactory = internalCallContextFactory;
         this.clock = clock;
+        this.accountApi = accountApi;
     }
 
     @Override
@@ -124,7 +132,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         Preconditions.checkArgument(paymentControlContext.getPaymentApiType() == PaymentApiType.PAYMENT_TRANSACTION);
         Preconditions.checkArgument(transactionType == TransactionType.PURCHASE ||
                                     transactionType == TransactionType.REFUND ||
-                                    transactionType == TransactionType.CHARGEBACK);
+                                    transactionType == TransactionType.CHARGEBACK ||
+                                   transactionType == TransactionType.CREDIT);
 
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
         switch (transactionType) {
@@ -134,6 +143,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                 return getPluginRefundResult(paymentControlContext, pluginProperties, internalContext);
             case CHARGEBACK:
                 return new DefaultPriorPaymentControlResult(false, paymentControlContext.getAmount());
+            case CREDIT:
+                return getPluginCreditResult(paymentControlContext, pluginProperties, internalContext);
             default:
                 throw new IllegalStateException("Unexpected transactionType " + transactionType);
         }
@@ -144,7 +155,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         final TransactionType transactionType = paymentControlContext.getTransactionType();
         Preconditions.checkArgument(transactionType == TransactionType.PURCHASE ||
                                     transactionType == TransactionType.REFUND ||
-                                    transactionType == TransactionType.CHARGEBACK);
+                                    transactionType == TransactionType.CHARGEBACK ||
+                                    transactionType == TransactionType.CREDIT);
 
         final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
         try {
@@ -161,7 +173,7 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                         if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
                             invoicePaymentAmount = paymentControlContext.getProcessedAmount();
                         } else {
-                            log.warn("processedCurrency='{}' of invoice paymentId='{}' doesn't match invoice currency='{}', assuming it is a full payment" , paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
+                            log.warn("processedCurrency='{}' of invoice paymentId='{}' doesn't match invoice currency='{}', assuming it is a full payment", paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
                             invoicePaymentAmount = paymentControlContext.getAmount();
                         }
                         log.debug("Notifying invoice of successful paymentId='{}', amount='{}', currency='{}', invoiceId='{}'", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
@@ -205,10 +217,21 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
                             currency = linkedInvoicePayment.getCurrency();
                         }
 
-                        invoiceApi.recordChargeback(paymentControlContext.getPaymentId(), amount, currency, internalContext);
+                        invoiceApi.recordChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), amount, currency, internalContext);
                     }
                     break;
 
+                case CREDIT:
+                    final Map<UUID, BigDecimal> idWithAmountMap = extractIdsWithAmountFromProperties(pluginProperties);
+                    final PluginProperty properties = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
+                    final boolean isInvoiceAdjusted = properties != null ? Boolean.valueOf((String) properties.getValue()) : false;
+
+                    final PluginProperty legacyPayment = getPluginProperty(pluginProperties, PROP_IPCD_PAYMENT_ID);
+                    final UUID paymentId = legacyPayment != null ? (UUID) legacyPayment.getValue() : paymentControlContext.getPaymentId();
+
+                    invoiceApi.recordRefund(paymentId, paymentControlContext.getAmount(), isInvoiceAdjusted, idWithAmountMap, paymentControlContext.getTransactionExternalKey(), internalContext);
+                    break;
+
                 default:
                     throw new IllegalStateException("Unexpected transactionType " + transactionType);
             }
@@ -246,9 +269,16 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
 
                 nextRetryDate = computeNextRetryDate(paymentControlContext.getPaymentExternalKey(), paymentControlContext.isApiPayment(), internalContext);
                 break;
+            case CREDIT:
             case REFUND:
+                // We don't retry REFUND
+                break;
             case CHARGEBACK:
-                // We don't retry REFUND, CHARGEBACK
+                try {
+                    invoiceApi.recordChargebackReversal(paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), internalContext);
+                } catch (final InvoiceApiException e) {
+                    log.warn("onFailureCall failed for attemptId='{}', transactionType='{}'", paymentControlContext.getAttemptPaymentId(), transactionType, e);
+                }
                 break;
             default:
                 throw new IllegalStateException("Unexpected transactionType " + transactionType);
@@ -279,19 +309,45 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         try {
             final UUID invoiceId = getInvoiceId(pluginProperties);
             final Invoice invoice = getAndSanitizeInvoice(invoiceId, internalContext);
+
+            if (!InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
+                // abort payment if the invoice status is not COMMITTED
+                return new DefaultPriorPaymentControlResult(true);
+            }
+
+            // get immutable account and check if it is child and payment is delegated to parent => abort
+            final ImmutableAccountData accountData = accountApi.getImmutableAccountDataById(invoice.getAccountId(), internalContext);
+            if ((accountData != null) && (accountData.getParentAccountId() != null) && accountData.isPaymentDelegatedToParent()) {
+                return new DefaultPriorPaymentControlResult(true);
+            }
             final BigDecimal requestedAmount = validateAndComputePaymentAmount(invoice, paymentControlPluginContext.getAmount(), paymentControlPluginContext.isApiPayment());
 
             final boolean isAborted = requestedAmount.compareTo(BigDecimal.ZERO) == 0;
+
+            if (!isAborted && paymentControlPluginContext.getPaymentMethodId() == null) {
+                log.warn("Payment for invoiceId='{}' was not triggered, accountId='{}' doesn't have a default payment method", getInvoiceId(pluginProperties), paymentControlPluginContext.getAccountId());
+                invoiceApi.recordPaymentAttemptCompletion(invoiceId,
+                                                          paymentControlPluginContext.getAmount(),
+                                                          paymentControlPluginContext.getCurrency(),
+                                                          paymentControlPluginContext.getProcessedCurrency(),
+                                                          paymentControlPluginContext.getPaymentId(),
+                                                          paymentControlPluginContext.getTransactionExternalKey(),
+                                                          paymentControlPluginContext.getCreatedDate(),
+                                                          false,
+                                                          internalContext);
+                return new DefaultPriorPaymentControlResult(true);
+            }
+
             if (!isAborted && insert_AUTO_PAY_OFF_ifRequired(paymentControlPluginContext, requestedAmount)) {
                 return new DefaultPriorPaymentControlResult(true);
             }
 
             if (paymentControlPluginContext.isApiPayment() && isAborted) {
                 throw new PaymentControlApiException("Abort purchase call: ", new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION,
-                                                                                                     String.format("Aborted Payment for invoice %s : invoice balance is = %s, requested payment amount is = %s",
-                                                                                                                   invoice.getId(),
-                                                                                                                   invoice.getBalance(),
-                                                                                                                   paymentControlPluginContext.getAmount())));
+                                                                                                      String.format("Aborted Payment for invoice %s : invoice balance is = %s, requested payment amount is = %s",
+                                                                                                                    invoice.getId(),
+                                                                                                                    invoice.getBalance(),
+                                                                                                                    paymentControlPluginContext.getAmount())));
             } else {
 
                 //
@@ -315,6 +371,8 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
             throw new PaymentControlApiException(e);
         } catch (final IllegalArgumentException e) {
             throw new PaymentControlApiException(e);
+        } catch (AccountApiException e) {
+            throw new PaymentControlApiException(e);
         }
     }
 
@@ -323,9 +381,9 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         if ((paymentControlPluginContext.getAmount() == null || paymentControlPluginContext.getAmount().compareTo(BigDecimal.ZERO) == 0) &&
             idWithAmount.size() == 0) {
             throw new PaymentControlApiException("Abort refund call: ", new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION,
-                                                                                                  String.format("Refund for payment, key = %s, aborted: requested refund amount is = %s",
-                                                                                                                paymentControlPluginContext.getPaymentExternalKey(),
-                                                                                                                paymentControlPluginContext.getAmount())));
+                                                                                                String.format("Refund for payment, key = %s, aborted: requested refund amount is = %s",
+                                                                                                              paymentControlPluginContext.getPaymentExternalKey(),
+                                                                                                              paymentControlPluginContext.getAmount())));
         }
 
         final PaymentModelDao payment = paymentDao.getPayment(paymentControlPluginContext.getPaymentId(), internalContext);
@@ -360,6 +418,11 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         return new DefaultPriorPaymentControlResult(isAborted, amountToBeRefunded);
     }
 
+    private PriorPaymentControlResult getPluginCreditResult(final PaymentControlContext paymentControlPluginContext, final Iterable<PluginProperty> pluginProperties, final InternalCallContext internalContext) throws PaymentControlApiException {
+        // TODO implement
+        return new DefaultPriorPaymentControlResult(false, paymentControlPluginContext.getAmount());
+    }
+
     private Map<UUID, BigDecimal> extractIdsWithAmountFromProperties(final Iterable<PluginProperty> properties) {
         final PluginProperty prop = getPluginProperty(properties, PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY);
         if (prop == null) {
@@ -429,10 +492,10 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         final PaymentTransactionModelDao lastTransaction = purchasedTransactions.get(purchasedTransactions.size() - 1);
         switch (lastTransaction.getTransactionStatus()) {
             case PAYMENT_FAILURE:
-                return getNextRetryDateForPaymentFailure(purchasedTransactions);
+                return getNextRetryDateForPaymentFailure(purchasedTransactions, internalContext);
 
             case PLUGIN_FAILURE:
-                return getNextRetryDateForPluginFailure(purchasedTransactions);
+                return getNextRetryDateForPluginFailure(purchasedTransactions, internalContext);
 
             case UNKNOWN:
             default:
@@ -440,10 +503,10 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         }
     }
 
-    private DateTime getNextRetryDateForPaymentFailure(final List<PaymentTransactionModelDao> purchasedTransactions) {
+    private DateTime getNextRetryDateForPaymentFailure(final List<PaymentTransactionModelDao> purchasedTransactions, final InternalCallContext internalContext) {
 
         DateTime result = null;
-        final List<Integer> retryDays = paymentConfig.getPaymentFailureRetryDays();
+        final List<Integer> retryDays = paymentConfig.getPaymentFailureRetryDays(internalContext);
         final int attemptsInState = getNumberAttemptsInState(purchasedTransactions, TransactionStatus.PAYMENT_FAILURE);
         final int retryCount = (attemptsInState - 1) >= 0 ? (attemptsInState - 1) : 0;
         if (retryCount < retryDays.size()) {
@@ -460,17 +523,17 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
         return result;
     }
 
-    private DateTime getNextRetryDateForPluginFailure(final List<PaymentTransactionModelDao> purchasedTransactions) {
+    private DateTime getNextRetryDateForPluginFailure(final List<PaymentTransactionModelDao> purchasedTransactions, final InternalCallContext internalContext) {
 
         DateTime result = null;
         final int attemptsInState = getNumberAttemptsInState(purchasedTransactions, TransactionStatus.PLUGIN_FAILURE);
         final int retryAttempt = (attemptsInState - 1) >= 0 ? (attemptsInState - 1) : 0;
 
-        if (retryAttempt < paymentConfig.getPluginFailureRetryMaxAttempts()) {
-            int nbSec = paymentConfig.getPluginFailureInitialRetryInSec();
+        if (retryAttempt < paymentConfig.getPluginFailureRetryMaxAttempts(internalContext)) {
+            int nbSec = paymentConfig.getPluginFailureInitialRetryInSec(internalContext);
             int remainingAttempts = retryAttempt;
             while (--remainingAttempts > 0) {
-                nbSec = nbSec * paymentConfig.getPluginFailureRetryMultiplier();
+                nbSec = nbSec * paymentConfig.getPluginFailureRetryMultiplier(internalContext);
             }
             result = clock.getUTCNow().plusSeconds(nbSec);
             log.debug("Next retryDate={}, retryAttempt={}, now={}", result, retryAttempt, clock.getUTCNow());
diff --git a/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java b/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java
new file mode 100644
index 0000000..e61101a
--- /dev/null
+++ b/payment/src/main/java/org/killbill/billing/payment/logging/PaymentLoggingHelper.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.payment.logging;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.slf4j.Logger;
+
+import com.google.common.base.Joiner;
+
+public abstract class PaymentLoggingHelper {
+
+    private static final Joiner JOINER = Joiner.on(",");
+
+    public static void logEnterAPICall(final Logger log,
+                                       final String transactionType,
+                                       final Account account,
+                                       @Nullable final UUID paymentMethodId,
+                                       @Nullable final UUID paymentId,
+                                       @Nullable final UUID transactionId,
+                                       @Nullable final BigDecimal amount,
+                                       @Nullable final Currency currency,
+                                       @Nullable final String paymentExternalKey,
+                                       @Nullable final String paymentTransactionExternalKey,
+                                       @Nullable final TransactionStatus transactionStatus,
+                                       @Nullable final List<String> paymentControlPluginNames) {
+        logAPICallInternal(log,
+                           "ENTERING ",
+                           transactionType,
+                           account,
+                           paymentMethodId,
+                           paymentId,
+                           transactionId,
+                           amount,
+                           currency,
+                           paymentExternalKey,
+                           paymentTransactionExternalKey,
+                           transactionStatus,
+                           paymentControlPluginNames,
+                           null);
+    }
+
+    public static void logExitAPICall(final Logger log,
+                                      final String transactionType,
+                                      final Account account,
+                                      @Nullable final UUID paymentMethodId,
+                                      @Nullable final UUID paymentId,
+                                      @Nullable final UUID transactionId,
+                                      @Nullable final BigDecimal amount,
+                                      @Nullable final Currency currency,
+                                      @Nullable final String paymentExternalKey,
+                                      @Nullable final String paymentTransactionExternalKey,
+                                      @Nullable final TransactionStatus transactionStatus,
+                                      @Nullable final List<String> paymentControlPluginNames,
+                                      @Nullable final PaymentApiException exception) {
+        logAPICallInternal(log,
+                           "EXITING ",
+                           transactionType,
+                           account,
+                           paymentMethodId,
+                           paymentId,
+                           transactionId,
+                           amount,
+                           currency,
+                           paymentExternalKey,
+                           paymentTransactionExternalKey,
+                           transactionStatus,
+                           paymentControlPluginNames,
+                           exception);
+    }
+
+    public static void logAPICallInternal(final Logger log,
+                                          final String prefixMsg,
+                                          final String transactionType,
+                                          final Account account,
+                                          final UUID paymentMethodId,
+                                          @Nullable final UUID paymentId,
+                                          @Nullable final UUID transactionId,
+                                          @Nullable final BigDecimal amount,
+                                          @Nullable final Currency currency,
+                                          @Nullable final String paymentExternalKey,
+                                          @Nullable final String paymentTransactionExternalKey,
+                                          @Nullable final TransactionStatus transactionStatus,
+                                          @Nullable final List<String> paymentControlPluginNames,
+                                          @Nullable final PaymentApiException exception) {
+        if (log.isInfoEnabled()) {
+            final StringBuilder logLine = new StringBuilder(prefixMsg);
+            logLine.append("PaymentApi: transactionType='")
+                   .append(transactionType)
+                   .append("', accountId='")
+                   .append(account.getId())
+                   .append("'");
+            if (paymentMethodId != null) {
+                logLine.append(", paymentMethodId='")
+                       .append(paymentMethodId)
+                       .append("'");
+            }
+            if (paymentExternalKey != null) {
+                logLine.append(", paymentExternalKey='")
+                       .append(paymentExternalKey)
+                       .append("'");
+            }
+            if (paymentTransactionExternalKey != null) {
+                logLine.append(", paymentTransactionExternalKey='")
+                       .append(paymentTransactionExternalKey)
+                       .append("'");
+            }
+            if (paymentId != null) {
+                logLine.append(", paymentId='")
+                       .append(paymentId)
+                       .append("'");
+            }
+            if (transactionId != null) {
+                logLine.append(", transactionId='")
+                       .append(transactionId)
+                       .append("'");
+            }
+            if (amount != null) {
+                logLine.append(", amount='")
+                       .append(amount)
+                       .append("'");
+            }
+            if (currency != null) {
+                logLine.append(", currency='")
+                       .append(currency)
+                       .append("'");
+            }
+            if (transactionStatus != null) {
+                logLine.append(", transactionStatus='")
+                       .append(transactionStatus)
+                       .append("'");
+            }
+            if (paymentControlPluginNames != null) {
+                logLine.append(", paymentControlPluginNames='")
+                       .append(JOINER.join(paymentControlPluginNames))
+                       .append("'");
+            }
+            if (exception != null) {
+                final ErrorCode error = ErrorCode.fromCode(exception.getCode());
+                if (error == ErrorCode.PAYMENT_PLUGIN_API_ABORTED) {
+                    logLine.append(", aborted=true");
+                }
+                logLine.append(", error='")
+                       .append(error)
+                       .append("', exception='")
+                       .append(exception.getMessage())
+                       .append("'");
+            }
+            log.info(logLine.toString());
+        }
+    }
+}
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
index 9ebbb2e..d8472a5 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentControlProviderPluginRegistry.java
@@ -23,7 +23,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
index ce8d315..475b46d 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/DefaultPaymentProviderPluginRegistry.java
@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 
 import com.google.inject.Inject;
 
diff --git a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 33f1e30..db8a69e 100644
--- a/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/org/killbill/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -36,12 +36,15 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
 
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
 
 /**
@@ -52,35 +55,37 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     public static final String PLUGIN_NAME = "__EXTERNAL_PAYMENT__";
 
     private final Clock clock;
+    private final PaymentConfig paymentConfig;
 
     @Inject
-    public ExternalPaymentProviderPlugin(final Clock clock) {
+    public ExternalPaymentProviderPlugin(final Clock clock, final PaymentConfig paymentConfig) {
         this.clock = clock;
+        this.paymentConfig = paymentConfig;
     }
 
     @Override
     public PaymentTransactionInfoPlugin authorizePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.AUTHORIZE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin capturePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CAPTURE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin purchasePayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.PURCHASE, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin voidPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.VOID, BigDecimal.ZERO, null, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
     public PaymentTransactionInfoPlugin creditPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.CREDIT, amount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
@@ -95,7 +100,7 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
 
     @Override
     public PaymentTransactionInfoPlugin refundPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbTransactionId, final UUID kbPaymentMethodId, final BigDecimal refundAmount, final Currency currency, final Iterable<PluginProperty> properties, final CallContext context) throws PaymentPluginApiException {
-        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null, null);
+        return new DefaultNoOpPaymentInfoPlugin(kbPaymentId, kbTransactionId, TransactionType.REFUND, refundAmount, currency, clock.getUTCNow(), clock.getUTCNow(), getPaymentPluginStatus(properties), null, null);
     }
 
     @Override
@@ -138,4 +143,46 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
     public GatewayNotification processNotification(final String notification, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentPluginApiException {
         return new DefaultNoOpGatewayNotification();
     }
+
+    private PaymentPluginStatus getPaymentPluginStatus(final Iterable<PluginProperty> properties) throws PaymentPluginApiException {
+        if (shouldPaymentFailWithError(properties)) {
+            return PaymentPluginStatus.ERROR;
+        } else if (shouldPaymentFailWithException(properties)) {
+            throw new PaymentPluginApiException("Failed to process payment", "Triggered from killbill.external.payment.fail.cancellation");
+        } else if (shouldPaymentFailWithCancellation(properties)) {
+            return PaymentPluginStatus.CANCELED;
+        } else if (shouldPaymentTimeout(properties)) {
+            try {
+                Thread.sleep(paymentConfig.getPaymentPluginTimeout().getMillis() + 1000);
+            } catch (final InterruptedException ignored) {
+            }
+        }
+        return PaymentPluginStatus.PROCESSED;
+    }
+
+    private boolean shouldPaymentFailWithError(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.error");
+    }
+
+    private boolean shouldPaymentFailWithException(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.exception");
+    }
+
+    private boolean shouldPaymentFailWithCancellation(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.cancellation");
+    }
+
+    private boolean shouldPaymentTimeout(final Iterable<PluginProperty> properties) {
+        return isPropertySet(properties, "killbill.external.payment.fail.timeout");
+    }
+
+    private boolean isPropertySet(final Iterable<PluginProperty> properties, final String targetProperty) {
+        return Iterables.any(properties, new Predicate<PluginProperty>() {
+            @Override
+            public boolean apply(final PluginProperty input) {
+                return input.getKey().equals(targetProperty) && input.getValue().equals("true");
+            }
+        });
+    }
+
 }
diff --git a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
index fef6bd7..25af914 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
+++ b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
@@ -6,9 +6,9 @@ CREATE TABLE payment_attempts (
     id varchar(36) NOT NULL,
     account_id varchar(36) NOT NULL,
     payment_method_id varchar(36) DEFAULT NULL,
-    payment_external_key varchar(128) NOT NULL,
+    payment_external_key varchar(255) NOT NULL,
     transaction_id varchar(36),
-    transaction_external_key varchar(128) NOT NULL,
+    transaction_external_key varchar(255) NOT NULL,
     transaction_type varchar(32) NOT NULL,
     state_name varchar(32) NOT NULL,
     amount numeric(15,9),
@@ -37,9 +37,9 @@ CREATE TABLE payment_attempt_history (
     target_record_id bigint /*! unsigned */ not null,
     account_id varchar(36) NOT NULL,
     payment_method_id varchar(36) DEFAULT NULL,
-    payment_external_key varchar(128) NOT NULL,
+    payment_external_key varchar(255) NOT NULL,
     transaction_id varchar(36),
-    transaction_external_key varchar(128) NOT NULL,
+    transaction_external_key varchar(255) NOT NULL,
     transaction_type varchar(32) NOT NULL,
     state_name varchar(32) NOT NULL,
     amount numeric(15,9),
diff --git a/payment/src/main/resources/org/killbill/billing/payment/migration/V20161005110745__payment_external_keys.sql b/payment/src/main/resources/org/killbill/billing/payment/migration/V20161005110745__payment_external_keys.sql
new file mode 100644
index 0000000..b5ccf70
--- /dev/null
+++ b/payment/src/main/resources/org/killbill/billing/payment/migration/V20161005110745__payment_external_keys.sql
@@ -0,0 +1,4 @@
+alter table payment_attempts modify payment_external_key varchar(255);
+alter table payment_attempts modify transaction_external_key varchar(255);
+alter table payment_attempt_history modify payment_external_key varchar(255);
+alter table payment_attempt_history modify transaction_external_key varchar(255);
\ No newline at end of file
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java
index 6bf1743..85e0a0a 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestDefaultPayment.java
@@ -220,6 +220,7 @@ public class TestDefaultPayment extends PaymentTestSuiteNoDB {
                                   UUID.randomUUID(),
                                   1,
                                   UUID.randomUUID().toString(),
-                                  transactions);
+                                  transactions,
+                                  null);
     }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestExternalPaymentPlugin.java b/payment/src/test/java/org/killbill/billing/payment/api/TestExternalPaymentPlugin.java
index 2e6f46e..589ec38 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestExternalPaymentPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestExternalPaymentPlugin.java
@@ -60,8 +60,8 @@ public class TestExternalPaymentPlugin extends PaymentTestSuiteWithEmbeddedDB {
         final Payment payment = paymentApi.createPurchase(account, account.getPaymentMethodId(), null, requestedAmount, Currency.AED, paymentExternalKey, transactionExternalKey,
                                                           ImmutableList.<PluginProperty>of(), callContext);
 
-        final Payment paymentPluginInfoFalse = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
-        final Payment paymentPluginInfoTrue = paymentApi.getPayment(payment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment paymentPluginInfoFalse = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment paymentPluginInfoTrue = paymentApi.getPayment(payment.getId(), true, false, ImmutableList.<PluginProperty>of(), callContext);
 
         Assert.assertEquals(paymentPluginInfoFalse.getAccountId(), paymentPluginInfoTrue.getAccountId());
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
index 235ac9e..92d8c17 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApi.java
@@ -147,12 +147,43 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
         checkPaymentMethodPagination(paymentMethodId, baseNbRecords + 1, false);
 
-        paymentApi.deletePaymentMethod(account, paymentMethodId, true, ImmutableList.<PluginProperty>of(), callContext);
+        paymentApi.deletePaymentMethod(account, paymentMethodId, true, false, ImmutableList.<PluginProperty>of(), callContext);
 
         checkPaymentMethodPagination(paymentMethodId, baseNbRecords, true);
     }
 
     @Test(groups = "slow")
+    public void testAddRemovePaymentMethodWithForcedDeletion() throws Exception {
+        final Long baseNbRecords = paymentApi.getPaymentMethods(0L, 1000L, false, ImmutableList.<PluginProperty>of(), callContext).getMaxNbRecords();
+        Assert.assertEquals(baseNbRecords, (Long) 1L);
+
+        final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+        final UUID paymentMethodId = account.getPaymentMethodId();
+
+        checkPaymentMethodPagination(paymentMethodId, baseNbRecords + 1, false);
+
+        paymentApi.deletePaymentMethod(account, paymentMethodId, false, true, ImmutableList.<PluginProperty>of(), callContext);
+
+        checkPaymentMethodPagination(paymentMethodId, baseNbRecords, true);
+    }
+
+    @Test(groups = "slow")
+    public void testAddRemovePaymentMethodWithoutForcedDeletion() throws Exception {
+        final Long baseNbRecords = paymentApi.getPaymentMethods(0L, 1000L, false, ImmutableList.<PluginProperty>of(), callContext).getMaxNbRecords();
+        Assert.assertEquals(baseNbRecords, (Long) 1L);
+
+        final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+        final UUID paymentMethodId = account.getPaymentMethodId();
+
+        try {
+            paymentApi.deletePaymentMethod(account, paymentMethodId, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        } catch (final PaymentApiException e) {
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INTERNAL_ERROR.getCode());
+        }
+        checkPaymentMethodPagination(paymentMethodId, baseNbRecords + 1, false);
+    }
+
+    @Test(groups = "slow")
     public void testCreateSuccessPurchase() throws PaymentApiException {
 
         final BigDecimal requestedAmount = BigDecimal.TEN;
@@ -837,7 +868,25 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         assertEquals(attempts.size(), 1);
     }
 
+    @Test(groups = "slow")
+    public void testCreatePurchaseWithExternalKeyOverLimit() throws PaymentApiException, InvoiceApiException, EventBusException {
+        final BigDecimal requestedAmount = BigDecimal.TEN;
+        final LocalDate now = clock.getUTCToday();
+
+        final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
+
+        final String paymentExternalKey = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.";
+        final String transactionExternalKey = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.";
+
+        try {
+            paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey,
+                                                        createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
+            Assert.fail();
+        } catch (final PaymentApiException e) {
+            assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
+        }
 
+    }
 
     @Test(groups = "slow")
     public void testCreateFailedPurchaseWithPaymentControl() throws PaymentApiException, InvoiceApiException, EventBusException {
@@ -871,7 +920,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         }
 
 
-        final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments.size(), 1);
         final Payment payment = accountPayments.get(0);
         assertEquals(payment.getExternalKey(), paymentExternalKey);
@@ -928,7 +977,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         }
 
 
-        final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments.size(), 1);
         final Payment payment = accountPayments.get(0);
         assertEquals(payment.getExternalKey(), paymentExternalKey);
@@ -956,7 +1005,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
                                                     createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
 
 
-        final List<Payment> accountPayments2 = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final List<Payment> accountPayments2 = paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(accountPayments2.size(), 1);
         final Payment payment2 = accountPayments2.get(0);
         assertEquals(payment2.getTransactions().size(), 2);
@@ -1134,9 +1183,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/477")
     public void testCreateChargeback() throws PaymentApiException {
-        // API change in 0.17
-        final DefaultPaymentApi paymentApi = (DefaultPaymentApi) this.paymentApi;
-
         final BigDecimal requestedAmount = BigDecimal.TEN;
         final Currency currency = Currency.AED;
         final String paymentExternalKey = UUID.randomUUID().toString();
@@ -1372,9 +1418,6 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testCreateChargebackReversalBeforeChargeback() throws PaymentApiException {
-        // API change in 0.17
-        final DefaultPaymentApi paymentApi = (DefaultPaymentApi) this.paymentApi;
-
         final BigDecimal requestedAmount = BigDecimal.TEN;
         final Currency currency = Currency.AED;
         final String paymentExternalKey = UUID.randomUUID().toString();
@@ -1426,7 +1469,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_SUCH_SUCCESS_PAYMENT.getCode());
         }
 
-        assertEquals(paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext).getTransactions().size(), 1);
+        assertEquals(paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext).getTransactions().size(), 1);
 
         assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getStateName(), "PURCHASE_SUCCESS");
         assertEquals(paymentDao.getPayment(payment.getId(), internalCallContext).getLastSuccessStateName(), "PURCHASE_SUCCESS");
@@ -1515,7 +1558,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         } catch (final PaymentApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_NO_SUCH_PAYMENT.getCode());
 
-            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
+            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, false, ImmutableList.<PluginProperty>of(), callContext);
             assertEquals(latestPayment, initialPayment);
         }
     }
@@ -1533,7 +1576,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
         } catch (final PaymentApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_PARAMETER.getCode());
 
-            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
+            final Payment latestPayment = paymentApi.getPayment(initialPayment.getId(), true, false, ImmutableList.<PluginProperty>of(), callContext);
             assertEquals(latestPayment, initialPayment);
         }
     }
@@ -1869,7 +1912,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, pendingAuthorization.getExternalKey(), null, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -1877,7 +1920,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, pendingAuthorization.getExternalKey(), UUID.randomUUID().toString(), BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -1885,7 +1928,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, authKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -1913,7 +1956,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, pendingAuthorization.getExternalKey(), null, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -1921,7 +1964,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, pendingAuthorization.getExternalKey(), UUID.randomUUID().toString(), BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -1929,7 +1972,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, authKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         // Capture with a different transaction external key should go through
@@ -1952,7 +1995,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, captureKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         // Second capture with the same transaction external key should go through (completion)
@@ -2016,7 +2059,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, authorization.getExternalKey(), UUID.randomUUID().toString(), BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -2024,7 +2067,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, authorization.getExternalKey(), UUID.randomUUID().toString(), BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -2032,7 +2075,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, authKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         // Capture with a different transaction external key should go through
@@ -2063,7 +2106,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, captureKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         // Second capture with a different transaction external key should go through
@@ -2127,7 +2170,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, failedAuthorization1.getExternalKey(), null, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         try {
@@ -2135,7 +2178,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
             createPayment(account1, TransactionType.AUTHORIZE, null, null, authKey, BigDecimal.TEN, PaymentPluginStatus.PROCESSED);
             Assert.fail();
         } catch (final PaymentApiException e) {
-            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_DIFFERENT_ACCOUNT_ID.getCode());
         }
 
         // Different auth with the same payment external key should go through
@@ -2233,7 +2276,7 @@ public class TestPaymentApi extends PaymentTestSuiteWithEmbeddedDB {
 
     private void verifyPaymentViaGetPath(final Payment payment) throws PaymentApiException {
         // We can't use Assert.assertEquals because the updateDate may have been updated by the Janitor
-        final Payment refreshedPayment = paymentApi.getPayment(payment.getId(), true, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment refreshedPayment = paymentApi.getPayment(payment.getId(), true, false, ImmutableList.<PluginProperty>of(), callContext);
 
         Assert.assertEquals(refreshedPayment.getAccountId(), payment.getAccountId());
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
index 729e49a..bf1e53c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/api/TestPaymentApiNoDB.java
@@ -74,7 +74,7 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
     public void beforeMethod() throws Exception {
         super.beforeMethod();
         final PaymentMethodPlugin paymentMethodInfo = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
-        testHelper.addTestPaymentMethod(account, paymentMethodInfo);
+        account = testHelper.addTestPaymentMethod(account, paymentMethodInfo);
     }
 
     @Test(groups = "fast")
@@ -169,29 +169,26 @@ public class TestPaymentApiNoDB extends PaymentTestSuiteNoDB {
         final PaymentMethod initDefaultMethod = methods.get(0);
         assertEquals(initDefaultMethod.getId(), account.getPaymentMethodId());
 
-        final PaymentMethodPlugin newPaymenrMethod = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
-        final UUID newPaymentMethodId = paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), MockPaymentProviderPlugin.PLUGIN_NAME, true, newPaymenrMethod, PLUGIN_PROPERTIES, callContext);
-        Mockito.when(account.getPaymentMethodId()).thenReturn(newPaymentMethodId);
+        final PaymentMethodPlugin newPaymentMethod = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
+        account = testHelper.addTestPaymentMethod(account, newPaymentMethod, PLUGIN_PROPERTIES);
 
         methods = paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext);
         assertEquals(methods.size(), 2);
 
-        assertEquals(newPaymentMethodId, account.getPaymentMethodId());
-
         boolean failed = false;
         try {
-            paymentApi.deletePaymentMethod(account, newPaymentMethodId, false, PLUGIN_PROPERTIES, callContext);
+            paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), false, false, PLUGIN_PROPERTIES, callContext);
         } catch (final PaymentApiException e) {
             failed = true;
         }
         assertTrue(failed);
 
-        paymentApi.deletePaymentMethod(account, initDefaultMethod.getId(), true, PLUGIN_PROPERTIES, callContext);
+        paymentApi.deletePaymentMethod(account, initDefaultMethod.getId(), true, false, PLUGIN_PROPERTIES, callContext);
         methods = paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext);
         assertEquals(methods.size(), 1);
 
         // NOW retry with default payment method with special flag
-        paymentApi.deletePaymentMethod(account, newPaymentMethodId, true, PLUGIN_PROPERTIES, callContext);
+        paymentApi.deletePaymentMethod(account, account.getPaymentMethodId(), true, false, PLUGIN_PROPERTIES, callContext);
 
         methods = paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext);
         assertEquals(methods.size(), 0);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTask.java b/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTask.java
index 9e81218..a3d260c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTask.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/janitor/TestIncompletePaymentTransactionTask.java
@@ -19,7 +19,6 @@ package org.killbill.billing.payment.core.janitor;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.payment.PaymentTestSuiteNoDB;
-import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.inject.Inject;
@@ -39,7 +38,7 @@ public class TestIncompletePaymentTransactionTask extends PaymentTestSuiteNoDB {
 
         // Based on config "15s,1m,3m,1h,1d,1d,1d,1d,1d"
         for (int i = 1; i < 10; i++) {
-            final DateTime nextTime = incompletePaymentTransactionTask.getNextNotificationTime(i);
+            final DateTime nextTime = incompletePaymentTransactionTask.getNextNotificationTime(i, internalCallContext);
             assertNotNull(nextTime);
             assertTrue(nextTime.compareTo(initTime) > 0);
             if (i == 0) {
@@ -62,6 +61,6 @@ public class TestIncompletePaymentTransactionTask extends PaymentTestSuiteNoDB {
                 assertTrue(nextTime.compareTo(initTime.plusDays(1).plusSeconds(1)) < 0);
             }
         }
-        assertNull(incompletePaymentTransactionTask.getNextNotificationTime(10));
+        assertNull(incompletePaymentTransactionTask.getNextNotificationTime(10, internalCallContext));
     }
 }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
index 07c8031..386ed00 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryablePaymentAutomatonRunner.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -45,7 +45,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.retry.BaseRetryService.RetryServiceScheduler;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
@@ -65,9 +65,9 @@ public class MockRetryablePaymentAutomatonRunner extends PluginControlPaymentAut
     }
 
     @Override
-    OperationCallback createOperationCallback(final TransactionType transactionType, final PaymentStateControlContext paymentStateContext) {
+    OperationCallback createOperationCallback(final ControlOperation controlOperation, final PaymentStateControlContext paymentStateContext) {
         if (operationCallback == null) {
-            return super.createOperationCallback(transactionType, paymentStateContext);
+            return super.createOperationCallback(controlOperation, paymentStateContext);
         } else {
             return operationCallback;
         }
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
index 07f878b..d6e79c8 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/MockRetryAuthorizeOperationCallback.java
@@ -19,12 +19,11 @@ package org.killbill.billing.payment.core.sm;
 import java.util.Collections;
 
 import org.killbill.automaton.OperationResult;
-import org.killbill.billing.osgi.api.OSGIServiceRegistration;
 import org.killbill.billing.payment.api.DefaultPayment;
 import org.killbill.billing.payment.api.DefaultPaymentTransaction;
 import org.killbill.billing.payment.api.Payment;
-import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.sm.control.AuthorizeControlOperation;
@@ -34,8 +33,7 @@ import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
-import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
 
@@ -108,7 +106,7 @@ public class MockRetryAuthorizeOperationCallback extends AuthorizeControlOperati
                                                                                                   null);
 
         return new DefaultPayment(paymentModelDao.getId(), paymentModelDao.getCreatedDate(), paymentModelDao.getUpdatedDate(), paymentModelDao.getAccountId(),
-                                        paymentModelDao.getPaymentMethodId(), paymentModelDao.getPaymentNumber(), paymentModelDao.getExternalKey(), Collections.singletonList(convertedTransaction));
+                                        paymentModelDao.getPaymentMethodId(), paymentModelDao.getPaymentNumber(), paymentModelDao.getExternalKey(), Collections.singletonList(convertedTransaction), null);
     }
 
     public MockRetryAuthorizeOperationCallback setException(final Exception exception) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
index 1765bef..cc32c53 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPaymentOperation.java
@@ -38,7 +38,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.memory.MemoryGlobalLocker;
 import org.mockito.Mockito;
@@ -123,7 +123,7 @@ public class TestPaymentOperation extends PaymentTestSuiteNoDB {
         final PaymentMethodModelDao paymentMethodModelDao = new PaymentMethodModelDao(paymentStateContext.getPaymentMethodId(), UUID.randomUUID().toString(), clock.getUTCNow(), clock.getUTCNow(),
                                                                                       paymentStateContext.getAccount().getId(), MockPaymentProviderPlugin.PLUGIN_NAME, true);
         final PaymentDao paymentDao = Mockito.mock(PaymentDao.class);
-        Mockito.when(paymentDao.getPaymentMethodIncludedDeleted(paymentStateContext.getPaymentMethodId(), internalCallContext)).thenReturn(paymentMethodModelDao);
+        Mockito.when(paymentDao.getPaymentMethod(paymentStateContext.getPaymentMethodId(), internalCallContext)).thenReturn(paymentMethodModelDao);
 
         final PaymentAutomatonDAOHelper daoHelper = new PaymentAutomatonDAOHelper(paymentStateContext, clock.getUTCNow(), paymentDao, registry, internalCallContext, eventBus, paymentSMHelper);
         paymentOperation = new PaymentOperationTest(paymentPluginStatus, daoHelper, locker, paymentPluginDispatcher, paymentConfig, paymentStateContext);
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index b24ed53..e34c632 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -42,7 +42,7 @@ import org.killbill.billing.payment.dispatcher.PluginDispatcher;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.memory.MemoryGlobalLocker;
 import org.mockito.Mockito;
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 ff72d21..4f3b429 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
@@ -42,6 +42,7 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PaymentExecutors;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.PluginControlPaymentProcessor;
+import org.killbill.billing.payment.core.sm.PluginControlPaymentAutomatonRunner.ControlOperation;
 import org.killbill.billing.payment.core.sm.control.ControlPluginRunner;
 import org.killbill.billing.payment.core.sm.control.PaymentStateControlContext;
 import org.killbill.billing.payment.dao.PaymentAttemptModelDao;
@@ -61,7 +62,6 @@ import org.killbill.commons.locker.GlobalLock;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.LockFailedException;
 import org.mockito.Mockito;
-import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -229,6 +229,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         try {
             runner.run(true,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -269,6 +270,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
 
         runner.run(true,
                    TransactionType.AUTHORIZE,
+                   ControlOperation.AUTHORIZE,
                    account,
                    paymentMethodId,
                    null,
@@ -303,6 +305,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
 
         runner.run(true,
                    TransactionType.AUTHORIZE,
+                   ControlOperation.AUTHORIZE,
                    account,
                    paymentMethodId,
                    null,
@@ -337,6 +340,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         try {
             runner.run(true,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -375,6 +379,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         try {
             runner.run(true,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -412,6 +417,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         try {
             runner.run(true,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -449,6 +455,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         try {
             runner.run(true,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -493,6 +500,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
         runner.run(state,
                    false,
                    TransactionType.AUTHORIZE,
+                   ControlOperation.AUTHORIZE,
                    account,
                    paymentMethodId,
                    null,
@@ -543,6 +551,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
             runner.run(state,
                        false,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
@@ -590,6 +599,7 @@ public class TestRetryablePayment extends PaymentTestSuiteNoDB {
             runner.run(state,
                        false,
                        TransactionType.AUTHORIZE,
+                       ControlOperation.AUTHORIZE,
                        account,
                        paymentMethodId,
                        null,
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
index fe0fe2a..754d1fe 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/TestPaymentProcessor.java
@@ -25,6 +25,7 @@ import java.util.concurrent.Callable;
 
 import javax.annotation.Nullable;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
@@ -33,6 +34,7 @@ import org.killbill.billing.events.PaymentInternalEvent;
 import org.killbill.billing.events.PaymentPluginErrorInternalEvent;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
@@ -87,7 +89,7 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
 
         mockPaymentProviderPlugin.overridePaymentPluginStatus(paymentId, authorization.getTransactions().get(0).getId(), PaymentPluginStatus.PROCESSED);
 
-        final List<Payment> payments = paymentProcessor.getAccountPayments(account.getId(), true, callContext, internalCallContext);
+        final List<Payment> payments = paymentProcessor.getAccountPayments(account.getId(), true, false, callContext, internalCallContext);
         Assert.assertEquals(payments.size(), 1);
         verifyPayment(payments.get(0), paymentExternalKey, TEN, ZERO, ZERO, 1);
         verifyPaymentTransaction(payments.get(0).getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
@@ -199,6 +201,58 @@ public class TestPaymentProcessor extends PaymentTestSuiteWithEmbeddedDB {
         paymentBusListener.verify(1, account.getId(), paymentId, TEN, TransactionStatus.SUCCESS);
     }
 
+    @Test(groups = "slow")
+    public void testInvalidTransition() throws Exception {
+        final String paymentExternalKey = UUID.randomUUID().toString();
+        final Iterable<PluginProperty> pluginPropertiesToDriveTransationToPending = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.ERROR, false));
+
+        // AUTH
+        final String authorizationKey = UUID.randomUUID().toString();
+        final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
+                                                                           SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
+        verifyPayment(authorization, paymentExternalKey, ZERO, ZERO, ZERO, 1);
+        final UUID paymentId = authorization.getId();
+        verifyPaymentTransaction(authorization.getTransactions().get(0), authorizationKey, TransactionType.AUTHORIZE, TEN, paymentId);
+        paymentBusListener.verify(0, 1, 0, account.getId(), paymentId, ZERO, TransactionStatus.PAYMENT_FAILURE);
+
+        // REFUND
+        final String refundKey = UUID.randomUUID().toString();
+        try {
+            paymentProcessor.createRefund(true, null, account, paymentId, TEN, CURRENCY, refundKey,
+                                          SHOULD_LOCK_ACCOUNT, PLUGIN_PROPERTIES, callContext, internalCallContext);
+            Assert.fail();
+        } catch (final PaymentApiException e) {
+            Assert.assertEquals(e.getCode(), ErrorCode.PAYMENT_INVALID_OPERATION.getCode());
+        }
+        final Payment refreshedPayment = paymentProcessor.getPayment(authorization.getId(), false, false, PLUGIN_PROPERTIES, callContext, internalCallContext);
+        // Make sure no state has been created (no UNKNOWN transaction for the refund)
+        verifyPayment(refreshedPayment, paymentExternalKey, ZERO, ZERO, ZERO, 1);
+        paymentBusListener.verify(0, 1, 0, account.getId(), paymentId, ZERO, TransactionStatus.PAYMENT_FAILURE);
+    }
+
+    @Test(groups = "slow")
+    public void testNotifyPendingPaymentOfStateChanged() throws Exception {
+        final String paymentExternalKey = UUID.randomUUID().toString();
+        final Iterable<PluginProperty> pluginPropertiesToDriveTransationToPending = ImmutableList.<PluginProperty>of(new PluginProperty(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, PaymentPluginStatus.PENDING, false));
+
+        // Create Pending AUTH
+        final String authorizationKey = UUID.randomUUID().toString();
+        final Payment authorization = paymentProcessor.createAuthorization(true, null, account, null, null, TEN, CURRENCY, paymentExternalKey, authorizationKey,
+                                                                           SHOULD_LOCK_ACCOUNT, pluginPropertiesToDriveTransationToPending, callContext, internalCallContext);
+        final PaymentTransaction pendingTransaction = authorization.getTransactions().get(0);
+        Assert.assertEquals(pendingTransaction.getTransactionStatus(), TransactionStatus.PENDING);
+
+        final UUID transactionId = pendingTransaction.getId();
+        // Override plugin status of payment
+        mockPaymentProviderPlugin.overridePaymentPluginStatus(authorization.getId(), transactionId, PaymentPluginStatus.PROCESSED);
+        // Notify that state has changed, after changing the state in the plugin
+        final Payment updatedPayment = paymentProcessor.notifyPendingPaymentOfStateChanged(account, transactionId, true, callContext, internalCallContext);
+        verifyPayment(updatedPayment, paymentExternalKey, TEN, ZERO, ZERO, 1);
+
+        final PaymentTransaction updatedTransaction = updatedPayment.getTransactions().get(0);
+        Assert.assertEquals(updatedTransaction.getTransactionStatus(), TransactionStatus.SUCCESS);
+    }
+
     private void verifyPayment(final Payment payment, final String paymentExternalKey,
                                final BigDecimal authAmount, final BigDecimal capturedAmount, final BigDecimal refundedAmount,
                                final int transactionsSize) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
index 2c93d7f..4d6a938 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestDefaultPaymentDao.java
@@ -21,7 +21,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import org.joda.time.DateTimeZone;
+import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
@@ -37,13 +37,13 @@ public class TestDefaultPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     @Test(groups = "slow")
     public void testPaymentCRUD() throws Exception {
         for (int i = 0; i < 3; i++) {
-            testPaymentCRUDForAccount(UUID.randomUUID(), i + 1);
+            testPaymentCRUDForAccount();
         }
     }
 
-    public void testPaymentCRUDForAccount(final UUID accountId, final int accountNb) {
-        // We need to create specific call contexts to make the account_record_id magic work
-        internalCallContext.setAccountRecordId((long) accountNb);
+    public void testPaymentCRUDForAccount() throws Exception {
+        final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+        final UUID accountId = account.getId();
 
         final PaymentModelDao specifiedFirstPaymentModelDao = generatePaymentModelDao(accountId);
         final PaymentTransactionModelDao specifiedFirstPaymentTransactionModelDao = generatePaymentTransactionModelDao(specifiedFirstPaymentModelDao.getId());
diff --git a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
index e988651..a195593 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dao/TestPaymentDao.java
@@ -25,18 +25,16 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.account.api.Account;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.payment.PaymentTestSuiteWithEmbeddedDB;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.dao.PluginPropertySerializer.PluginPropertySerializerException;
-import org.killbill.billing.util.callcontext.CallOrigin;
-import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.callcontext.UserType;
 import org.killbill.billing.util.entity.Pagination;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -60,8 +58,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final TransactionType transactionType = TransactionType.AUTHORIZE;
         final String pluginName = "superPlugin";
 
-        final UUID accountId = UUID.randomUUID();
-
         final List<PluginProperty> properties = new ArrayList<PluginProperty>();
         properties.add(new PluginProperty("key1", "value1", false));
         properties.add(new PluginProperty("key2", "value2", false));
@@ -315,9 +311,8 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         // Just to guarantee that next clock.getUTCNow() > newTime
         try {
             Thread.sleep(1000);
-        } catch (InterruptedException e) {
+        } catch (final InterruptedException e) {
         }
-        ;
 
         final Iterable<PaymentTransactionModelDao> transactions2 = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.PENDING), clock.getUTCNow(), initialTime, 0L, 1L);
         for (PaymentTransactionModelDao paymentTransaction : transactions2) {
@@ -333,10 +328,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testPaymentByStatesAcrossTenants() {
-
-        final UUID paymentMethodId = UUID.randomUUID();
-        final UUID accountId = UUID.randomUUID();
+    public void testPaymentByStatesAcrossTenants() throws Exception {
         final String externalKey1 = "XXhhhhooo1";
         final String transactionExternalKey1 = "transactionXX1";
 
@@ -357,72 +349,72 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
 
         // Right before createdAfterDate, so should not be returned
         final DateTime createdDate1 = createdAfterDate.minusHours(1);
-        final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, accountId, paymentMethodId, externalKey1);
+        clock.setTime(createdDate1);
+        Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, account.getId(), account.getPaymentMethodId(), externalKey1);
         paymentModelDao1.setStateName("AUTH_ERRORED");
         final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(createdDate1, createdDate1, null, transactionExternalKey1,
                                                                                        paymentModelDao1.getId(), TransactionType.AUTHORIZE, createdDate1,
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        internalCallContext.setAccountRecordId(1L);
-        internalCallContext.setCreatedDate(createdDate1);
-        internalCallContext.setUpdatedDate(createdDate1);
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, internalCallContext);
 
         // Right after createdAfterDate, so it should  be returned
         final DateTime createdDate2 = createdAfterDate.plusHours(1);
-        final PaymentModelDao paymentModelDao2 = new PaymentModelDao(createdDate2, createdDate2, accountId, paymentMethodId, externalKey2);
+        clock.setTime(createdDate2);
+        account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentModelDao paymentModelDao2 = new PaymentModelDao(createdDate2, createdDate2, account.getId(), account.getPaymentMethodId(), externalKey2);
         paymentModelDao2.setStateName("CAPTURE_ERRORED");
         final PaymentTransactionModelDao transaction2 = new PaymentTransactionModelDao(createdDate2, createdDate2, null, transactionExternalKey2,
                                                                                        paymentModelDao2.getId(), TransactionType.AUTHORIZE, createdDate2,
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        internalCallContext.setAccountRecordId(2L);
-        internalCallContext.setCreatedDate(createdDate2);
-        internalCallContext.setUpdatedDate(createdDate2);
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao2, transaction2, internalCallContext);
 
         // Right before createdBeforeDate, so it should be returned
         final DateTime createdDate3 = createdBeforeDate.minusDays(1);
-        final PaymentModelDao paymentModelDao3 = new PaymentModelDao(createdDate3, createdDate3, accountId, paymentMethodId, externalKey3);
+        clock.setTime(createdDate3);
+        account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentModelDao paymentModelDao3 = new PaymentModelDao(createdDate3, createdDate3, account.getId(), account.getPaymentMethodId(), externalKey3);
         paymentModelDao3.setStateName("CAPTURE_ERRORED");
         final PaymentTransactionModelDao transaction3 = new PaymentTransactionModelDao(createdDate3, createdDate3, null, transactionExternalKey3,
                                                                                        paymentModelDao3.getId(), TransactionType.AUTHORIZE, createdDate3,
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        internalCallContext.setAccountRecordId(3L);
-        internalCallContext.setCreatedDate(createdDate3);
-        internalCallContext.setUpdatedDate(createdDate3);
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao3, transaction3, internalCallContext);
 
         // Right before createdBeforeDate but with a SUCCESS state so it should NOT be returned
         final DateTime createdDate4 = createdBeforeDate.minusDays(1);
-        final PaymentModelDao paymentModelDao4 = new PaymentModelDao(createdDate4, createdDate4, accountId, paymentMethodId, externalKey4);
+        clock.setTime(createdDate4);
+        account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentModelDao paymentModelDao4 = new PaymentModelDao(createdDate4, createdDate4, account.getId(), account.getPaymentMethodId(), externalKey4);
         paymentModelDao4.setStateName("CAPTURE_SUCCESS");
         final PaymentTransactionModelDao transaction4 = new PaymentTransactionModelDao(createdDate4, createdDate4, null, transactionExternalKey4,
                                                                                        paymentModelDao4.getId(), TransactionType.AUTHORIZE, createdDate4,
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        internalCallContext.setAccountRecordId(4L);
-        internalCallContext.setCreatedDate(createdDate4);
-        internalCallContext.setUpdatedDate(createdDate4);
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao4, transaction4, internalCallContext);
 
         // Right after createdBeforeDate, so it should NOT be returned
         final DateTime createdDate5 = createdBeforeDate.plusDays(1);
-        final PaymentModelDao paymentModelDao5 = new PaymentModelDao(createdDate5, createdDate5, accountId, paymentMethodId, externalKey5);
+        clock.setTime(createdDate5);
+        account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentModelDao paymentModelDao5 = new PaymentModelDao(createdDate5, createdDate5, account.getId(), account.getPaymentMethodId(), externalKey5);
         paymentModelDao5.setStateName("CAPTURE_ERRORED");
         final PaymentTransactionModelDao transaction5 = new PaymentTransactionModelDao(createdDate5, createdDate5, null, transactionExternalKey5,
                                                                                        paymentModelDao5.getId(), TransactionType.AUTHORIZE, createdDate5,
                                                                                        TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                        "unknown", "");
 
-        internalCallContext.setAccountRecordId(5L);
-        internalCallContext.setCreatedDate(createdDate5);
-        internalCallContext.setUpdatedDate(createdDate5);
         paymentDao.insertPaymentWithFirstTransaction(paymentModelDao5, transaction5, internalCallContext);
 
         final String[] errorStates = {"AUTH_ERRORED", "CAPTURE_ERRORED", "REFUND_ERRORED", "CREDIT_ERRORED"};
@@ -431,25 +423,26 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testPaginationForPaymentByStatesAcrossTenants() {
-        // Right before createdAfterDate, so should not be returned
+    public void testPaginationForPaymentByStatesAcrossTenants() throws Exception {
         final DateTime createdDate1 = clock.getUTCNow().minusHours(1);
+        clock.setTime(createdDate1);
+
+        final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
 
         final int NB_ENTRIES = 30;
         for (int i = 0; i < NB_ENTRIES; i++) {
-            final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID().toString());
+            final PaymentModelDao paymentModelDao1 = new PaymentModelDao(createdDate1, createdDate1, account.getId(), account.getPaymentMethodId(), UUID.randomUUID().toString());
             final PaymentTransactionModelDao transaction1 = new PaymentTransactionModelDao(createdDate1, createdDate1, null, UUID.randomUUID().toString(),
                                                                                            paymentModelDao1.getId(), TransactionType.AUTHORIZE, createdDate1,
                                                                                            TransactionStatus.UNKNOWN, BigDecimal.TEN, Currency.AED,
                                                                                            "unknown", "");
 
-            internalCallContext.setAccountRecordId(1L);
-            internalCallContext.setCreatedDate(createdDate1);
-            internalCallContext.setUpdatedDate(createdDate1);
             paymentDao.insertPaymentWithFirstTransaction(paymentModelDao1, transaction1, internalCallContext);
         }
 
-        final Pagination<PaymentTransactionModelDao> result = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.UNKNOWN), clock.getUTCNow(), createdDate1, 0L, new Long(NB_ENTRIES));
+        clock.setTime(createdDate1.plusHours(1));
+
+        final Pagination<PaymentTransactionModelDao> result = paymentDao.getByTransactionStatusAcrossTenants(ImmutableList.of(TransactionStatus.UNKNOWN), clock.getUTCNow(), createdDate1, 0L, (long) NB_ENTRIES);
         Assert.assertEquals(result.getTotalNbRecords(), new Long(NB_ENTRIES));
 
         final Iterator<PaymentTransactionModelDao> iterator = result.iterator();
@@ -461,10 +454,7 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
-    public void testPaymentAttemptsByStateAcrossTenants() {
-
-        final UUID paymentMethodId = UUID.randomUUID();
-        final UUID accountId = UUID.randomUUID();
+    public void testPaymentAttemptsByStateAcrossTenants() throws Exception {
         final String externalKey1 = "gfhfg";
         final String transactionExternalKey1 = "sadas";
 
@@ -477,42 +467,41 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final String stateName = "FOO";
         final String pluginName = "miraculous";
 
-        final PaymentAttemptModelDao attempt1 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey1,
+        clock.setTime(createdAfterDate);
+        Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentAttemptModelDao attempt1 = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey1,
                                                                            UUID.randomUUID(), transactionExternalKey1, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
                                                                            ImmutableList.<String>of(pluginName), null);
 
-        final PaymentAttemptModelDao attempt2 = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey2,
+        paymentDao.insertPaymentAttemptWithProperties(attempt1, internalCallContext);
+
+        account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
+
+        final PaymentAttemptModelDao attempt2 = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey2,
                                                                            UUID.randomUUID(), transactionExternalKey2, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
                                                                            ImmutableList.<String>of(pluginName), null);
 
-        internalCallContext.setAccountRecordId(1L);
-        internalCallContext.setCreatedDate(createdAfterDate);
-        internalCallContext.setUpdatedDate(createdAfterDate);
-        paymentDao.insertPaymentAttemptWithProperties(attempt1, internalCallContext);
-
-        internalCallContext.setAccountRecordId(2L);
-        internalCallContext.setCreatedDate(createdAfterDate);
-        internalCallContext.setUpdatedDate(createdAfterDate);
-        paymentDao.insertPaymentAttemptWithProperties(attempt2, internalCallContext);
+       paymentDao.insertPaymentAttemptWithProperties(attempt2, internalCallContext);
 
         final Pagination<PaymentAttemptModelDao> result = paymentDao.getPaymentAttemptsByStateAcrossTenants(stateName, createdBeforeDate, 0L, 2L);
         Assert.assertEquals(result.getTotalNbRecords().longValue(), 2L);
     }
 
     @Test(groups = "slow")
-    public void testUpdatePaymentAttempt() throws PluginPropertySerializerException {
+    public void testUpdatePaymentAttempt() throws Exception {
+        final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
+        clock.setTime(createdAfterDate);
+
+        final Account account = testHelper.createTestAccount(UUID.randomUUID().toString(), true);
 
-        final UUID paymentMethodId = UUID.randomUUID();
-        final UUID accountId = UUID.randomUUID();
         final String externalKey1 = "2354";
         final String transactionExternalKey1 = "jkjkjk";
 
-        final DateTime createdAfterDate = clock.getUTCNow().minusDays(10);
-
         final String stateName = "RRRRR";
         final String pluginName = "elated";
 
-        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(accountId, paymentMethodId, createdAfterDate, createdAfterDate, externalKey1,
+        final PaymentAttemptModelDao attempt = new PaymentAttemptModelDao(account.getId(), account.getPaymentMethodId(), createdAfterDate, createdAfterDate, externalKey1,
                                                                           UUID.randomUUID(), transactionExternalKey1, TransactionType.AUTHORIZE, stateName, BigDecimal.ONE, Currency.USD,
                                                                           ImmutableList.<String>of(pluginName), null);
 
@@ -530,7 +519,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         properties.add(new PluginProperty("prop1", "value1", false));
         properties.add(new PluginProperty("prop2", "value2", false));
 
-
         final byte [] serializedProperties = PluginPropertySerializer.serialize(properties);
         paymentDao.updatePaymentAttemptWithProperties(rehydratedAttempt.getId(), transactionId, newStateName, serializedProperties, internalCallContext);
         final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(rehydratedAttempt.getId(), internalCallContext);
@@ -540,7 +528,6 @@ public class TestPaymentDao extends PaymentTestSuiteWithEmbeddedDB {
         final Iterable<PluginProperty> properties2 = PluginPropertySerializer.deserialize(attempt2.getPluginProperties());
         checkProperty(properties2, new PluginProperty("prop1", "value1", false));
         checkProperty(properties2, new PluginProperty("prop2", "value2", false));
-
     }
 
     private void checkProperty(final Iterable<PluginProperty> properties, final PluginProperty expected) {
diff --git a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
index c5d16c5..a4bc6e9 100644
--- a/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
+++ b/payment/src/test/java/org/killbill/billing/payment/glue/TestPaymentModule.java
@@ -31,9 +31,10 @@ import org.killbill.billing.payment.provider.MockPaymentProviderPluginModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.api.TagUserApi;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.MemoryGlobalLockerModule;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
@@ -72,6 +73,7 @@ public class TestPaymentModule extends PaymentModule {
         install(new MemoryGlobalLockerModule(configSource));
         install(new MockTenantModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
 
         installExternalApis();
diff --git a/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java b/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
index 981e974..0100b74 100644
--- a/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
+++ b/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
@@ -32,6 +32,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
 import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 
 public class MockInvoice extends EntityBase implements Invoice {
     private final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
@@ -42,15 +43,17 @@ public class MockInvoice extends EntityBase implements Invoice {
     private final LocalDate targetDate;
     private final Currency currency;
     private final boolean migrationInvoice;
+    private final InvoiceStatus status;
+    private final boolean parentInvoice;
 
     // used to create a new invoice
     public MockInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
-        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+        this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED, false);
     }
 
     // used to hydrate invoice from persistence layer
     public MockInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
-                       final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
+                       @Nullable final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice, final InvoiceStatus status, final boolean parentInvoice) {
         super(invoiceId);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
@@ -58,6 +61,8 @@ public class MockInvoice extends EntityBase implements Invoice {
         this.targetDate = targetDate;
         this.currency = currency;
         this.migrationInvoice = isMigrationInvoice;
+        this.status = status;
+        this.parentInvoice = parentInvoice;
     }
 
     @Override
@@ -194,8 +199,13 @@ public class MockInvoice extends EntityBase implements Invoice {
     }
 
     @Override
+    public InvoiceStatus getStatus() {
+        return status;
+    }
+
+    @Override
     public String toString() {
-        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + "]";
+        return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + ", status=" + status + ", parentInvoice=" + parentInvoice + "]";
     }
 
     @Override
@@ -207,5 +217,10 @@ public class MockInvoice extends EntityBase implements Invoice {
     public BigDecimal getOriginalChargedAmount() {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public boolean isParentInvoice() {
+        return parentInvoice;
+    }
 }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/MockRecurringInvoiceItem.java b/payment/src/test/java/org/killbill/billing/payment/MockRecurringInvoiceItem.java
index cf3eb92..6cb7be3 100644
--- a/payment/src/test/java/org/killbill/billing/payment/MockRecurringInvoiceItem.java
+++ b/payment/src/test/java/org/killbill/billing/payment/MockRecurringInvoiceItem.java
@@ -185,6 +185,10 @@ public class MockRecurringInvoiceItem extends EntityBase implements InvoiceItem 
         return rate;
     }
 
+    @Override
+    public UUID getChildAccountId() {
+        return null;
+    }
 
     @Override
     public String toString() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index 713219a..b50ce2c 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -44,7 +44,7 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.profiling.Profiling;
 import org.testng.annotations.AfterMethod;
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index f0f7e09..73cc4ea 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -37,7 +37,7 @@ import org.killbill.billing.payment.glue.TestPaymentModuleWithEmbeddedDB;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.util.config.PaymentConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.profiling.Profiling;
diff --git a/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java b/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
index 3241ea5..5bd6128 100644
--- a/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
+++ b/payment/src/test/java/org/killbill/billing/payment/provider/TestExternalPaymentProviderPlugin.java
@@ -45,7 +45,7 @@ public class TestExternalPaymentProviderPlugin extends PaymentTestSuiteNoDB {
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
-        plugin = new ExternalPaymentProviderPlugin(clock);
+        plugin = new ExternalPaymentProviderPlugin(clock, paymentConfig);
     }
 
     @Test(groups = "fast")
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
index 982e35f..c248d23 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestJanitor.java
@@ -301,7 +301,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         assertNotificationsCompleted(internalCallContext, 5);
         testListener.assertListenerStatus();
 
-        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
     }
 
@@ -341,7 +341,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         Assert.assertEquals(paymentTransactionHistoryAfterJanitor.size(), 4);
         Assert.assertEquals(paymentTransactionHistoryAfterJanitor.get(3).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
 
-        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         // Janitor should have moved us to PAYMENT_FAILURE
         assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PAYMENT_FAILURE);
     }
@@ -362,7 +362,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         } catch (PaymentApiException ignore) {
             testListener.assertListenerStatus();
         }
-        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment payment = paymentApi.getPaymentByExternalKey(paymentExternalKey, false, false, ImmutableList.<PluginProperty>of(), callContext);
 
         // Artificially move the transaction status to UNKNOWN
         final String paymentStateName = paymentSMHelper.getErroredStateForTransaction(TransactionType.AUTHORIZE).toString();
@@ -411,7 +411,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
 
         assertNotificationsCompleted(internalCallContext, 5);
         testListener.assertListenerStatus();
-        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
         Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS);
     }
 
@@ -445,7 +445,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
         testListener.assertListenerStatus();
 
         // 15s,1m,3m,1h,1d,1d,1d,1d,1d
-        for (final TimeSpan cur : paymentConfig.getIncompleteTransactionsRetries()) {
+        for (final TimeSpan cur : paymentConfig.getIncompleteTransactionsRetries(internalCallContext)) {
             // Verify there is a notification to retry updating the value
             assertEquals(getPendingNotificationCnt(internalCallContext), 1);
 
@@ -458,7 +458,7 @@ public class TestJanitor extends PaymentTestSuiteWithEmbeddedDB {
             Thread.sleep(1000);
             assertNotificationsCompleted(internalCallContext, 5);
 
-            final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+            final Payment updatedPayment = paymentApi.getPayment(payment.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext);
             Assert.assertEquals(updatedPayment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.PENDING);
         }
 
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
index f32c37e..772235b 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestPaymentHelper.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -20,29 +20,31 @@ package org.killbill.billing.payment;
 
 import java.util.UUID;
 
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
-import org.killbill.billing.ObjectType;
+import org.killbill.billing.GuicyKillbillTestSuite;
+import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.dao.MockNonEntityDao;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
 import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
 import org.killbill.billing.invoice.api.InvoiceItem;
+import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
@@ -56,38 +58,38 @@ public class TestPaymentHelper {
 
     protected final AccountUserApi accountApi;
     protected final AccountInternalApi accountInternalApi;
+    protected final ImmutableAccountInternalApi immutableAccountInternalApi;
     protected final InvoiceInternalApi invoiceApi;
-    protected PaymentApi paymentApi;
     private final PersistentBus eventBus;
     private final Clock clock;
     private final NonEntityDao nonEntityDao;
-    private final MockNonEntityDao mockNonEntityDao;
-    private final CacheControllerDispatcher cacheControllerDispatcher;
     private final MutableInternalCallContext internalCallContext;
+    private final InternalCallContextFactory internalCallContextFactory;
     private final CallContext context;
+    protected PaymentApi paymentApi;
 
     @Inject
     public TestPaymentHelper(final AccountUserApi accountApi,
                              final AccountInternalApi accountInternalApi,
+                             final ImmutableAccountInternalApi immutableAccountInternalApi,
                              final InvoiceInternalApi invoiceApi,
                              final PaymentApi paymentApi,
                              final PersistentBus eventBus,
                              final Clock clock,
                              final NonEntityDao nonEntityDao,
-                             final MockNonEntityDao mockNonEntityDao,
-                             final CacheControllerDispatcher cacheControllerDispatcher,
                              final MutableInternalCallContext internalCallContext,
+                             final InternalCallContextFactory internalCallContextFactory,
                              final CallContext context) {
         this.accountApi = accountApi;
         this.eventBus = eventBus;
         this.accountInternalApi = accountInternalApi;
+        this.immutableAccountInternalApi = immutableAccountInternalApi;
         this.invoiceApi = invoiceApi;
         this.paymentApi = paymentApi;
         this.clock = clock;
         this.nonEntityDao = nonEntityDao;
-        this.mockNonEntityDao = mockNonEntityDao;
-        this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.internalCallContext = internalCallContext;
+        this.internalCallContextFactory = internalCallContextFactory;
         this.context = context;
     }
 
@@ -120,7 +122,8 @@ public class TestPaymentHelper {
 
         final InvoiceCreationInternalEvent event = new MockInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
                                                                                 invoice.getBalance(), invoice.getCurrency(),
-                                                                                invoice.getInvoiceDate(), 1L, 2L, null);
+                                                                                invoice.getInvoiceDate(), internalCallContext.getAccountRecordId(),
+                                                                                internalCallContext.getTenantRecordId(), null);
 
         eventBus.post(event);
         return invoice;
@@ -141,22 +144,17 @@ public class TestPaymentHelper {
         Mockito.when(accountData.getBillCycleDayLocal()).thenReturn(1);
         Mockito.when(accountData.isMigrated()).thenReturn(false);
         Mockito.when(accountData.isNotifiedForInvoices()).thenReturn(false);
+        Mockito.when(accountData.getTimeZone()).thenReturn(DateTimeZone.UTC);
+        Mockito.when(accountData.getCreatedDate()).thenReturn(clock.getUTCNow());
 
         Account account;
         if (isFastTest()) {
-            account = accountData;
-            Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>any(), Mockito.<InternalTenantContext>any())).thenReturn(account);
-            Mockito.when(accountInternalApi.getAccountByKey(Mockito.anyString(), Mockito.<InternalTenantContext>any())).thenReturn(account);
-            mockNonEntityDao.addTenantRecordIdMapping(account.getId(), internalCallContext);
-            mockNonEntityDao.addAccountRecordIdMapping(account.getId(), internalCallContext);
+            account = GuicyKillbillTestSuiteNoDB.createMockAccount(accountData, accountApi, accountInternalApi, immutableAccountInternalApi, nonEntityDao, clock, internalCallContextFactory, context, internalCallContext);
         } else {
             account = accountApi.createAccount(accountData, context);
-
-            final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID));
-            internalCallContext.setAccountRecordId(accountRecordId);
         }
 
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        GuicyKillbillTestSuite.refreshCallContext(account.getId(), clock, internalCallContextFactory, context, internalCallContext);
 
         if (addPaymentMethod) {
             final PaymentMethodPlugin pm = new DefaultNoOpPaymentMethodPlugin(UUID.randomUUID().toString(), true, null);
@@ -167,10 +165,15 @@ public class TestPaymentHelper {
     }
 
     public Account addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo) throws Exception {
-        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, true, paymentMethodInfo, ImmutableList.<PluginProperty>of(), context);
+        return addTestPaymentMethod(account, paymentMethodInfo, ImmutableList.<PluginProperty>of());
+    }
+
+    public Account addTestPaymentMethod(final Account account, final PaymentMethodPlugin paymentMethodInfo, final Iterable<PluginProperty> pluginProperties) throws Exception {
+        final UUID paymentMethodId = paymentApi.addPaymentMethod(account, paymentMethodInfo.getExternalPaymentMethodId(), MockPaymentProviderPlugin.PLUGIN_NAME, true, paymentMethodInfo, pluginProperties, context);
         if (isFastTest()) {
-            Mockito.when(account.getPaymentMethodId()).thenReturn(paymentMethodId);
-            return account;
+            final Account account1 = new MockAccountBuilder(account).paymentMethodId(paymentMethodId).build();
+            accountApi.updateAccount(account, context);
+            return account1;
         } else {
             // To reflect the payment method id change
             return accountApi.getAccountById(account.getId(), context);
diff --git a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
index 6949690..676c940 100644
--- a/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
+++ b/payment/src/test/java/org/killbill/billing/payment/TestRetryService.java
@@ -83,7 +83,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
     }
 
     private Payment getPaymentForExternalKey(final String externalKey) throws PaymentApiException {
-        final Payment payment = paymentProcessor.getPaymentByExternalKey(externalKey, false, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
+        final Payment payment = paymentProcessor.getPaymentByExternalKey(externalKey, false, false, ImmutableList.<PluginProperty>of(), callContext, internalCallContext);
         return payment;
     }
 
@@ -247,7 +247,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
         final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(payment.getId(), internalCallContext);
         assertEquals(transactions.size(), 1);
 
-        int maxTries = paymentConfig.getPaymentFailureRetryDays().size();
+        int maxTries = paymentConfig.getPaymentFailureRetryDays(internalCallContext).size();
         for (int curFailure = 0; curFailure < maxTries; curFailure++) {
 
             // Set plugin to fail with specific type unless this is the last attempt and we want a success
@@ -334,7 +334,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
         final List<PaymentTransactionModelDao> transactions = paymentDao.getTransactionsForPayment(payment.getId(), internalCallContext);
         assertEquals(transactions.size(), 1);
 
-        int maxTries = paymentConfig.getPaymentFailureRetryDays().size();
+        int maxTries = paymentConfig.getPaymentFailureRetryDays(internalCallContext).size();
         for (int curFailure = 0; curFailure < maxTries; curFailure++) {
 
             // Set plugin to fail with specific type unless this is the last attempt and we want a success
@@ -394,7 +394,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
     private void moveClockForFailureType(final FailureType failureType, final int curFailure) throws InterruptedException {
         final int nbDays;
         if (failureType == FailureType.PAYMENT_FAILURE) {
-            nbDays = paymentConfig.getPaymentFailureRetryDays().get(curFailure) + 1;
+            nbDays = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(curFailure) + 1;
         } else {
             nbDays = 1;
         }
@@ -403,7 +403,7 @@ public class TestRetryService extends PaymentTestSuiteNoDB {
 
     private int getMaxRetrySizeForFailureType(final FailureType failureType) {
         if (failureType == FailureType.PAYMENT_FAILURE) {
-            return paymentConfig.getPaymentFailureRetryDays().size();
+            return paymentConfig.getPaymentFailureRetryDays(internalCallContext).size();
         } else {
             return 0;
         }

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index 88c4c45..2254f5b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,10 +21,10 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.94.5</version>
+        <version>0.135</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.16.10-SNAPSHOT</version>
+    <version>0.17.9-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index d4f6e46..dda1488 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
@@ -39,6 +39,19 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
             <scope>compile</scope>
@@ -65,17 +78,22 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.ning</groupId>
+            <artifactId>async-http-client</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.palominolabs.metrics</groupId>
             <artifactId>metrics-guice</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
-            <groupId>com.sun.jersey.contribs</groupId>
-            <artifactId>jersey-guice</artifactId>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.wordnik</groupId>
-            <artifactId>swagger-jersey-jaxrs_2.10</artifactId>
+            <groupId>com.sun.jersey.contribs</groupId>
+            <artifactId>jersey-guice</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <!-- Will be required when we upgrade metrics-guice to 3.1.3 -->
         <!--<dependency>-->
@@ -88,18 +106,60 @@
             <artifactId>HikariCP-java6</artifactId>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-logback</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-servlet</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-jersey-jaxrs</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
         </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>jsr311-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
         <!-- Make sure to only include this in tests (it contains mysqld for all platforms and is around 134M) -->
         <dependency>
             <groupId>mysql</groupId>
@@ -123,6 +183,11 @@
             <artifactId>shiro-web</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-deploy</artifactId>
             <scope>test</scope>
@@ -149,6 +214,11 @@
         </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-util</artifactId>
             <scope>test</scope>
         </dependency>
@@ -160,6 +230,11 @@
         <dependency>
             <groupId>org.javassist</groupId>
             <artifactId>javassist</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
@@ -199,6 +274,14 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-entitlement</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-internal-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-invoice</artifactId>
         </dependency>
         <dependency>
@@ -242,6 +325,7 @@
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-osgi-bundles-lib-killbill</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
@@ -277,9 +361,17 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing.plugin</groupId>
+            <artifactId>killbill-plugin-api-control</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing.plugin</groupId>
             <artifactId>killbill-plugin-api-notification</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.kill-bill.billing.plugin</groupId>
+            <artifactId>killbill-plugin-api-payment</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-clock</artifactId>
         </dependency>
@@ -294,13 +386,17 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
-            <artifactId>killbill-embeddeddb-h2</artifactId>
-            <scope>compile</scope>
+            <artifactId>killbill-concurrent</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-common</artifactId>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
-            <scope>compile</scope>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
@@ -309,15 +405,31 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-jdbi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-queue</artifactId>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
-            <artifactId>killbill-skeleton</artifactId>
+            <artifactId>killbill-queue</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
-            <artifactId>killbill-xmlloader</artifactId>
+            <artifactId>killbill-skeleton</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
@@ -346,6 +458,7 @@
         <dependency>
             <groupId>org.weakref</groupId>
             <artifactId>jmxutils</artifactId>
+            <scope>runtime</scope>
         </dependency>
     </dependencies>
     <build>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/config/MultiTenantNotificationConfig.java b/profiles/killbill/src/main/java/org/killbill/billing/server/config/MultiTenantNotificationConfig.java
new file mode 100644
index 0000000..08ffd8a
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/config/MultiTenantNotificationConfig.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.config;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.payment.glue.PaymentModule;
+import org.killbill.billing.util.config.definition.KillbillConfig;
+import org.killbill.billing.util.config.definition.NotificationConfig;
+import org.killbill.billing.util.config.tenant.CacheConfig;
+import org.killbill.billing.util.config.tenant.MultiTenantConfigBase;
+import org.skife.config.Param;
+import org.skife.config.TimeSpan;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+public class MultiTenantNotificationConfig extends MultiTenantConfigBase implements NotificationConfig {
+
+    private final Map<String, Method> methodsCache = new HashMap<String, Method>();
+    private final NotificationConfig staticConfig;
+
+    @Inject
+    public MultiTenantNotificationConfig(@Named(PaymentModule.STATIC_CONFIG) final NotificationConfig staticConfig, final CacheConfig cacheConfig) {
+        super(cacheConfig);
+        this.staticConfig = staticConfig;
+    }
+
+    @Override
+    public List<TimeSpan> getPushNotificationsRetries() {
+        return staticConfig.getPushNotificationsRetries();
+    }
+
+    @Override
+    public List<TimeSpan> getPushNotificationsRetries(@Param("dummy") final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("getPushNotificationsRetries", tenantContext);
+        if (result != null) {
+            return convertToListTimeSpan(result, "getPushNotificationsRetries");
+        }
+        return getPushNotificationsRetries();
+    }
+
+    @Override
+    protected Class<? extends KillbillConfig> getConfigClass() {
+        return NotificationConfig.class;
+    }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java b/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
index a085bfa..cad84c9 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/DefaultServerService.java
@@ -25,8 +25,11 @@ import org.killbill.billing.lifecycle.glue.BusModule;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
 import org.killbill.billing.server.notifications.PushNotificationListener;
+import org.killbill.billing.server.notifications.PushNotificationRetryService;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueAlreadyExists;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -34,15 +37,19 @@ public class DefaultServerService implements ServerService {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultServerService.class);
 
-    private static final String SERVER_SERVICE = "server-service";
+    public static final String SERVER_SERVICE = "server-service";
 
     private final PersistentBus bus;
     private final PushNotificationListener pushNotificationListener;
+    private final PushNotificationRetryService pushNotificationRetryService;
 
     @Inject
-    public DefaultServerService(@Named(BusModule.EXTERNAL_BUS_NAMED) final PersistentBus bus, final PushNotificationListener pushNotificationListener) {
+    public DefaultServerService(@Named(BusModule.EXTERNAL_BUS_NAMED) final PersistentBus bus,
+                                final PushNotificationListener pushNotificationListener,
+                                final PushNotificationRetryService pushNotificationRetryService) {
         this.bus = bus;
         this.pushNotificationListener = pushNotificationListener;
+        this.pushNotificationRetryService = pushNotificationRetryService;
     }
 
     @Override
@@ -51,20 +58,27 @@ public class DefaultServerService implements ServerService {
     }
 
     @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
-    public void registerForNotifications() {
+    public void registerForNotifications() throws NotificationQueueAlreadyExists {
         try {
             bus.register(pushNotificationListener);
         } catch (final EventBusException e) {
             log.warn("Failed to register PushNotificationListener", e);
         }
+        pushNotificationRetryService.initialize();
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
+    public void start() {
+        pushNotificationRetryService.start();
     }
 
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
-    public void unregisterForNotifications() {
+    public void unregisterForNotifications() throws NoSuchNotificationQueue {
         try {
             bus.unregister(pushNotificationListener);
         } catch (final EventBusException e) {
             log.warn("Failed to unregister PushNotificationListener", e);
         }
+        pushNotificationRetryService.stop();
     }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
index 51a2a75..79838b1 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
@@ -43,13 +43,14 @@ import com.google.common.collect.ImmutableMap;
 import com.google.inject.Module;
 import com.google.inject.servlet.ServletModule;
 import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter;
-import com.wordnik.swagger.jaxrs.config.BeanConfig;
+import io.swagger.jaxrs.config.BeanConfig;
 
 public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
 
     private static final Logger logger = LoggerFactory.getLogger(KillbillGuiceListener.class);
 
-    private static final String SWAGGER_PATH = "api-docs";
+    // See io.swagger.jaxrs.listing.ApiListingResource
+    private static final String SWAGGER_PATH = "swagger.*";
 
     private KillbillEventHandler killbilleventHandler;
 
@@ -61,7 +62,7 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
                                                                              .addJaxrsResource("org.killbill.billing.jaxrs.mappers")
                                                                              .addJaxrsResource("org.killbill.billing.jaxrs.resources")
                                                                              // Swagger integration
-                                                                             .addJaxrsResource("com.wordnik.swagger.jersey.listing");
+                                                                             .addJaxrsResource("io.swagger.jaxrs.listing");
 
         // Set the per-thread RequestData first
         builder.addJerseyFilter(RequestDataFilter.class.getName());
@@ -144,7 +145,6 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
         beanConfig.setContact("killbilling-users@googlegroups.com");
         beanConfig.setLicense("Apache License, Version 2.0");
         beanConfig.setLicenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
-        beanConfig.setBasePath(config.getBaseUrl());
         beanConfig.setScan(true);
     }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
index 1edb705..8ad5478 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/LuhnMaskingObfuscator.java
@@ -76,10 +76,7 @@ public class LuhnMaskingObfuscator extends Obfuscator {
                 numberEnd = last4pos[3] + 1;
                 if ((digitsSeen >= MIN_CC_DIGITS)
                     && luhnCheck(stripSeparators(formattedMessage.substring(numberStart, numberEnd)))) {
-                    masked.append(formattedMessage, unwrittenStart, numberStart);
-                    masked.append(obfuscateConfidentialData(formattedMessage.substring(numberStart, numberEnd),
-                                                            formattedMessage.substring(last4pos[0], numberEnd)));
-                    masked.append(formattedMessage, last4pos[0], numberEnd);
+                    maskCC(formattedMessage, unwrittenStart, numberStart, numberEnd, last4pos[0], masked);
                     unwrittenStart = numberEnd;
                 }
                 numberStart = -1;
@@ -89,10 +86,7 @@ public class LuhnMaskingObfuscator extends Obfuscator {
 
         if (numberStart != -1 && (digitsSeen >= MIN_CC_DIGITS)
             && luhnCheck(stripSeparators(formattedMessage.substring(numberStart, pos)))) {
-            masked.append(formattedMessage, unwrittenStart, numberStart);
-            masked.append(obfuscateConfidentialData(formattedMessage.substring(numberStart, pos),
-                                                    formattedMessage.substring(last4pos[0], pos)));
-            masked.append(formattedMessage, last4pos[0], pos);
+            maskCC(formattedMessage, unwrittenStart, numberStart, pos, last4pos[0], masked);
         } else {
             masked.append(formattedMessage, unwrittenStart, pos);
         }
@@ -100,6 +94,30 @@ public class LuhnMaskingObfuscator extends Obfuscator {
         return masked.toString();
     }
 
+    private void maskCC(final String formattedMessage, final int unwrittenStart, final int numberStart, final int numberEnd, final int last4pos, final StringBuilder masked) {
+        masked.append(formattedMessage, unwrittenStart, numberStart);
+
+        // Don't mask the BIN
+        int binNumbersLeft = 6;
+        int panStartPos = numberStart;
+        char current;
+        while (binNumbersLeft > 0) {
+            current = formattedMessage.charAt(panStartPos);
+            if (isDigit(current)) {
+                masked.append(current);
+                binNumbersLeft--;
+            }
+            panStartPos++;
+        }
+
+        // Append the mask
+        masked.append(obfuscateConfidentialData(formattedMessage.substring(panStartPos, numberEnd),
+                                                formattedMessage.substring(last4pos, numberEnd)));
+
+        // Append last 4
+        masked.append(formattedMessage, last4pos, numberEnd);
+    }
+
     private boolean hasEnoughDigits(final CharSequence formattedMessage) {
         int digits = 0;
         final int length = formattedMessage.length();
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
index bf37cca..cd4426d 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/log/obfuscators/Obfuscator.java
@@ -34,17 +34,7 @@ public abstract class Obfuscator {
 
     protected static final int DEFAULT_PATTERN_FLAGS = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL;
 
-    protected static final String MASK_LABEL = "MASKED";
-    protected static final int MASK_LABEL_LENGTH = MASK_LABEL.length();
     protected static final char PAD_CHAR = '*';
-    protected static final int MASK_LOOKUPS_SIZE = 20;
-    protected final String[] MASK_LOOKUPS = new String[MASK_LOOKUPS_SIZE];
-
-    public Obfuscator() {
-        for (int i = 0; i < MASK_LOOKUPS.length; i++) {
-            MASK_LOOKUPS[i] = buildMask(i);
-        }
-    }
 
     public abstract String obfuscate(final String originalString, final ILoggingEvent event);
 
@@ -111,33 +101,6 @@ public abstract class Obfuscator {
     @VisibleForTesting
     String obfuscateConfidentialData(final CharSequence confidentialSequence, @Nullable final CharSequence unmasked) {
         final int maskedLength = unmasked == null ? confidentialSequence.length() : confidentialSequence.length() - unmasked.length();
-        if (maskedLength < MASK_LOOKUPS_SIZE) {
-            return MASK_LOOKUPS[maskedLength];
-        } else {
-            return buildMask(maskedLength);
-        }
-    }
-
-    /**
-     * Create a masking string with the given length.
-     *
-     * @param maskedLength obfuscated String length
-     * @return a mask string
-     */
-    private String buildMask(final int maskedLength) {
-        final int pads = maskedLength - MASK_LABEL_LENGTH;
-        final StringBuilder mask = new StringBuilder(maskedLength);
-        if (pads <= 0) {
-            mask.append(MASK_LABEL);
-        } else {
-            for (int i = 0; i < pads / 2; i++) {
-                mask.append(PAD_CHAR);
-            }
-            mask.append(MASK_LABEL);
-            while (mask.length() < maskedLength) {
-                mask.append(PAD_CHAR);
-            }
-        }
-        return mask.toString();
+        return new String(new char[maskedLength]).replace('\0', PAD_CHAR);
     }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
index 2fb4840..d53b571 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
@@ -22,7 +22,7 @@ import javax.sql.DataSource;
 
 import org.apache.shiro.cache.CacheManager;
 import org.killbill.billing.server.security.KillbillJdbcTenantRealm;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.glue.ShiroEhCacheInstrumentor;
 
 import com.google.inject.Inject;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
index c8c94c2..ff6de44 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
@@ -58,11 +58,14 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.server.DefaultServerService;
 import org.killbill.billing.server.ServerService;
 import org.killbill.billing.server.config.KillbillServerConfig;
+import org.killbill.billing.server.config.MultiTenantNotificationConfig;
 import org.killbill.billing.server.filters.ResponseCorsFilter;
 import org.killbill.billing.server.notifications.PushNotificationListener;
+import org.killbill.billing.server.notifications.PushNotificationRetryService;
 import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
 import org.killbill.billing.tenant.glue.DefaultTenantModule;
 import org.killbill.billing.usage.glue.UsageModule;
+import org.killbill.billing.util.config.definition.NotificationConfig;
 import org.killbill.billing.util.dao.AuditLogModelDaoMapper;
 import org.killbill.billing.util.dao.RecordIdIdMappingsMapper;
 import org.killbill.billing.util.email.EmailModule;
@@ -72,12 +75,13 @@ import org.killbill.billing.util.glue.BroadcastModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
 import org.killbill.billing.util.glue.ClockModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
-import org.killbill.billing.util.glue.NodesModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
 import org.killbill.billing.util.glue.KillbillApiAopModule;
+import org.killbill.billing.util.glue.NodesModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
 import org.killbill.billing.util.glue.RecordIdModule;
 import org.killbill.billing.util.glue.SecurityModule;
@@ -87,15 +91,18 @@ import org.killbill.clock.Clock;
 import org.killbill.clock.ClockMock;
 import org.killbill.commons.embeddeddb.EmbeddedDB;
 import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
+import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.ResultSetMapperFactory;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import ch.qos.logback.classic.helpers.MDCInsertingServletFilter;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Names;
 
 public class KillbillServerModule extends KillbillPlatformModule {
 
     public static final String SHIRO_DATA_SOURCE_ID = "shiro";
+    public static final String STATIC_CONFIG = "StaticConfig";
 
     public KillbillServerModule(final ServletContext servletContext, final KillbillServerConfig serverConfig, final KillbillConfigSource configSource) {
         super(servletContext, serverConfig, configSource);
@@ -145,6 +152,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
         install(new BroadcastModule(configSource));
         install(new BeatrixModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new CatalogModule(configSource));
         install(new CurrencyModule(configSource));
@@ -205,7 +213,12 @@ public class KillbillServerModule extends KillbillPlatformModule {
     }
 
     protected void configurePushNotification() {
-        bind(ServerService.class).to(DefaultServerService.class).asEagerSingleton();
+        final ConfigurationObjectFactory factory = new ConfigurationObjectFactory(skifeConfigSource);
+        final NotificationConfig notificationConfig = factory.build(NotificationConfig.class);
+        bind(NotificationConfig.class).annotatedWith(Names.named(STATIC_CONFIG)).toInstance(notificationConfig);
+        bind(NotificationConfig.class).to(MultiTenantNotificationConfig.class).asEagerSingleton();
         bind(PushNotificationListener.class).asEagerSingleton();
+        bind(PushNotificationRetryService.class).asEagerSingleton();
+        bind(ServerService.class).to(DefaultServerService.class).asEagerSingleton();
     }
 }
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
index 721c7a7..a623ff3 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
@@ -38,7 +38,7 @@ import org.apache.shiro.web.util.WebUtils;
 import org.killbill.billing.jaxrs.resources.JaxrsResource;
 import org.killbill.billing.server.security.FirstSuccessfulStrategyWith540;
 import org.killbill.billing.server.security.KillbillJdbcTenantRealm;
-import org.killbill.billing.util.config.RbacConfig;
+import org.killbill.billing.util.config.definition.RbacConfig;
 import org.killbill.billing.util.glue.EhCacheManagerProvider;
 import org.killbill.billing.util.glue.IniRealmProvider;
 import org.killbill.billing.util.glue.JDBCSessionDaoProvider;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java
new file mode 100644
index 0000000..e72dec3
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationKey.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.notifications;
+
+import java.util.UUID;
+
+import org.killbill.notificationq.api.NotificationEvent;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PushNotificationKey implements NotificationEvent {
+
+    private final UUID tenantId;
+    private final UUID accountId;
+    private final String eventType;
+    private final String objectType;
+    private final UUID objectId;
+    private final int attemptNumber;
+    private final String url;
+
+    @JsonCreator
+    public PushNotificationKey(@JsonProperty("tenantId") final UUID tenantId,
+                               @JsonProperty("accountId") final UUID accountId,
+                               @JsonProperty("eventType") final String eventType,
+                               @JsonProperty("objectType") final String objectType,
+                               @JsonProperty("objectId") final UUID objectId,
+                               @JsonProperty("attemptNumber")  final int attemptNumber,
+                               @JsonProperty("url") final String url) {
+        this.tenantId = tenantId;
+        this.accountId = accountId;
+        this.eventType = eventType;
+        this.objectType = objectType;
+        this.objectId = objectId;
+        this.attemptNumber = attemptNumber;
+        this.url = url;
+    }
+
+    public PushNotificationKey(final PushNotificationKey key, final int attemptNumber) {
+        this(key.getTenantId(), key.getAccountId(), key.getEventType(), key.getObjectType(), key.getObjectId(),
+             attemptNumber, key.getUrl());
+    }
+
+    public UUID getTenantId() {
+        return tenantId;
+    }
+
+    public UUID getAccountId() {
+        return accountId;
+    }
+
+    public String getEventType() {
+        return eventType;
+    }
+
+    public String getObjectType() {
+        return objectType;
+    }
+
+    public UUID getObjectId() {
+        return objectId;
+    }
+
+    public Integer getAttemptNumber() {
+        return attemptNumber;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    @Override
+    public String toString() {
+        return "PushNotificationKey{" +
+               "tenantId=" + tenantId +
+               ", accountId=" + accountId +
+               ", eventType='" + eventType + '\'' +
+               ", objectType='" + objectType + '\'' +
+               ", objectId=" + objectId +
+               ", attemptNumber=" + attemptNumber +
+               ", url='" + url + '\'' +
+               '}';
+    }
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
index 7e3a993..afa18cf 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationListener.java
@@ -22,16 +22,28 @@ import java.io.IOException;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.inject.Inject;
 
+import org.joda.time.DateTime;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.jaxrs.json.NotificationJson;
 import org.killbill.billing.notification.plugin.api.ExtBusEvent;
+import org.killbill.billing.server.DefaultServerService;
 import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.callcontext.CallContextFactory;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.config.definition.NotificationConfig;
+import org.killbill.clock.Clock;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.skife.config.TimeSpan;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,8 +54,10 @@ import com.ning.http.client.AsyncHttpClientConfig;
 import com.ning.http.client.ListenableFuture;
 import com.ning.http.client.Response;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
 import com.google.common.eventbus.AllowConcurrentEvents;
 import com.google.common.eventbus.Subscribe;
 
@@ -62,13 +76,23 @@ public class PushNotificationListener {
     private final CallContextFactory contextFactory;
     private final AsyncHttpClient httpClient;
     private final ObjectMapper mapper;
+    private final NotificationQueueService notificationQueueService;
+    private final InternalCallContextFactory internalCallContextFactory;
+    private final Clock clock;
+    private final NotificationConfig notificationConfig;
 
     @Inject
-    public PushNotificationListener(final ObjectMapper mapper, final TenantUserApi tenantApi, final CallContextFactory contextFactory) {
+    public PushNotificationListener(final ObjectMapper mapper, final TenantUserApi tenantApi, final CallContextFactory contextFactory,
+                                    final NotificationQueueService notificationQueueService, final InternalCallContextFactory internalCallContextFactory,
+                                    final Clock clock, final NotificationConfig notificationConfig) {
         this.httpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeout(TIMEOUT_NOTIFICATION * 1000).build());
         this.tenantApi = tenantApi;
         this.contextFactory = contextFactory;
         this.mapper = mapper;
+        this.notificationQueueService = notificationQueueService;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.clock = clock;
+        this.notificationConfig = notificationConfig;
     }
 
     @AllowConcurrentEvents
@@ -93,11 +117,12 @@ public class PushNotificationListener {
         final NotificationJson notification = new NotificationJson(event);
         final String body = mapper.writeValueAsString(notification);
         for (final String cur : callbacks) {
-            doPost(tenantId, cur, body, TIMEOUT_NOTIFICATION);
+            doPost(tenantId, cur, body, notification, TIMEOUT_NOTIFICATION, 0);
         }
     }
 
-    private boolean doPost(final UUID tenantId, final String url, final String body, final int timeoutSec) {
+    private boolean doPost(final UUID tenantId, final String url, final String body, final NotificationJson notification,
+                           final int timeoutSec, final int attemptRetryNumber) {
         final BoundRequestBuilder builder = httpClient.preparePost(url);
         builder.setBody(body == null ? "{}" : body);
         builder.addHeader(HTTP_HEADER_CONTENT_TYPE, CONTENT_TYPE_JSON);
@@ -112,11 +137,69 @@ public class PushNotificationListener {
                         }
                     });
             response = futureStatus.get(timeoutSec, TimeUnit.SECONDS);
+        } catch (final TimeoutException toe) {
+            saveRetryPushNotificationInQueue(tenantId, url, notification, attemptRetryNumber);
+            return false;
         } catch (final Exception e) {
             log.warn("Failed to push notification url='{}', tenantId='{}'", url, tenantId, e);
             return false;
         }
-        return response.getStatusCode() >= 200 && response.getStatusCode() < 300;
+
+        if (response.getStatusCode() >= 200 && response.getStatusCode() < 300) {
+            return true;
+        } else {
+            saveRetryPushNotificationInQueue(tenantId, url, notification, attemptRetryNumber);
+            return false;
+        }
+    }
+
+    public void resendPushNotification(final PushNotificationKey key) throws JsonProcessingException {
+
+        final NotificationJson notification = new NotificationJson(key.getEventType() != null ? key.getEventType().toString() : null,
+                                                                   key.getAccountId() != null ? key.getAccountId().toString() : null,
+                                                                   key.getObjectType() != null ? key.getObjectType().toString() : null,
+                                                                   key.getObjectId() != null ? key.getObjectId().toString() : null);
+        final String body = mapper.writeValueAsString(notification);
+        doPost(key.getTenantId(), key.getUrl(), body, notification, TIMEOUT_NOTIFICATION, key.getAttemptNumber());
+    }
+
+    private void saveRetryPushNotificationInQueue(final UUID tenantId, final String url, final NotificationJson notificationJson, final int attemptRetryNumber) {
+        final PushNotificationKey key = new PushNotificationKey(tenantId,
+                                                                notificationJson.getAccountId() != null ? UUID.fromString(notificationJson.getAccountId()) : null,
+                                                                notificationJson.getEventType(),
+                                                                notificationJson.getObjectType(),
+                                                                notificationJson.getObjectId() != null ? UUID.fromString(notificationJson.getObjectId()) : null,
+                                                                attemptRetryNumber + 1, url);
+
+        final TenantContext tenantContext = contextFactory.createTenantContext(tenantId);
+        final DateTime nextNotificationTime = getNextNotificationTime(key.getAttemptNumber(), internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(tenantContext));
+
+        if (nextNotificationTime == null) {
+            log.warn("Max attempt number reached for push notification url='{}', tenantId='{}'", key.getUrl(), key.getTenantId());
+            return;
+        }
+        log.debug("Push notification is scheduled to send at {} for url='{}', tenantId='{}'", nextNotificationTime, key.getUrl(), key.getTenantId());
+
+        final Long accountRecordId = internalCallContextFactory.getRecordIdFromObject(key.getAccountId(), ObjectType.ACCOUNT, tenantContext);
+        final Long tenantRecordId = internalCallContextFactory.getRecordIdFromObject(key.getTenantId(), ObjectType.TENANT, tenantContext);
+        try {
+            final NotificationQueue notificationQueue = notificationQueueService.getNotificationQueue(DefaultServerService.SERVER_SERVICE, PushNotificationRetryService.QUEUE_NAME);
+            notificationQueue.recordFutureNotification(nextNotificationTime, key, null, MoreObjects.firstNonNull(accountRecordId, new Long(0)), tenantRecordId);
+        } catch (NoSuchNotificationQueue noSuchNotificationQueue) {
+            log.error("Failed to push notification url='{}', tenantId='{}'", key.getUrl(), key.getTenantId(), noSuchNotificationQueue);
+        } catch (IOException e) {
+            log.error("Failed to push notification url='{}', tenantId='{}'", key.getUrl(), key.getTenantId(), e);
+        }
+    }
+
+    private DateTime getNextNotificationTime(final int attemptNumber, final InternalTenantContext tenantContext) {
+
+        final List<TimeSpan> retries = notificationConfig.getPushNotificationsRetries(tenantContext);
+        if (attemptNumber > retries.size()) {
+            return null;
+        }
+        final TimeSpan nextDelay = retries.get(attemptNumber - 1);
+        return clock.getUTCNow().plusMillis((int) nextDelay.getMillis());
     }
 
     private List<String> getCallbacksForTenant(final TenantContext context) throws TenantApiException {
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationRetryService.java b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationRetryService.java
new file mode 100644
index 0000000..5284a21
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/notifications/PushNotificationRetryService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.server.notifications;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.server.DefaultServerService;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.notificationq.api.NotificationEvent;
+import org.killbill.notificationq.api.NotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueAlreadyExists;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.inject.Inject;
+
+public class PushNotificationRetryService {
+
+    private static final Logger log = LoggerFactory.getLogger(PushNotificationRetryService.class);
+    public static final String QUEUE_NAME = "push-notification-queue";
+    private static final String retryService = DefaultServerService.SERVER_SERVICE + "-" + QUEUE_NAME;
+
+    private final NotificationQueueService notificationQueueService;
+    private final InternalCallContextFactory internalCallContextFactory;
+    private final PushNotificationListener pushNotificationListener;
+
+    private NotificationQueue retryQueue;
+
+    @Inject
+    public PushNotificationRetryService(final NotificationQueueService notificationQueueService,
+                                        final InternalCallContextFactory internalCallContextFactory,
+                                        final PushNotificationListener pushNotificationListener) {
+        this.notificationQueueService = notificationQueueService;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.pushNotificationListener = pushNotificationListener;
+    }
+
+    public void initialize() throws NotificationQueueAlreadyExists {
+        retryQueue = notificationQueueService.createNotificationQueue(DefaultServerService.SERVER_SERVICE,
+                                                                      QUEUE_NAME,
+                                                                      new NotificationQueueHandler() {
+                                                                          @Override
+                                                                          public void handleReadyNotification(final NotificationEvent notificationKey, final DateTime eventDateTime, final UUID userToken, final Long accountRecordId, final Long tenantRecordId) {
+                                                                              if (!(notificationKey instanceof PushNotificationKey)) {
+                                                                                  log.error("Push Notification service got an unexpected notification type {}", notificationKey.getClass().getName());
+                                                                                  return;
+                                                                              }
+                                                                              final PushNotificationKey key = (PushNotificationKey) notificationKey;
+                                                                              try {
+                                                                                  pushNotificationListener.resendPushNotification(key);
+                                                                              } catch (JsonProcessingException e) {
+                                                                                  log.error("Failed to push notification url='{}', tenantId='{}'", key.getUrl(), key.getTenantId(), e);
+                                                                              }
+                                                                          }
+                                                                      }
+                                                                     );
+    }
+
+    public void start() {
+        retryQueue.startQueue();
+    }
+
+    public void stop() throws NoSuchNotificationQueue {
+        if (retryQueue != null) {
+            retryQueue.stopQueue();
+            notificationQueueService.deleteNotificationQueue(retryQueue.getServiceName(), retryQueue.getQueueName());
+        }
+    }
+
+}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcTenantRealm.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcTenantRealm.java
index 20b2d7a..aa2e377 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcTenantRealm.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/KillbillJdbcTenantRealm.java
@@ -27,7 +27,7 @@ import org.apache.shiro.authc.SimpleAuthenticationInfo;
 import org.apache.shiro.codec.Base64;
 import org.apache.shiro.realm.jdbc.JdbcRealm;
 import org.apache.shiro.util.ByteSource;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.security.shiro.KillbillCredentialsMatcher;
 
 /**
diff --git a/profiles/killbill/src/main/resources/killbill-server.properties b/profiles/killbill/src/main/resources/killbill-server.properties
index 734d17b..3c2b298 100644
--- a/profiles/killbill/src/main/resources/killbill-server.properties
+++ b/profiles/killbill/src/main/resources/killbill-server.properties
@@ -16,6 +16,8 @@
 # under the License.
 #
 
+org.killbill.jruby.context.version=RUBY2_0
+
 #
 # KILLBILL GENERIC PROPERTIES
 #
@@ -80,7 +82,8 @@ org.killbill.server.test.mode=true
 # Set payment calls to timeout after 5 sec -- mostly for integration tests
 org.killbill.payment.plugin.timeout=5s
 
-org.killbill.payment.retry.days=
+org.killbill.payment.retry.days=1,1,1
+
 
 org.killbill.notificationq.analytics.tableName=analytics_notifications
 org.killbill.notificationq.analytics.historyTableName=analytics_notifications_history
diff --git a/profiles/killbill/src/main/resources/logback.xml b/profiles/killbill/src/main/resources/logback.xml
index f2cdef8..c2c710a 100644
--- a/profiles/killbill/src/main/resources/logback.xml
+++ b/profiles/killbill/src/main/resources/logback.xml
@@ -43,7 +43,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
@@ -64,7 +64,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
@@ -85,7 +85,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
@@ -106,7 +106,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
@@ -127,7 +127,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
@@ -148,7 +148,7 @@
                     </timeBasedFileNamingAndTriggeringPolicy>
                 </rollingPolicy>
                 <encoder>
-                    <pattern>%date [%thread] %maskedMsg%n</pattern>
+                    <pattern>%date{"yyyy-MM-dd'T'HH:mm:ss,SSSZ", UTC} lvl='%level', log='%logger{0}', th='%thread', xff='%X{req.xForwardedFor}', rId='%X{req.requestId}', aRId='%X{kb.accountRecordId}', tRId='%X{kb.tenantRecordId}', %maskedMsg%n</pattern>
                 </encoder>
             </appender>
         </sift>
diff --git a/profiles/killbill/src/main/webapp/api.html b/profiles/killbill/src/main/webapp/api.html
index 8cd886f..e4e7e52 100644
--- a/profiles/killbill/src/main/webapp/api.html
+++ b/profiles/killbill/src/main/webapp/api.html
@@ -1,7 +1,9 @@
 <!--
   ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2016 Groupon, Inc
+  ~ Copyright 2014-2016 The Billing Project, LLC
   ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
+  ~ The Billing Project 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:
   ~
@@ -17,90 +19,83 @@
 <!DOCTYPE html>
 <html>
 <head>
+  <meta charset="UTF-8">
   <title>Kill Bill APIs</title>
-  <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
-  <link href='//fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'/>
+  <link rel="icon" href="images/favicon.ico" />
+  <link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
-  <link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
+  <link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
   <link href='css/killbill-swagger.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/killbill-swagger.css' media='print' rel='stylesheet' type='text/css'/>
-  <script type="text/javascript" src="lib/shred.bundle.js"></script>
+
+  <script src='lib/object-assign-pollyfill.js' type='text/javascript'></script>
   <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
   <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
   <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
   <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
-  <script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>
-  <script src='lib/underscore-min.js' type='text/javascript'></script>
+  <script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
+  <script src='lib/js-yaml.min.js' type='text/javascript'></script>
+  <script src='lib/lodash.min.js' type='text/javascript'></script>
   <script src='lib/backbone-min.js' type='text/javascript'></script>
-  <script src='lib/swagger.js' type='text/javascript'></script>
-  <script src='lib/swagger-client.js' type='text/javascript'></script>
   <script src='lib/swagger-ui.min.js' type='text/javascript'></script>
-  <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
-
-  <!-- enabling this will enable oauth2 implicit scope support -->
+  <script src='lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
+  <script src='lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
+  <script src='lib/jsoneditor.min.js' type='text/javascript'></script>
+  <script src='lib/marked.js' type='text/javascript'></script>
   <script src='lib/swagger-oauth.js' type='text/javascript'></script>
+
   <script type="text/javascript">
     $(function () {
-      var url = window.location.href.replace(/(.*)(\/[^\/]*?)$/, '$1/api-docs')
+      var url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + window.location.pathname.replace(/(.*)(\/[^\/]*?)$/, '$1/swagger.json')
+
+      hljs.configure({
+        highlightSizeThreshold: 5000
+      });
+
+      // Pre load translate...
+      if(window.SwaggerTranslator) {
+        window.SwaggerTranslator.translate();
+      }
       window.swaggerUi = new SwaggerUi({
-        url: (url || "http://127.0.0.1:8080/api-docs"),
+        url: url,
         dom_id: "swagger-ui-container",
-        supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
+        supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
         onComplete: function(swaggerApi, swaggerUi){
-          log("Loaded SwaggerUI");
           if(typeof initOAuth == "function") {
-            /*
             initOAuth({
               clientId: "your-client-id",
+              clientSecret: "your-client-secret-if-required",
               realm: "your-realms",
-              appName: "your-app-name"
+              appName: "your-app-name",
+              scopeSeparator: ",",
+              additionalQueryStringParams: {}
             });
-            */
           }
-          $('pre code').each(function(i, e) {
-            hljs.highlightBlock(e)
-          });
+
+          if(window.SwaggerTranslator) {
+            window.SwaggerTranslator.translate();
+          }
+
+          $("input.parameter[name|='X-Killbill-CreatedBy']").val('Swagger');
         },
         onFailure: function(data) {
-          log("Unable to Load SwaggerUI");
+          log("Unable to Load SwaggerUI: " + data);
         },
         docExpansion: "none",
-        sorter : "alpha"
-      });
-
-      function addApiKeyAuthorization() {
-        var key = $('#input_apiKey')[0].value;
-        log("key: " + key);
-        if(key && key.trim() != "") {
-            log("added key " + key);
-            window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
-        }
-      }
-
-      $('#input_apiKey').change(function() {
-        addApiKeyAuthorization();
-      });
-
-      // if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
-      /*
-        var apiKey = "myApiKeyXXXX123456789";
-        $('#input_apiKey').val(apiKey);
-        addApiKeyAuthorization();
-      */
-
-      $.each(["#input_kb_apiKey", "#input_kb_apiSecret", "#input_kb_username", "#input_kb_password"], function(idx, selector_id) {
-        $(selector_id).change(function() {
-          setHeaders();
-        });
+        apisSorter: "alpha",
+        operationsSorter: "alpha",
+        jsonEditor: false,
+        defaultModelRendering: 'schema',
+        showRequestHeaders: true
       });
 
       function setHeader(header, value) {
         if (value && value.trim() != "") {
           log("Setting header " + header + " to " + value);
-          window.authorizations.remove(header);
-          window.authorizations.add(header, new ApiKeyAuthorization(header, value, "header"));
+          window.swaggerUi.api.clientAuthorizations.remove(header);
+          window.swaggerUi.api.clientAuthorizations.add(header, new SwaggerClient.ApiKeyAuthorization(header, value, "header"));
         }
       }
 
@@ -110,9 +105,22 @@
         setHeader("Authorization", "Basic " + btoa(($("#input_kb_username")[0].value || "admin") + ":" + ($("#input_kb_password")[0].value || "password")));
       }
 
-      setHeaders();
+      $.each(["#input_kb_apiKey", "#input_kb_apiSecret", "#input_kb_username", "#input_kb_password"], function(idx, selector_id) {
+        $(selector_id).change(function() {
+          setHeaders();
+        });
+      });
+
 
       window.swaggerUi.load();
+
+      setHeaders();
+
+      function log() {
+        if ('console' in window) {
+          console.log.apply(console, arguments);
+        }
+      }
   });
   </script>
 </head>
@@ -126,17 +134,17 @@
       </a>
     </div>
     <form id='api_selector'>
-      <div class='input'><input placeholder="http://127.0.0.1:8080/api-docs" id="input_baseUrl" name="baseUrl" type="text" size="38"/></div>
+      <div class='input'><input placeholder="http://127.0.0.1:8080/swagger.json" id="input_baseUrl" name="baseUrl" type="text" size="38"/></div>
       <div class='input'><input placeholder="api_key" id="input_kb_apiKey" name="apiKey" type="text" size="8"/></div>
       <div class='input'><input placeholder="api_secret" id="input_kb_apiSecret" name="apiSecret" type="text" size="8"/></div>
       <div class='input'><input placeholder="username" id="input_kb_username" name="username" type="text" size="8"/></div>
       <div class='input'><input placeholder="password" id="input_kb_password" name="password" type="text" size="8"/></div>
-      <div class='input'><a id="explore" href="#">Explore</a></div>
+      <div class='input'><a id="explore" class="header__btn" href="#" data-sw-translate>Explore</a></div>
     </form>
   </div>
 </div>
 
-<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
+<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
 <div id="swagger-ui-container" class="swagger-ui-wrap"></div>
 </body>
 </html>
diff --git a/profiles/killbill/src/main/webapp/css/print.css b/profiles/killbill/src/main/webapp/css/print.css
new file mode 100644
index 0000000..d4c1e70
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/css/print.css
@@ -0,0 +1,1362 @@
+/* Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org> */
+.swagger-section pre code {
+  display: block;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section pre code,
+.swagger-section pre .subst,
+.swagger-section pre .tag .title,
+.swagger-section pre .lisp .title,
+.swagger-section pre .clojure .built_in,
+.swagger-section pre .nginx .title {
+  color: black;
+}
+.swagger-section pre .string,
+.swagger-section pre .title,
+.swagger-section pre .constant,
+.swagger-section pre .parent,
+.swagger-section pre .tag .value,
+.swagger-section pre .rules .value,
+.swagger-section pre .rules .value .number,
+.swagger-section pre .preprocessor,
+.swagger-section pre .ruby .symbol,
+.swagger-section pre .ruby .symbol .string,
+.swagger-section pre .aggregate,
+.swagger-section pre .template_tag,
+.swagger-section pre .django .variable,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .addition,
+.swagger-section pre .flow,
+.swagger-section pre .stream,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .apache .cbracket,
+.swagger-section pre .tex .command,
+.swagger-section pre .tex .special,
+.swagger-section pre .erlang_repl .function_or_atom,
+.swagger-section pre .markdown .header {
+  color: #800;
+}
+.swagger-section pre .comment,
+.swagger-section pre .annotation,
+.swagger-section pre .template_comment,
+.swagger-section pre .diff .header,
+.swagger-section pre .chunk,
+.swagger-section pre .markdown .blockquote {
+  color: #888;
+}
+.swagger-section pre .number,
+.swagger-section pre .date,
+.swagger-section pre .regexp,
+.swagger-section pre .literal,
+.swagger-section pre .smalltalk .symbol,
+.swagger-section pre .smalltalk .char,
+.swagger-section pre .go .constant,
+.swagger-section pre .change,
+.swagger-section pre .markdown .bullet,
+.swagger-section pre .markdown .link_url {
+  color: #080;
+}
+.swagger-section pre .label,
+.swagger-section pre .javadoc,
+.swagger-section pre .ruby .string,
+.swagger-section pre .decorator,
+.swagger-section pre .filter .argument,
+.swagger-section pre .localvars,
+.swagger-section pre .array,
+.swagger-section pre .attr_selector,
+.swagger-section pre .important,
+.swagger-section pre .pseudo,
+.swagger-section pre .pi,
+.swagger-section pre .doctype,
+.swagger-section pre .deletion,
+.swagger-section pre .envvar,
+.swagger-section pre .shebang,
+.swagger-section pre .apache .sqbracket,
+.swagger-section pre .nginx .built_in,
+.swagger-section pre .tex .formula,
+.swagger-section pre .erlang_repl .reserved,
+.swagger-section pre .prompt,
+.swagger-section pre .markdown .link_label,
+.swagger-section pre .vhdl .attribute,
+.swagger-section pre .clojure .attribute,
+.swagger-section pre .coffeescript .property {
+  color: #88F;
+}
+.swagger-section pre .keyword,
+.swagger-section pre .id,
+.swagger-section pre .phpdoc,
+.swagger-section pre .title,
+.swagger-section pre .built_in,
+.swagger-section pre .aggregate,
+.swagger-section pre .css .tag,
+.swagger-section pre .javadoctag,
+.swagger-section pre .phpdoc,
+.swagger-section pre .yardoctag,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .winutils,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .go .typename,
+.swagger-section pre .tex .command,
+.swagger-section pre .markdown .strong,
+.swagger-section pre .request,
+.swagger-section pre .status {
+  font-weight: bold;
+}
+.swagger-section pre .markdown .emphasis {
+  font-style: italic;
+}
+.swagger-section pre .nginx .built_in {
+  font-weight: normal;
+}
+.swagger-section pre .coffeescript .javascript,
+.swagger-section pre .javascript .xml,
+.swagger-section pre .tex .formula,
+.swagger-section pre .xml .javascript,
+.swagger-section pre .xml .vbscript,
+.swagger-section pre .xml .css,
+.swagger-section pre .xml .cdata {
+  opacity: 0.5;
+}
+.swagger-section .hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section .hljs,
+.swagger-section .hljs-subst {
+  color: #444;
+}
+.swagger-section .hljs-keyword,
+.swagger-section .hljs-attribute,
+.swagger-section .hljs-selector-tag,
+.swagger-section .hljs-meta-keyword,
+.swagger-section .hljs-doctag,
+.swagger-section .hljs-name {
+  font-weight: bold;
+}
+.swagger-section .hljs-built_in,
+.swagger-section .hljs-literal,
+.swagger-section .hljs-bullet,
+.swagger-section .hljs-code,
+.swagger-section .hljs-addition {
+  color: #1F811F;
+}
+.swagger-section .hljs-regexp,
+.swagger-section .hljs-symbol,
+.swagger-section .hljs-variable,
+.swagger-section .hljs-template-variable,
+.swagger-section .hljs-link,
+.swagger-section .hljs-selector-attr,
+.swagger-section .hljs-selector-pseudo {
+  color: #BC6060;
+}
+.swagger-section .hljs-type,
+.swagger-section .hljs-string,
+.swagger-section .hljs-number,
+.swagger-section .hljs-selector-id,
+.swagger-section .hljs-selector-class,
+.swagger-section .hljs-quote,
+.swagger-section .hljs-template-tag,
+.swagger-section .hljs-deletion {
+  color: #880000;
+}
+.swagger-section .hljs-title,
+.swagger-section .hljs-section {
+  color: #880000;
+  font-weight: bold;
+}
+.swagger-section .hljs-comment {
+  color: #888888;
+}
+.swagger-section .hljs-meta {
+  color: #2B6EA1;
+}
+.swagger-section .hljs-emphasis {
+  font-style: italic;
+}
+.swagger-section .hljs-strong {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap {
+  line-height: 1;
+  font-family: "Droid Sans", sans-serif;
+  min-width: 760px;
+  max-width: 960px;
+  margin-left: auto;
+  margin-right: auto;
+  /* JSONEditor specific styling */
+}
+.swagger-section .swagger-ui-wrap b,
+.swagger-section .swagger-ui-wrap strong {
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap q,
+.swagger-section .swagger-ui-wrap blockquote {
+  quotes: none;
+}
+.swagger-section .swagger-ui-wrap p {
+  line-height: 1.4em;
+  padding: 0 0 10px;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap q:before,
+.swagger-section .swagger-ui-wrap q:after,
+.swagger-section .swagger-ui-wrap blockquote:before,
+.swagger-section .swagger-ui-wrap blockquote:after {
+  content: none;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu h1,
+.swagger-section .swagger-ui-wrap .heading_with_menu h2,
+.swagger-section .swagger-ui-wrap .heading_with_menu h3,
+.swagger-section .swagger-ui-wrap .heading_with_menu h4,
+.swagger-section .swagger-ui-wrap .heading_with_menu h5,
+.swagger-section .swagger-ui-wrap .heading_with_menu h6 {
+  display: block;
+  clear: none;
+  float: left;
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  -ms-box-sizing: border-box;
+  box-sizing: border-box;
+  width: 60%;
+}
+.swagger-section .swagger-ui-wrap table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+.swagger-section .swagger-ui-wrap table thead tr th {
+  padding: 5px;
+  font-size: 0.9em;
+  color: #666666;
+  border-bottom: 1px solid #999999;
+}
+.swagger-section .swagger-ui-wrap table tbody tr:last-child td {
+  border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap table tbody tr.offset {
+  background-color: #f0f0f0;
+}
+.swagger-section .swagger-ui-wrap table tbody tr td {
+  padding: 6px;
+  font-size: 0.9em;
+  border-bottom: 1px solid #cccccc;
+  vertical-align: top;
+  line-height: 1.3em;
+}
+.swagger-section .swagger-ui-wrap ol {
+  margin: 0px 0 10px;
+  padding: 0 0 0 18px;
+  list-style-type: decimal;
+}
+.swagger-section .swagger-ui-wrap ol li {
+  padding: 5px 0px;
+  font-size: 0.9em;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap ol,
+.swagger-section .swagger-ui-wrap ul {
+  list-style: none;
+}
+.swagger-section .swagger-ui-wrap h1 a,
+.swagger-section .swagger-ui-wrap h2 a,
+.swagger-section .swagger-ui-wrap h3 a,
+.swagger-section .swagger-ui-wrap h4 a,
+.swagger-section .swagger-ui-wrap h5 a,
+.swagger-section .swagger-ui-wrap h6 a {
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap h1 a:hover,
+.swagger-section .swagger-ui-wrap h2 a:hover,
+.swagger-section .swagger-ui-wrap h3 a:hover,
+.swagger-section .swagger-ui-wrap h4 a:hover,
+.swagger-section .swagger-ui-wrap h5 a:hover,
+.swagger-section .swagger-ui-wrap h6 a:hover {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap h1 span.divider,
+.swagger-section .swagger-ui-wrap h2 span.divider,
+.swagger-section .swagger-ui-wrap h3 span.divider,
+.swagger-section .swagger-ui-wrap h4 span.divider,
+.swagger-section .swagger-ui-wrap h5 span.divider,
+.swagger-section .swagger-ui-wrap h6 span.divider {
+  color: #aaaaaa;
+}
+.swagger-section .swagger-ui-wrap a {
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap a img {
+  border: none;
+}
+.swagger-section .swagger-ui-wrap article,
+.swagger-section .swagger-ui-wrap aside,
+.swagger-section .swagger-ui-wrap details,
+.swagger-section .swagger-ui-wrap figcaption,
+.swagger-section .swagger-ui-wrap figure,
+.swagger-section .swagger-ui-wrap footer,
+.swagger-section .swagger-ui-wrap header,
+.swagger-section .swagger-ui-wrap hgroup,
+.swagger-section .swagger-ui-wrap menu,
+.swagger-section .swagger-ui-wrap nav,
+.swagger-section .swagger-ui-wrap section,
+.swagger-section .swagger-ui-wrap summary {
+  display: block;
+}
+.swagger-section .swagger-ui-wrap pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap pre code {
+  line-height: 1.6em;
+  background: none;
+}
+.swagger-section .swagger-ui-wrap .content > .content-type > div > label {
+  clear: both;
+  display: block;
+  color: #0F6AB4;
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap .content pre {
+  font-size: 12px;
+  margin-top: 5px;
+  padding: 5px;
+}
+.swagger-section .swagger-ui-wrap .icon-btn {
+  cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .info_title {
+  padding-bottom: 10px;
+  font-weight: bold;
+  font-size: 25px;
+}
+.swagger-section .swagger-ui-wrap .footer {
+  margin-top: 20px;
+}
+.swagger-section .swagger-ui-wrap p.big,
+.swagger-section .swagger-ui-wrap div.big p {
+  font-size: 1em;
+  margin-bottom: 10px;
+}
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input {
+  width: 500px !important;
+}
+.swagger-section .swagger-ui-wrap .info_license {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_tos {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .message-fail {
+  color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap .info_url {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_description {
+  padding-bottom: 10px;
+  font-size: 15px;
+}
+.swagger-section .swagger-ui-wrap .markdown ol li,
+.swagger-section .swagger-ui-wrap .markdown ul li {
+  padding: 3px 0px;
+  line-height: 1.4em;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input {
+  display: block;
+  padding: 4px;
+  width: auto;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title {
+  font-size: 1.3em;
+}
+.swagger-section .swagger-ui-wrap table.fullwidth {
+  width: 100%;
+}
+.swagger-section .swagger-ui-wrap .model-signature {
+  font-family: "Droid Sans", sans-serif;
+  font-size: 1em;
+  line-height: 1.5em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a {
+  text-decoration: none;
+  color: #AAA;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover {
+  text-decoration: underline;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected {
+  color: black;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propType {
+  color: #5555aa;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre:hover {
+  background-color: #ffffdd;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+  font-size: .85em;
+  line-height: 1.2em;
+  overflow: auto;
+  max-height: 200px;
+  cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
+  display: block;
+  min-width: 230px;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li {
+  float: left;
+  margin: 0 5px 5px 0;
+  padding: 2px 5px 2px 0;
+  border-right: 1px solid #ddd;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOpt {
+  color: #555;
+}
+.swagger-section .swagger-ui-wrap .model-signature .snippet small {
+  font-size: 0.75em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOptKey {
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .strong {
+  font-weight: bold;
+  color: #000;
+  font-size: .9em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description div {
+  font-size: 0.9em;
+  line-height: 1.5em;
+  margin-left: 1em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .stronger {
+  font-weight: bold;
+  color: #000;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+  border-spacing: 0;
+  position: absolute;
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+  display: none;
+  font-size: 11px;
+  max-width: 400px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+  margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+  text-align: center;
+  background-color: #eeeeee;
+  border: 1px solid #bbbbbb;
+  font-size: 11px;
+  color: #666666;
+  font-weight: bold;
+  padding: 5px;
+  line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child,
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child {
+  display: inline;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before {
+  display: block;
+  content: '';
+}
+.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child {
+  margin-right: -3px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-container {
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+  width: 300px;
+  height: 100px;
+  border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap .markdown p code,
+.swagger-section .swagger-ui-wrap .markdown li code {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #f0f0f0;
+  color: black;
+  padding: 1px 3px;
+}
+.swagger-section .swagger-ui-wrap .required {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .editor_holder {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap .editor_holder label {
+  font-weight: normal!important;
+  /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */
+}
+.swagger-section .swagger-ui-wrap .editor_holder label.required {
+  font-weight: bold!important;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+  width: 300px;
+  border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap h1 {
+  color: black;
+  font-size: 1.5em;
+  line-height: 1.3em;
+  padding: 10px 0 10px 0;
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu ul {
+  display: block;
+  clear: none;
+  float: right;
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  -ms-box-sizing: border-box;
+  box-sizing: border-box;
+  margin-top: 10px;
+}
+.swagger-section .swagger-ui-wrap h2 {
+  color: black;
+  font-size: 1.3em;
+  padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap h2 a {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub {
+  font-size: 0.7em;
+  color: #999999;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub a {
+  color: #777777;
+}
+.swagger-section .swagger-ui-wrap span.weak {
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap .message-success {
+  color: #89BF04;
+}
+.swagger-section .swagger-ui-wrap caption,
+.swagger-section .swagger-ui-wrap th,
+.swagger-section .swagger-ui-wrap td {
+  text-align: left;
+  font-weight: normal;
+  vertical-align: middle;
+}
+.swagger-section .swagger-ui-wrap .code {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea {
+  font-family: "Droid Sans", sans-serif;
+  height: 250px;
+  padding: 4px;
+  display: block;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select {
+  display: block;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label {
+  display: block;
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input {
+  display: block;
+  float: left;
+  clear: none;
+  margin: 0 5px 0 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label {
+  display: block;
+  clear: both;
+  width: auto;
+  padding: 0 0 3px;
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr {
+  padding-left: 3px;
+  color: #888888;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints {
+  margin-left: 0;
+  font-style: italic;
+  font-size: 0.9em;
+  margin: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons {
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap span.blank,
+.swagger-section .swagger-ui-wrap span.empty {
+  color: #888888;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .markdown h3 {
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap .markdown h4 {
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap .markdown pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  padding: 10px;
+  margin: 0 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown pre code {
+  line-height: 1.6em;
+  overflow: auto;
+}
+.swagger-section .swagger-ui-wrap div.gist {
+  margin: 20px 0 25px 0 !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources {
+  font-family: "Droid Sans", sans-serif;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource {
+  border-bottom: 1px solid #dddddd;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a {
+  color: #555555;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child {
+  border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading {
+  border: 1px solid transparent;
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+  overflow: hidden;
+  padding: 0;
+  display: block;
+  clear: none;
+  float: right;
+  margin: 14px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li {
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 2px 10px;
+  border-right: 1px solid #dddddd;
+  color: #666666;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a {
+  color: #aaaaaa;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover {
+  text-decoration: underline;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+  color: #999999;
+  padding-left: 0;
+  display: block;
+  clear: none;
+  float: left;
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+  color: #999999;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+  margin: 0 0 10px;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 {
+  display: block;
+  clear: none;
+  float: left;
+  width: auto;
+  margin: 0;
+  padding: 0;
+  line-height: 1.1em;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path {
+  padding-left: 10px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a {
+  color: black;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated {
+  text-decoration: line-through;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  text-decoration: none;
+  color: white;
+  display: inline-block;
+  width: 50px;
+  font-size: 0.7em;
+  text-align: center;
+  padding: 7px 0 4px;
+  -moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -o-border-radius: 2px;
+  -ms-border-radius: 2px;
+  -khtml-border-radius: 2px;
+  border-radius: 2px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span {
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options {
+  overflow: hidden;
+  padding: 0;
+  display: block;
+  clear: none;
+  float: right;
+  margin: 6px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li {
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 2px 10px;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+  border-top: none;
+  padding: 10px;
+  -moz-border-radius-bottomleft: 6px;
+  -webkit-border-bottom-left-radius: 6px;
+  -o-border-bottom-left-radius: 6px;
+  -ms-border-bottom-left-radius: 6px;
+  -khtml-border-bottom-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+  -moz-border-radius-bottomright: 6px;
+  -webkit-border-bottom-right-radius: 6px;
+  -o-border-bottom-right-radius: 6px;
+  -ms-border-bottom-right-radius: 6px;
+  -khtml-border-bottom-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+  margin: 0 0 20px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 {
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a {
+  padding: 4px 0 0 10px;
+  display: inline-block;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit {
+  display: block;
+  clear: none;
+  float: left;
+  padding: 6px 8px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
+  background-image: url('../images/throbber.gif');
+  width: 128px;
+  height: 16px;
+  display: block;
+  clear: none;
+  float: right;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error {
+  outline: 2px solid black;
+  outline-color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] {
+  max-width: 300px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  padding: 10px;
+  font-size: 0.9em;
+  max-height: 400px;
+  overflow-y: auto;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading {
+  background-color: #f9f2e9;
+  border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a {
+  background-color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #f0e0ca;
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a {
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content {
+  background-color: #faf5ee;
+  border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 {
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a {
+  color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading {
+  background-color: #fcffcd;
+  border: 1px solid black;
+  border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  background-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #ffd20f;
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a {
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content {
+  background-color: #fcffcd;
+  border: 1px solid black;
+  border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 {
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a {
+  color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading {
+  background-color: #f5e8e8;
+  border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  background-color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #e8c6c7;
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a {
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+  background-color: #f7eded;
+  border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 {
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a {
+  color: #c8787a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading {
+  background-color: #e7f6ec;
+  border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a {
+  background-color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3e8d1;
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a {
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content {
+  background-color: #ebf7f0;
+  border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 {
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a {
+  color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading {
+  background-color: #FCE9E3;
+  border: 1px solid #F5D5C3;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a {
+  background-color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #f0cecb;
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a {
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content {
+  background-color: #faf0ef;
+  border: 1px solid #f0cecb;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 {
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a {
+  color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading {
+  background-color: #e7f0f7;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a {
+  background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3d9ec;
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a {
+  color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading {
+  background-color: #e7f0f7;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a {
+  background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3d9ec;
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a {
+  color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+  border-top: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap p#colophon {
+  margin: 0 15px 40px 15px;
+  padding: 10px 0;
+  font-size: 0.8em;
+  border-top: 1px solid #dddddd;
+  font-family: "Droid Sans", sans-serif;
+  color: #999999;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap p#colophon a {
+  text-decoration: none;
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap h3 {
+  color: black;
+  font-size: 1.1em;
+  padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown ol,
+.swagger-section .swagger-ui-wrap .markdown ul {
+  font-family: "Droid Sans", sans-serif;
+  margin: 5px 0 10px;
+  padding: 0 0 0 18px;
+  list-style-type: disc;
+}
+.swagger-section .swagger-ui-wrap form.form_box {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box label {
+  color: #0f6ab4 !important;
+}
+.swagger-section .swagger-ui-wrap form.form_box input[type=submit] {
+  display: block;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box p.weak {
+  font-size: 0.8em;
+}
+.swagger-section .swagger-ui-wrap form.form_box p {
+  font-size: 0.9em;
+  padding: 0 0 15px;
+  color: #7e7b6d;
+}
+.swagger-section .swagger-ui-wrap form.form_box p a {
+  color: #646257;
+}
+.swagger-section .swagger-ui-wrap form.form_box p strong {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child {
+  padding-bottom: 0;
+}
+.swagger-section .title {
+  font-style: bold;
+}
+.swagger-section .secondary_form {
+  display: none;
+}
+.swagger-section .main_image {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+.swagger-section .oauth_body {
+  margin-left: 100px;
+  margin-right: 100px;
+}
+.swagger-section .oauth_submit {
+  text-align: center;
+  display: inline-block;
+}
+.swagger-section .authorize-wrapper {
+  margin: 15px 0 10px;
+}
+.swagger-section .authorize-wrapper_operation {
+  float: right;
+}
+.swagger-section .authorize__btn:hover {
+  text-decoration: underline;
+  cursor: pointer;
+}
+.swagger-section .authorize__btn_operation:hover .authorize-scopes {
+  display: block;
+}
+.swagger-section .authorize-scopes {
+  position: absolute;
+  margin-top: 20px;
+  background: #FFF;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  display: none;
+  font-size: 13px;
+  max-width: 300px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+}
+.swagger-section .authorize-scopes .authorize__scope {
+  text-decoration: none;
+}
+.swagger-section .authorize__btn_operation {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .authorize__btn_operation_login {
+  background-position: 0 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section .authorize__btn_operation_logout {
+  background-position: -30px 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section #auth_container {
+  color: #fff;
+  display: inline-block;
+  border: none;
+  padding: 5px;
+  width: 87px;
+  height: 13px;
+}
+.swagger-section #auth_container .authorize__btn {
+  color: #fff;
+}
+.swagger-section .auth_container {
+  padding: 0 0 10px;
+  margin-bottom: 5px;
+  border-bottom: solid 1px #CCC;
+  font-size: 0.9em;
+}
+.swagger-section .auth_container .auth__title {
+  color: #547f00;
+  font-size: 1.2em;
+}
+.swagger-section .auth_container .basic_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .auth_container .auth__description {
+  color: #999999;
+  margin-bottom: 5px;
+}
+.swagger-section .auth_container .auth__button {
+  margin-top: 10px;
+  height: 30px;
+}
+.swagger-section .auth_container .key_auth__field {
+  margin: 5px 0;
+}
+.swagger-section .auth_container .key_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .api-popup-dialog {
+  position: absolute;
+  display: none;
+}
+.swagger-section .api-popup-dialog-wrapper {
+  z-index: 1000;
+  width: 500px;
+  background: #FFF;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  font-size: 13px;
+  color: #777;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+.swagger-section .api-popup-dialog-shadow {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  opacity: 0.2;
+  background-color: gray;
+  z-index: 900;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+  font-size: 24px;
+  padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+  font-size: 24px;
+  padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .error-msg {
+  padding-left: 5px;
+  padding-bottom: 5px;
+}
+.swagger-section .api-popup-dialog .api-popup-content {
+  max-height: 500px;
+  overflow-y: auto;
+}
+.swagger-section .api-popup-dialog .api-popup-authbtn {
+  height: 30px;
+}
+.swagger-section .api-popup-dialog .api-popup-cancel {
+  height: 30px;
+}
+.swagger-section .api-popup-scopes {
+  padding: 10px 20px;
+}
+.swagger-section .api-popup-scopes li {
+  padding: 5px 0;
+  line-height: 20px;
+}
+.swagger-section .api-popup-scopes li input {
+  position: relative;
+  top: 2px;
+}
+.swagger-section .api-popup-scopes .api-scope-desc {
+  padding-left: 20px;
+  font-style: italic;
+}
+.swagger-section .api-popup-actions {
+  padding-top: 10px;
+}
+#header {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+  max-height: none;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+  width: 100px;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+  width: 100px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints {
+  display: block !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+  display: block !important;
+}
diff --git a/profiles/killbill/src/main/webapp/css/screen.css b/profiles/killbill/src/main/webapp/css/screen.css
index 478b998..9d680e2 100644
--- a/profiles/killbill/src/main/webapp/css/screen.css
+++ b/profiles/killbill/src/main/webapp/css/screen.css
@@ -82,7 +82,7 @@
 .swagger-section pre .vhdl .attribute,
 .swagger-section pre .clojure .attribute,
 .swagger-section pre .coffeescript .property {
-  color: #8888ff;
+  color: #88F;
 }
 .swagger-section pre .keyword,
 .swagger-section pre .id,
@@ -120,12 +120,75 @@
 .swagger-section pre .xml .cdata {
   opacity: 0.5;
 }
+.swagger-section .hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section .hljs,
+.swagger-section .hljs-subst {
+  color: #444;
+}
+.swagger-section .hljs-keyword,
+.swagger-section .hljs-attribute,
+.swagger-section .hljs-selector-tag,
+.swagger-section .hljs-meta-keyword,
+.swagger-section .hljs-doctag,
+.swagger-section .hljs-name {
+  font-weight: bold;
+}
+.swagger-section .hljs-built_in,
+.swagger-section .hljs-literal,
+.swagger-section .hljs-bullet,
+.swagger-section .hljs-code,
+.swagger-section .hljs-addition {
+  color: #1F811F;
+}
+.swagger-section .hljs-regexp,
+.swagger-section .hljs-symbol,
+.swagger-section .hljs-variable,
+.swagger-section .hljs-template-variable,
+.swagger-section .hljs-link,
+.swagger-section .hljs-selector-attr,
+.swagger-section .hljs-selector-pseudo {
+  color: #BC6060;
+}
+.swagger-section .hljs-type,
+.swagger-section .hljs-string,
+.swagger-section .hljs-number,
+.swagger-section .hljs-selector-id,
+.swagger-section .hljs-selector-class,
+.swagger-section .hljs-quote,
+.swagger-section .hljs-template-tag,
+.swagger-section .hljs-deletion {
+  color: #880000;
+}
+.swagger-section .hljs-title,
+.swagger-section .hljs-section {
+  color: #880000;
+  font-weight: bold;
+}
+.swagger-section .hljs-comment {
+  color: #888888;
+}
+.swagger-section .hljs-meta {
+  color: #2B6EA1;
+}
+.swagger-section .hljs-emphasis {
+  font-style: italic;
+}
+.swagger-section .hljs-strong {
+  font-weight: bold;
+}
 .swagger-section .swagger-ui-wrap {
   line-height: 1;
   font-family: "Droid Sans", sans-serif;
+  min-width: 760px;
   max-width: 960px;
   margin-left: auto;
   margin-right: auto;
+  /* JSONEditor specific styling */
 }
 .swagger-section .swagger-ui-wrap b,
 .swagger-section .swagger-ui-wrap strong {
@@ -274,6 +337,9 @@
   font-weight: bold;
   font-size: 25px;
 }
+.swagger-section .swagger-ui-wrap .footer {
+  margin-top: 20px;
+}
 .swagger-section .swagger-ui-wrap p.big,
 .swagger-section .swagger-ui-wrap div.big p {
   font-size: 1em;
@@ -294,7 +360,13 @@
 .swagger-section .swagger-ui-wrap .message-fail {
   color: #cc0000;
 }
-.swagger-section .swagger-ui-wrap .info_contact {
+.swagger-section .swagger-ui-wrap .info_url {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
   padding-bottom: 5px;
 }
 .swagger-section .swagger-ui-wrap .info_description {
@@ -355,6 +427,7 @@
 }
 .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
   display: block;
+  min-width: 230px;
   margin: 0;
   padding: 0;
 }
@@ -391,6 +464,43 @@
   font-weight: bold;
   color: #000;
 }
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+  border-spacing: 0;
+  position: absolute;
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+  display: none;
+  font-size: 11px;
+  max-width: 400px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+  margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+  text-align: center;
+  background-color: #eeeeee;
+  border: 1px solid #bbbbbb;
+  font-size: 11px;
+  color: #666666;
+  font-weight: bold;
+  padding: 5px;
+  line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child,
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child {
+  display: inline;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before {
+  display: block;
+  content: '';
+}
+.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child {
+  margin-right: -3px;
+}
 .swagger-section .swagger-ui-wrap .model-signature .propName {
   font-weight: bold;
 }
@@ -412,6 +522,17 @@
 .swagger-section .swagger-ui-wrap .required {
   font-weight: bold;
 }
+.swagger-section .swagger-ui-wrap .editor_holder {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap .editor_holder label {
+  font-weight: normal!important;
+  /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */
+}
+.swagger-section .swagger-ui-wrap .editor_holder label.required {
+  font-weight: bold!important;
+}
 .swagger-section .swagger-ui-wrap input.parameter {
   width: 300px;
   border: 1px solid #aaa;
@@ -546,6 +667,7 @@
 }
 .swagger-section .swagger-ui-wrap .markdown pre code {
   line-height: 1.6em;
+  overflow: auto;
 }
 .swagger-section .swagger-ui-wrap div.gist {
   margin: 20px 0 25px 0 !important;
@@ -666,6 +788,9 @@
   color: black;
   text-decoration: none;
 }
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated {
+  text-decoration: line-through;
+}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
   text-decoration: underline;
 }
@@ -761,6 +886,9 @@
   outline: 2px solid black;
   outline-color: #cc0000;
 }
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] {
+  max-width: 300px;
+}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
   font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
   padding: 10px;
@@ -1034,6 +1162,9 @@
 .swagger-section .swagger-ui-wrap form.form_box p strong {
   color: black;
 }
+.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child {
+  padding-bottom: 0;
+}
 .swagger-section .title {
   font-style: bold;
 }
@@ -1051,18 +1182,122 @@
 }
 .swagger-section .oauth_submit {
   text-align: center;
+  display: inline-block;
+}
+.swagger-section .authorize-wrapper {
+  margin: 15px 0 10px;
+}
+.swagger-section .authorize-wrapper_operation {
+  float: right;
+}
+.swagger-section .authorize__btn:hover {
+  text-decoration: underline;
+  cursor: pointer;
+}
+.swagger-section .authorize__btn_operation:hover .authorize-scopes {
+  display: block;
+}
+.swagger-section .authorize-scopes {
+  position: absolute;
+  margin-top: 20px;
+  background: #FFF;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  display: none;
+  font-size: 13px;
+  max-width: 300px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+}
+.swagger-section .authorize-scopes .authorize__scope {
+  text-decoration: none;
+}
+.swagger-section .authorize__btn_operation {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .authorize__btn_operation_login {
+  background-position: 0 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section .authorize__btn_operation_logout {
+  background-position: -30px 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section #auth_container {
+  color: #fff;
+  display: inline-block;
+  border: none;
+  padding: 5px;
+  width: 87px;
+  height: 13px;
+}
+.swagger-section #auth_container .authorize__btn {
+  color: #fff;
+}
+.swagger-section .auth_container {
+  padding: 0 0 10px;
+  margin-bottom: 5px;
+  border-bottom: solid 1px #CCC;
+  font-size: 0.9em;
+}
+.swagger-section .auth_container .auth__title {
+  color: #547f00;
+  font-size: 1.2em;
+}
+.swagger-section .auth_container .basic_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .auth_container .auth__description {
+  color: #999999;
+  margin-bottom: 5px;
+}
+.swagger-section .auth_container .auth__button {
+  margin-top: 10px;
+  height: 30px;
+}
+.swagger-section .auth_container .key_auth__field {
+  margin: 5px 0;
+}
+.swagger-section .auth_container .key_auth__label {
+  display: inline-block;
+  width: 60px;
 }
 .swagger-section .api-popup-dialog {
-  z-index: 10000;
   position: absolute;
+  display: none;
+}
+.swagger-section .api-popup-dialog-wrapper {
+  z-index: 1000;
   width: 500px;
   background: #FFF;
   padding: 20px;
   border: 1px solid #ccc;
   border-radius: 5px;
-  display: none;
   font-size: 13px;
   color: #777;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+.swagger-section .api-popup-dialog-shadow {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  opacity: 0.2;
+  background-color: gray;
+  z-index: 900;
 }
 .swagger-section .api-popup-dialog .api-popup-title {
   font-size: 24px;
@@ -1072,14 +1307,18 @@
   font-size: 24px;
   padding: 10px 0;
 }
-.swagger-section .api-popup-dialog p.error-msg {
+.swagger-section .api-popup-dialog .error-msg {
   padding-left: 5px;
   padding-bottom: 5px;
 }
-.swagger-section .api-popup-dialog button.api-popup-authbtn {
+.swagger-section .api-popup-dialog .api-popup-content {
+  max-height: 500px;
+  overflow-y: auto;
+}
+.swagger-section .api-popup-dialog .api-popup-authbtn {
   height: 30px;
 }
-.swagger-section .api-popup-dialog button.api-popup-cancel {
+.swagger-section .api-popup-dialog .api-popup-cancel {
   height: 30px;
 }
 .swagger-section .api-popup-scopes {
@@ -1089,14 +1328,14 @@
   padding: 5px 0;
   line-height: 20px;
 }
-.swagger-section .api-popup-scopes .api-scope-desc {
-  padding-left: 20px;
-  font-style: italic;
-}
 .swagger-section .api-popup-scopes li input {
   position: relative;
   top: 2px;
 }
+.swagger-section .api-popup-scopes .api-scope-desc {
+  padding-left: 20px;
+  font-style: italic;
+}
 .swagger-section .api-popup-actions {
   padding-top: 10px;
 }
@@ -1106,8 +1345,16 @@
 .swagger-section .auth {
   float: right;
 }
-.swagger-section #api_information_panel {
-  position: absolute;
+.swagger-section .api-ic {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .api-ic .api_information_panel {
+  position: relative;
+  margin-top: 20px;
+  margin-left: -5px;
   background: #FFF;
   border: 1px solid #ccc;
   border-radius: 5px;
@@ -1118,34 +1365,32 @@
   color: black;
   padding: 5px;
 }
-.swagger-section #api_information_panel p .api-msg-enabled {
+.swagger-section .api-ic .api_information_panel p .api-msg-enabled {
   color: green;
 }
-.swagger-section #api_information_panel p .api-msg-disabled {
+.swagger-section .api-ic .api_information_panel p .api-msg-disabled {
   color: red;
 }
-.swagger-section .api-ic {
-  height: 18px;
-  vertical-align: middle;
-  display: inline-block;
-  background: url(../images/explorer_icons.png) no-repeat;
+.swagger-section .api-ic:hover .api_information_panel {
+  position: absolute;
+  display: block;
 }
 .swagger-section .ic-info {
   background-position: 0 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-warning {
   background-position: -60px 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-error {
   background-position: -30px 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-off {
@@ -1162,34 +1407,33 @@
 }
 .swagger-section #header {
   background-color: #89bf04;
-  padding: 14px;
+  padding: 9px 14px 19px 14px;
+  height: 23px;
+  min-width: 775px;
 }
-.swagger-section #header a#logo {
-  font-size: 1.5em;
-  font-weight: bold;
-  text-decoration: none;
-  background: transparent url(../images/logo_small.png) no-repeat left center;
-  padding: 20px 0 20px 40px;
-  color: white;
+.swagger-section #input_baseUrl {
+  width: 400px;
 }
-.swagger-section #header form#api_selector {
+.swagger-section #api_selector {
   display: block;
   clear: none;
   float: right;
 }
-.swagger-section #header form#api_selector .input {
-  display: block;
+.swagger-section #api_selector .input {
+  display: inline-block;
   clear: none;
-  float: left;
   margin: 0 10px 0 0;
 }
-.swagger-section #header form#api_selector .input input#input_apiKey {
-  width: 200px;
+.swagger-section #api_selector input {
+  font-size: 0.9em;
+  padding: 3px;
+  margin: 0;
 }
-.swagger-section #header form#api_selector .input input#input_baseUrl {
-  width: 400px;
+.swagger-section #input_apiKey {
+  width: 200px;
 }
-.swagger-section #header form#api_selector .input a#explore {
+.swagger-section #explore,
+.swagger-section #auth_container .authorize__btn {
   display: block;
   text-decoration: none;
   font-weight: bold;
@@ -1204,13 +1448,24 @@
   -khtml-border-radius: 4px;
   border-radius: 4px;
 }
-.swagger-section #header form#api_selector .input a#explore:hover {
+.swagger-section #explore:hover,
+.swagger-section #auth_container .authorize__btn:hover {
   background-color: #547f00;
 }
-.swagger-section #header form#api_selector .input input {
-  font-size: 0.9em;
-  padding: 3px;
-  margin: 0;
+.swagger-section #header #logo {
+  font-size: 1.5em;
+  font-weight: bold;
+  text-decoration: none;
+  color: white;
+}
+.swagger-section #header #logo .logo__img {
+  display: block;
+  float: left;
+  margin-top: 2px;
+}
+.swagger-section #header #logo .logo__title {
+  display: inline-block;
+  padding: 5px 0 0 10px;
 }
 .swagger-section #content_message {
   margin: 10px 15px;
@@ -1222,3 +1477,13 @@
   text-align: center;
   padding-top: 10px;
 }
+.swagger-section .swagger-collapse:before {
+  content: "-";
+}
+.swagger-section .swagger-expand:before {
+  content: "+";
+}
+.swagger-section .error {
+  outline-color: #cc0000;
+  background-color: #f2dede;
+}
diff --git a/profiles/killbill/src/main/webapp/css/style.css b/profiles/killbill/src/main/webapp/css/style.css
new file mode 100644
index 0000000..fc21a31
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/css/style.css
@@ -0,0 +1,250 @@
+.swagger-section #header a#logo {
+  font-size: 1.5em;
+  font-weight: bold;
+  text-decoration: none;
+  background: transparent url(../images/logo.png) no-repeat left center;
+  padding: 20px 0 20px 40px;
+}
+#text-head {
+  font-size: 80px;
+  font-family: 'Roboto', sans-serif;
+  color: #ffffff;
+  float: right;
+  margin-right: 20%;
+}
+.navbar-fixed-top .navbar-nav {
+  height: auto;
+}
+.navbar-fixed-top .navbar-brand {
+  height: auto;
+}
+.navbar-header {
+  height: auto;
+}
+.navbar-inverse {
+  background-color: #000;
+  border-color: #000;
+}
+#navbar-brand {
+  margin-left: 20%;
+}
+.navtext {
+  font-size: 10px;
+}
+.h1,
+h1 {
+  font-size: 60px;
+}
+.navbar-default .navbar-header .navbar-brand {
+  color: #a2dfee;
+}
+/* tag titles */
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+  color: #393939;
+  font-family: 'Arvo', serif;
+  font-size: 1.5em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+  color: #525252;
+  padding-left: 0px;
+  display: block;
+  clear: none;
+  float: left;
+  font-family: 'Arvo', serif;
+  font-weight: bold;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #0A0A0A;
+}
+.container1 {
+  width: 1500px;
+  margin: auto;
+  margin-top: 0;
+  background-image: url('../images/shield.png');
+  background-repeat: no-repeat;
+  background-position: -40px -20px;
+  margin-bottom: 210px;
+}
+.container-inner {
+  width: 1200px;
+  margin: auto;
+  background-color: rgba(223, 227, 228, 0.75);
+  padding-bottom: 40px;
+  padding-top: 40px;
+  border-radius: 15px;
+}
+.header-content {
+  padding: 0;
+  width: 1000px;
+}
+.title1 {
+  font-size: 80px;
+  font-family: 'Vollkorn', serif;
+  color: #404040;
+  text-align: center;
+  padding-top: 40px;
+  padding-bottom: 100px;
+}
+#icon {
+  margin-top: -18px;
+}
+.subtext {
+  font-size: 25px;
+  font-style: italic;
+  color: #08b;
+  text-align: right;
+  padding-right: 250px;
+}
+.bg-primary {
+  background-color: #00468b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:focus {
+  color: #08b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:hover {
+  color: #08b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:focus:hover {
+  color: #08b;
+}
+.text-faded {
+  font-size: 25px;
+  font-family: 'Vollkorn', serif;
+}
+.section-heading {
+  font-family: 'Vollkorn', serif;
+  font-size: 45px;
+  padding-bottom: 10px;
+}
+hr {
+  border-color: #00468b;
+  padding-bottom: 10px;
+}
+.description {
+  margin-top: 20px;
+  padding-bottom: 200px;
+}
+.description li {
+  font-family: 'Vollkorn', serif;
+  font-size: 25px;
+  color: #525252;
+  margin-left: 28%;
+  padding-top: 5px;
+}
+.gap {
+  margin-top: 200px;
+}
+.troubleshootingtext {
+  color: rgba(255, 255, 255, 0.7);
+  padding-left: 30%;
+}
+.troubleshootingtext li {
+  list-style-type: circle;
+  font-size: 25px;
+  padding-bottom: 5px;
+}
+.overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1000;
+}
+.block.response_body.json:hover {
+  cursor: pointer;
+}
+.backdrop {
+  color: blue;
+}
+#myModal {
+  height: 100%;
+}
+.modal-backdrop {
+  bottom: 0;
+  position: fixed;
+}
+.curl {
+  padding: 10px;
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+  max-height: 400px;
+  margin-top: 5px;
+  overflow-y: auto;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  border-radius: 4px;
+}
+.curl_title {
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+  font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
+  font-weight: 500;
+  line-height: 1.1;
+}
+.footer {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap h2 {
+  padding: 0;
+}
+h2 {
+  margin: 0;
+  margin-bottom: 5px;
+}
+.markdown p {
+  font-size: 15px;
+  font-family: 'Arvo', serif;
+}
+.swagger-section .swagger-ui-wrap .code {
+  font-size: 15px;
+  font-family: 'Arvo', serif;
+}
+.swagger-section .swagger-ui-wrap b {
+  font-family: 'Arvo', serif;
+}
+#signin:hover {
+  cursor: pointer;
+}
+.dropdown-menu {
+  padding: 15px;
+}
+.navbar-right .dropdown-menu {
+  left: 0;
+  right: auto;
+}
+#signinbutton {
+  width: 100%;
+  height: 32px;
+  font-size: 13px;
+  font-weight: bold;
+  color: #08b;
+}
+.navbar-default .nav > li .details {
+  color: #000000;
+  text-transform: none;
+  font-size: 15px;
+  font-weight: normal;
+  font-family: 'Open Sans', sans-serif;
+  font-style: italic;
+  line-height: 20px;
+  top: -2px;
+}
+.navbar-default .nav > li .details:hover {
+  color: black;
+}
+#signout {
+  width: 100%;
+  height: 32px;
+  font-size: 13px;
+  font-weight: bold;
+  color: #08b;
+}
diff --git a/profiles/killbill/src/main/webapp/css/typography.css b/profiles/killbill/src/main/webapp/css/typography.css
new file mode 100644
index 0000000..efb785f
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/css/typography.css
@@ -0,0 +1,14 @@
+/* Google Font's Droid Sans */
+@font-face {
+  font-family: 'Droid Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype');
+}
+/* Google Font's Droid Sans Bold */
+@font-face {
+  font-family: 'Droid Sans';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype');
+}
diff --git a/profiles/killbill/src/main/webapp/fonts/DroidSans.ttf b/profiles/killbill/src/main/webapp/fonts/DroidSans.ttf
new file mode 100644
index 0000000..e517a0c
Binary files /dev/null and b/profiles/killbill/src/main/webapp/fonts/DroidSans.ttf differ
diff --git a/profiles/killbill/src/main/webapp/fonts/DroidSans-Bold.ttf b/profiles/killbill/src/main/webapp/fonts/DroidSans-Bold.ttf
new file mode 100644
index 0000000..036c4d1
Binary files /dev/null and b/profiles/killbill/src/main/webapp/fonts/DroidSans-Bold.ttf differ
diff --git a/profiles/killbill/src/main/webapp/images/throbber.gif b/profiles/killbill/src/main/webapp/images/throbber.gif
new file mode 100644
index 0000000..0639388
Binary files /dev/null and b/profiles/killbill/src/main/webapp/images/throbber.gif differ
diff --git a/profiles/killbill/src/main/webapp/lib/backbone-min.js b/profiles/killbill/src/main/webapp/lib/backbone-min.js
index c1c0d4f..a3f544b 100644
--- a/profiles/killbill/src/main/webapp/lib/backbone-min.js
+++ b/profiles/killbill/src/main/webapp/lib/backbone-min.js
@@ -1,38 +1,15 @@
-// Backbone.js 0.9.2
+// Backbone.js 1.1.2
 
-// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://backbonejs.org
-(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
-{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
-z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
-{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
-b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
-b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
-a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
-h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
-return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
-{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
-!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
-this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
-l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
-a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
-shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
-this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
-e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
-{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
-arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
-C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
-this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
-""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
-!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
-this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
-stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
-function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
-this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
-f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
-for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
-!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
-e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
-b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);
+(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});
+
+// From http://stackoverflow.com/a/19431552
+// Compatibility override - Backbone 1.1 got rid of the 'options' binding
+// automatically to views in the constructor - we need to keep that.
+Backbone.View = (function(View) {
+   return View.extend({
+        constructor: function(options) {
+            this.options = options || {};
+            View.apply(this, arguments);
+        }
+    });
+})(Backbone.View);
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/handlebars-2.0.0.js b/profiles/killbill/src/main/webapp/lib/handlebars-2.0.0.js
new file mode 100644
index 0000000..53cf921
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/handlebars-2.0.0.js
@@ -0,0 +1,28 @@
+/*!
+
+ handlebars v2.0.0
+
+Copyright (C) 2011-2014 by Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+@license
+*/
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.Handlebars=a.Handlebars||b()}(this,function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return i[a]}function c(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])Object.prototype.hasOwnProperty.call(arguments[b],c)&&(a[c]=arguments[b][c]);return a}function d(a){return a instanceof h?a.toString():null==a?"":a?(a=""+a,k.test(a)?a.replace(j,b):a):a+""}function e(a){return a||0===a?n(a)&&0===a.length?!0:!1:!0}function f(a,b){return(a?a+".":"")+b}var g={},h=a,i={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},j=/[&<>"'`]/g,k=/[&<>"'`]/;g.extend=c;var l=Object.prototype.toString;g.toString=l;var m=function(a){return"function"==typeof a};m(/x/)&&(m=function(a){return"function"==typeof a&&"[object Function]"===l.call(a)});var m;g.isFunction=m;var n=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===l.call(a):!1};return g.isArray=n,g.escapeExpression=d,g.isEmpty=e,g.appendContextPath=f,g}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f<c.length;f++)this[c[f]]=e[c[f]];d&&(this.lineNumber=d,this.column=b.firstColumn)}var b,c=["description","fileName","lineNumber","message","name","number","stack"];return a.prototype=new Error,b=a}(),d=function(a,b){"use strict";function c(a,b){this.helpers=a||{},this.partials=b||{},d(this)}function d(a){a.registerHelper("helperMissing",function(){if(1===arguments.length)return void 0;throw new g("Missing helper: '"+arguments[arguments.length-1].name+"'")}),a.registerHelper("blockHelperMissing",function(b,c){var d=c.inverse,e=c.fn;if(b===!0)return e(this);if(b===!1||null==b)return d(this);if(k(b))return b.length>0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):d(this);if(c.data&&c.ids){var g=q(c.data);g.contextPath=f.appendContextPath(c.data.contextPath,c.name),c={data:g}}return e(b,c)}),a.registerHelper("each",function(a,b){if(!b)throw new g("Must pass iterator to #each");var c,d,e=b.fn,h=b.inverse,i=0,j="";if(b.data&&b.ids&&(d=f.appendContextPath(b.data.contextPath,b.ids[0])+"."),l(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(k(a))for(var m=a.length;m>i;i++)c&&(c.index=i,c.first=0===i,c.last=i===a.length-1,d&&(c.contextPath=d+i)),j+=e(a[i],{data:c});else for(var n in a)a.hasOwnProperty(n)&&(c&&(c.key=n,c.index=i,c.first=0===i,d&&(c.contextPath=d+n)),j+=e(a[n],{data:c}),i++);return 0===i&&(j=h(this)),j}),a.registerHelper("if",function(a,b){return l(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||f.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){l(a)&&(a=a.call(this));var c=b.fn;if(f.isEmpty(a))return b.inverse(this);if(b.data&&b.ids){var d=q(b.data);d.contextPath=f.appendContextPath(b.data.contextPath,b.ids[0]),b={data:d}}return c(a,b)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)}),a.registerHelper("lookup",function(a,b){return a&&a[b]})}var e={},f=a,g=b,h="2.0.0";e.VERSION=h;var i=6;e.COMPILER_REVISION=i;var j={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=j;var k=f.isArray,l=f.isFunction,m=f.toString,n="[object Object]";e.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:o,log:p,registerHelper:function(a,b){if(m.call(a)===n){if(b)throw new g("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){m.call(a)===n?f.extend(this.partials,a):this.partials[a]=b},unregisterPartial:function(a){delete this.partials[a]}};var o={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(o.level<=a){var c=o.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};e.logger=o;var p=o.log;e.log=p;var q=function(a){var b=f.extend({},a);return b._parent=a,b};return e.createFrame=q,e}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");if(!a||!a.main)throw new l("Unknown template object: "+typeof a);b.VM.checkRevision(a.compiler);var c=function(c,d,e,f,g,h,i,j,m){g&&(f=k.extend({},f,g));var n=b.VM.invokePartial.call(this,c,e,f,h,i,j,m);if(null==n&&b.compile){var o={helpers:h,partials:i,data:j,depths:m};i[e]=b.compile(c,{data:void 0!==j,compat:a.compat},b),n=i[e](f,o)}if(null!=n){if(d){for(var p=n.split("\n"),q=0,r=p.length;r>q&&(p[q]||q+1!==r);q++)p[q]=d+p[q];n=p.join("\n")}return n}throw new l("The partial "+e+" could not be compiled when running in runtime-only mode")},d={lookup:function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d]&&null!=a[d][b])return a[d][b]},lambda:function(a,b){return"function"==typeof a?a.call(b):a},escapeExpression:k.escapeExpression,invokePartial:c,fn:function(b){return a[b]},programs:[],program:function(a,b,c){var d=this.programs[a],e=this.fn(a);return b||c?d=f(this,a,e,b,c):d||(d=this.programs[a]=f(this,a,e)),d},data:function(a,b){for(;a&&b--;)a=a._parent;return a},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c=k.extend({},b,a)),c},noop:b.VM.noop,compilerInfo:a.compiler},e=function(b,c){c=c||{};var f=c.data;e._setup(c),!c.partial&&a.useData&&(f=i(b,f));var g;return a.useDepths&&(g=c.depths?[b].concat(c.depths):[b]),a.main.call(d,b,d.helpers,d.partials,f,g)};return e.isTop=!0,e._setup=function(c){c.partial?(d.helpers=c.helpers,d.partials=c.partials):(d.helpers=d.merge(c.helpers,b.helpers),a.usePartial&&(d.partials=d.merge(c.partials,b.partials)))},e._child=function(b,c,e){if(a.useDepths&&!e)throw new l("must pass parent depths");return f(d,b,a[b],c,e)},e}function f(a,b,c,d,e){var f=function(b,f){return f=f||{},c.call(a,b,a.helpers,a.partials,f.data||d,e&&[b].concat(e))};return f.program=b,f.depth=e?e.length:0,f}function g(a,b,c,d,e,f,g){var h={partial:!0,helpers:d,partials:e,data:f,depths:g};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,h):void 0}function h(){return""}function i(a,b){return b&&"root"in b||(b=b?o(b):{},b.root=a),b}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES,o=c.createFrame;return j.checkRevision=d,j.template=e,j.program=f,j.invokePartial=g,j.noop=h,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.escapeExpression=j.escapeExpression,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,m["default"]=m,f=m}(d,a,c,b,e),g=function(a){"use strict";function b(a){a=a||{},this.firstLine=a.first_line,this.firstColumn=a.first_column,this.lastColumn=a.last_column,this.lastLine=a.last_line}var c,d=a,e={ProgramNode:function(a,c,d){b.call(this,d),this.type="program",this.statements=a,this.strip=c},MustacheNode:function(a,c,d,f,g){if(b.call(this,g),this.type="mustache",this.strip=f,null!=d&&d.charAt){var h=d.charAt(3)||d.charAt(2);this.escaped="{"!==h&&"&"!==h}else this.escaped=!!d;this.sexpr=a instanceof e.SexprNode?a:new e.SexprNode(a,c),this.id=this.sexpr.id,this.params=this.sexpr.params,this.hash=this.sexpr.hash,this.eligibleHelper=this.sexpr.eligibleHelper,this.isHelper=this.sexpr.isHelper},SexprNode:function(a,c,d){b.call(this,d),this.type="sexpr",this.hash=c;var e=this.id=a[0],f=this.params=a.slice(1);this.isHelper=!(!f.length&&!c),this.eligibleHelper=this.isHelper||e.isSimple},PartialNode:function(a,c,d,e,f){b.call(this,f),this.type="partial",this.partialName=a,this.context=c,this.hash=d,this.strip=e,this.strip.inlineStandalone=!0},BlockNode:function(a,c,d,e,f){b.call(this,f),this.type="block",this.mustache=a,this.program=c,this.inverse=d,this.strip=e,d&&!c&&(this.isInverse=!0)},RawBlockNode:function(a,c,f,g){if(b.call(this,g),a.sexpr.id.original!==f)throw new d(a.sexpr.id.original+" doesn't match "+f,this);c=new e.ContentNode(c,g),this.type="block",this.mustache=a,this.program=new e.ProgramNode([c],{},g)},ContentNode:function(a,c){b.call(this,c),this.type="content",this.original=this.string=a},HashNode:function(a,c){b.call(this,c),this.type="hash",this.pairs=a},IdNode:function(a,c){b.call(this,c),this.type="ID";for(var e="",f=[],g=0,h="",i=0,j=a.length;j>i;i++){var k=a[i].part;if(e+=(a[i].separator||"")+k,".."===k||"."===k||"this"===k){if(f.length>0)throw new d("Invalid path: "+e,this);".."===k?(g++,h+="../"):this.isScoped=!0}else f.push(k)}this.original=e,this.parts=f,this.string=f.join("."),this.depth=g,this.idName=h+this.string,this.isSimple=1===a.length&&!this.isScoped&&0===g,this.stringModeValue=this.string},PartialNameNode:function(a,c){b.call(this,c),this.type="PARTIAL_NAME",this.name=a.original},DataNode:function(a,c){b.call(this,c),this.type="DATA",this.id=a,this.stringModeValue=a.stringModeValue,this.idName="@"+a.stringModeValue},StringNode:function(a,c){b.call(this,c),this.type="STRING",this.original=this.string=this.stringModeValue=a},NumberNode:function(a,c){b.call(this,c),this.type="NUMBER",this.original=this.number=a,this.stringModeValue=Number(a)},BooleanNode:function(a,c){b.call(this,c),this.type="BOOLEAN",this.bool=a,this.stringModeValue="true"===a},CommentNode:function(a,c){b.call(this,c),this.type="comment",this.comment=a,this.strip={inlineStandalone:!0}}};return c=e}(c),h=function(){"use strict";var a,b=function(){function a(){this.yy={}}var b={trace:function(){},yy:{},symbols_:{error:2,root:3,program:4,EOF:5,program_repetition0:6,statement:7,mustache:8,block:9,rawBlock:10,partial:11,CONTENT:12,COMMENT:13,openRawBlock:14,END_RAW_BLOCK:15,OPEN_RAW_BLOCK:16,sexpr:17,CLOSE_RAW_BLOCK:18,openBlock:19,block_option0:20,closeBlock:21,openInverse:22,block_option1:23,OPEN_BLOCK:24,CLOSE:25,OPEN_INVERSE:26,inverseAndProgram:27,INVERSE:28,OPEN_ENDBLOCK:29,path:30,OPEN:31,OPEN_UNESCAPED:32,CLOSE_UNESCAPED:33,OPEN_PARTIAL:34,partialName:35,param:36,partial_option0:37,partial_option1:38,sexpr_repetition0:39,sexpr_option0:40,dataName:41,STRING:42,NUMBER:43,BOOLEAN:44,OPEN_SEXPR:45,CLOSE_SEXPR:46,hash:47,hash_repetition_plus0:48,hashSegment:49,ID:50,EQUALS:51,DATA:52,pathSegments:53,SEP:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],performAction:function(a,b,c,d,e,f){var g=f.length-1;switch(e){case 1:return d.prepareProgram(f[g-1].statements,!0),f[g-1];case 2:this.$=new d.ProgramNode(d.prepareProgram(f[g]),{},this._$);break;case 3:this.$=f[g];break;case 4:this.$=f[g];break;case 5:this.$=f[g];break;case 6:this.$=f[g];break;case 7:this.$=new d.ContentNode(f[g],this._$);break;case 8:this.$=new d.CommentNode(f[g],this._$);break;case 9:this.$=new d.RawBlockNode(f[g-2],f[g-1],f[g],this._$);break;case 10:this.$=new d.MustacheNode(f[g-1],null,"","",this._$);break;case 11:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!1,this._$);break;case 12:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!0,this._$);break;case 13:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 14:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 15:this.$={strip:d.stripFlags(f[g-1],f[g-1]),program:f[g]};break;case 16:this.$={path:f[g-1],strip:d.stripFlags(f[g-2],f[g])};break;case 17:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 18:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 19:this.$=new d.PartialNode(f[g-3],f[g-2],f[g-1],d.stripFlags(f[g-4],f[g]),this._$);break;case 20:this.$=new d.PartialNode(f[g-2],void 0,f[g-1],d.stripFlags(f[g-3],f[g]),this._$);break;case 21:this.$=new d.SexprNode([f[g-2]].concat(f[g-1]),f[g],this._$);break;case 22:this.$=new d.SexprNode([f[g]],null,this._$);break;case 23:this.$=f[g];break;case 24:this.$=new d.StringNode(f[g],this._$);break;case 25:this.$=new d.NumberNode(f[g],this._$);break;case 26:this.$=new d.BooleanNode(f[g],this._$);break;case 27:this.$=f[g];break;case 28:f[g-1].isHelper=!0,this.$=f[g-1];break;case 29:this.$=new d.HashNode(f[g],this._$);break;case 30:this.$=[f[g-2],f[g]];break;case 31:this.$=new d.PartialNameNode(f[g],this._$);break;case 32:this.$=new d.PartialNameNode(new d.StringNode(f[g],this._$),this._$);break;case 33:this.$=new d.PartialNameNode(new d.NumberNode(f[g],this._$));break;case 34:this.$=new d.DataNode(f[g],this._$);break;case 35:this.$=new d.IdNode(f[g],this._$);break;case 36:f[g-2].push({part:f[g],separator:f[g-1]}),this.$=f[g-2];break;case 37:this.$=[{part:f[g]}];break;case 38:this.$=[];break;case 39:f[g-1].push(f[g]);break;case 48:this.$=[];break;case 49:f[g-1].push(f[g]);break;case 52:this.$=[f[g]];break;case 53:f[g-1].push(f[g])}},table:[{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],defaultActions:{4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},parseError:function(a){throw new Error(a)},parse:function(a){function b(){var a;return a=c.lexer.lex()||1,"number"!=typeof a&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var l=this.lexer.yylloc;f.push(l);var m=this.lexer.options&&this.lexer.options.ranges;"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var n,o,p,q,r,s,t,u,v,w={};;){if(p=d[d.length-1],this.defaultActions[p]?q=this.defaultActions[p]:((null===n||"undefined"==typeof n)&&(n=b()),q=g[p]&&g[p][n]),"undefined"==typeof q||!q.length||!q[0]){var x="";if(!k){v=[];for(s in g[p])this.terminals_[s]&&s>2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;g<f.length&&(c=this._input.match(this.rules[f[g]]),!c||b&&!(c[0].length>b[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substr(a,b.yyleng-c)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 12;break;case 1:return 12;case 2:return this.popState(),12;case 3:return b.yytext=b.yytext.substr(5,b.yyleng-9),this.popState(),15;case 4:return 12;case 5:return e(0,4),this.popState(),13;case 6:return 45;case 7:return 46;case 8:return 16;case 9:return this.popState(),this.begin("raw"),18;case 10:return 34;case 11:return 24;case 12:return 29;case 13:return this.popState(),28;case 14:return this.popState(),28;case 15:return 26;case 16:return 26;case 17:return 32;case 18:return 31;case 19:this.popState(),this.begin("com");break;case 20:return e(3,5),this.popState(),13;case 21:return 31;case 22:return 51;case 23:return 50;case 24:return 50;case 25:return 54;case 26:break;case 27:return this.popState(),33;case 28:return this.popState(),25;case 29:return b.yytext=e(1,2).replace(/\\"/g,'"'),42;case 30:return b.yytext=e(1,2).replace(/\\'/g,"'"),42;case 31:return 52;case 32:return 44;case 33:return 44;case 34:return 43;case 35:return 50;case 36:return b.yytext=e(1,2),50;case 37:return"INVALID";case 38:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[5],inclusive:!1},raw:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,1,38],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();return a=b}(),i=function(a){"use strict";function b(a,b){return{left:"~"===a.charAt(2),right:"~"===b.charAt(b.length-3)}}function c(a,b,c,d,i,k){if(a.sexpr.id.original!==d.path.original)throw new j(a.sexpr.id.original+" doesn't match "+d.path.original,a);var l=c&&c.program,m={left:a.strip.left,right:d.strip.right,openStandalone:f(b.statements),closeStandalone:e((l||b).statements)};if(a.strip.right&&g(b.statements,null,!0),l){var n=c.strip;n.left&&h(b.statements,null,!0),n.right&&g(l.statements,null,!0),d.strip.left&&h(l.statements,null,!0),e(b.statements)&&f(l.statements)&&(h(b.statements),g(l.statements))}else d.strip.left&&h(b.statements,null,!0);return i?new this.BlockNode(a,l,b,m,k):new this.BlockNode(a,b,l,m,k)}function d(a,b){for(var c=0,d=a.length;d>c;c++){var i=a[c],j=i.strip;if(j){var k=e(a,c,b,"partial"===i.type),l=f(a,c,b),m=j.openStandalone&&k,n=j.closeStandalone&&l,o=j.inlineStandalone&&k&&l;j.right&&g(a,c,!0),j.left&&h(a,c,!0),o&&(g(a,c),h(a,c)&&"partial"===i.type&&(i.indent=/([ \t]+$)/.exec(a[c-1].original)?RegExp.$1:"")),m&&(g((i.program||i.inverse).statements),h(a,c)),n&&(g(a,c),h((i.inverse||i.program).statements))}}return a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"content"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"content"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"content"===d.type&&(c||!d.rightStripped)){var e=d.string;d.string=d.string.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.string!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"content"===d.type&&(c||!d.leftStripped)){var e=d.string;return d.string=d.string.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.string!==e,d.leftStripped}}var i={},j=a;return i.stripFlags=b,i.prepareBlock=c,i.prepareProgram=d,i}(c),j=function(a,b,c,d){"use strict";function e(a){return a.constructor===h.ProgramNode?a:(g.yy=k,g.parse(a))}var f={},g=a,h=b,i=c,j=d.extend;f.parser=g;var k={};return j(k,i,h),f.parse=e,f}(h,g,i,b),k=function(a,b){"use strict";function c(){}function d(a,b,c){if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function e(a,b,c){function d(){var d=c.parse(a),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var e,f=function(a,b){return e||(e=d()),e.call(this,a,b)};return f._setup=function(a){return e||(e=d()),e._setup(a)},f._child=function(a,b,c){return e||(e=d()),e._child(a,b,c)},f}function f(a,b){if(a===b)return!0;if(i(a)&&i(b)&&a.length===b.length){for(var c=0;c<a.length;c++)if(!f(a[c],b[c]))return!1;return!0}}var g={},h=a,i=b.isArray,j=[].slice;return g.Compiler=c,c.prototype={compiler:c,equals:function(a){var b=this.opcodes.length;if(a.opcodes.length!==b)return!1;for(var c=0;b>c;c++){var d=this.opcodes[c],e=a.opcodes[c];if(d.opcode!==e.opcode||!f(d.args,e.args))return!1}for(b=this.children.length,c=0;b>c;c++)if(!this.children[c].equals(a.children[c]))return!1;return!0},guid:0,compile:function(a,b){this.opcodes=[],this.children=[],this.depths={list:[]},this.options=b,this.stringParams=b.stringParams,this.trackIds=b.trackIds;var c=this.options.knownHelpers;if(this.options.knownHelpers={helperMissing:!0,blockHelperMissing:!0,each:!0,"if":!0,unless:!0,"with":!0,log:!0,lookup:!0},c)for(var d in c)this.options.knownHelpers[d]=c[d];return this.accept(a)},accept:function(a){return this[a.type](a)},program:function(a){for(var b=a.statements,c=0,d=b.length;d>c;c++)this.accept(b[c]);return this.isSimple=1===d,this.depths.list=this.depths.list.sort(function(a,b){return a-b}),this},compileProgram:function(a){var b,c=(new this.compiler).compile(a,this.options),d=this.guid++;
+this.usePartial=this.usePartial||c.usePartial,this.children[d]=c;for(var e=0,f=c.depths.list.length;f>e;e++)b=c.depths.list[e],2>b||this.addDepth(b-1);return d},block:function(a){var b=a.mustache,c=a.program,d=a.inverse;c&&(c=this.compileProgram(c)),d&&(d=this.compileProgram(d));var e=b.sexpr,f=this.classifySexpr(e);"helper"===f?this.helperSexpr(e,c,d):"simple"===f?(this.simpleSexpr(e),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("blockValue",e.id.original)):(this.ambiguousSexpr(e,c,d),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("ambiguousBlockValue")),this.opcode("append")},hash:function(a){var b,c,d=a.pairs;for(this.opcode("pushHash"),b=0,c=d.length;c>b;b++)this.pushParam(d[b][1]);for(;b--;)this.opcode("assignToHash",d[b][0]);this.opcode("popHash")},partial:function(a){var b=a.partialName;this.usePartial=!0,a.hash?this.accept(a.hash):this.opcode("push","undefined"),a.context?this.accept(a.context):(this.opcode("getContext",0),this.opcode("pushContext")),this.opcode("invokePartial",b.name,a.indent||""),this.opcode("append")},content:function(a){a.string&&this.opcode("appendContent",a.string)},mustache:function(a){this.sexpr(a.sexpr),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},ambiguousSexpr:function(a,b,c){var d=a.id,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),this.ID(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.id;"DATA"===b.type?this.DATA(b):b.parts.length?this.ID(b):(this.addDepth(b.depth),this.opcode("getContext",b.depth),this.opcode("pushContext")),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.id,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new h("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.falsy=!0,this.ID(e),this.opcode("invokeHelper",d.length,e.original,e.isSimple)}},sexpr:function(a){var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ID:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0];b?this.opcode("lookupOnContext",a.parts,a.falsy,a.isScoped):this.opcode("pushContext")},DATA:function(a){this.options.data=!0,this.opcode("lookupData",a.id.depth,a.id.parts)},STRING:function(a){this.opcode("pushString",a.string)},NUMBER:function(a){this.opcode("pushLiteral",a.number)},BOOLEAN:function(a){this.opcode("pushLiteral",a.bool)},comment:function(){},opcode:function(a){this.opcodes.push({opcode:a,args:j.call(arguments,1)})},addDepth:function(a){0!==a&&(this.depths[a]||(this.depths[a]=!0,this.depths.list.push(a)))},classifySexpr:function(a){var b=a.isHelper,c=a.eligibleHelper,d=this.options;if(c&&!b){var e=a.id.parts[0];d.knownHelpers[e]?b=!0:d.knownHelpersOnly&&(c=!1)}return b?"helper":c?"ambiguous":"simple"},pushParams:function(a){for(var b=0,c=a.length;c>b;b++)this.pushParam(a[b])},pushParam:function(a){this.stringParams?(a.depth&&this.addDepth(a.depth),this.opcode("getContext",a.depth||0),this.opcode("pushStringParam",a.stringModeValue,a.type),"sexpr"===a.type&&this.sexpr(a)):(this.trackIds&&this.opcode("pushId",a.type,a.idName||a.stringModeValue),this.accept(a))},setupFullMustacheParams:function(a,b,c){var d=a.params;return this.pushParams(d),this.opcode("pushProgram",b),this.opcode("pushProgram",c),a.hash?this.hash(a.hash):this.opcode("emptyHash"),d}},g.precompile=d,g.compile=e,g}(c,b),l=function(a,b){"use strict";function c(a){this.value=a}function d(){}var e,f=a.COMPILER_REVISION,g=a.REVISION_CHANGES,h=b;d.prototype={nameLookup:function(a,b){return d.isValidJavaScriptVariableName(b)?a+"."+b:a+"['"+b+"']"},depthedLookup:function(a){return this.aliases.lookup="this.lookup",'lookup(depths, "'+a+'")'},compilerInfo:function(){var a=f,b=g[a];return[a,b]},appendToBuffer:function(a){return this.environment.isSimple?"return "+a+";":{appendToBuffer:!0,content:a,toString:function(){return"buffer += "+a+";"}}},initializeBuffer:function(){return this.quotedString("")},namespace:"Handlebars",compile:function(a,b,c,d){this.environment=a,this.options=b,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!d,this.name=this.environment.name,this.isChild=!!c,this.context=c||{programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.compileChildren(a,b),this.useDepths=this.useDepths||a.depths.list.length||this.options.compat;var e,f,g,i=a.opcodes;for(f=0,g=i.length;g>f;f++)e=i[f],this[e.opcode].apply(this,e.args);if(this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new h("Compile completed with content left on stack");var j=this.createFunctionContext(d);if(this.isChild)return j;var k={compiler:this.compilerInfo(),main:j},l=this.context.programs;for(f=0,g=l.length;g>f;f++)l[f]&&(k[f]=l[f]);return this.environment.usePartial&&(k.usePartial=!0),this.options.data&&(k.useData=!0),this.useDepths&&(k.useDepths=!0),this.options.compat&&(k.compat=!0),d||(k.compiler=JSON.stringify(k.compiler),k=this.objectLiteral(k)),k},preamble:function(){this.lastContext=0,this.source=[]},createFunctionContext:function(a){var b="",c=this.stackVars.concat(this.registers.list);c.length>0&&(b+=", "+c.join(", "));for(var d in this.aliases)this.aliases.hasOwnProperty(d)&&(b+=", "+d+"="+this.aliases[d]);var e=["depth0","helpers","partials","data"];this.useDepths&&e.push("depths");var f=this.mergeSource(b);return a?(e.push(f),Function.apply(this,e)):"function("+e.join(",")+") {\n  "+f+"}"},mergeSource:function(a){for(var b,c,d="",e=!this.forceBuffer,f=0,g=this.source.length;g>f;f++){var h=this.source[f];h.appendToBuffer?b=b?b+"\n    + "+h.content:h.content:(b&&(d?d+="buffer += "+b+";\n  ":(c=!0,d=b+";\n  "),b=void 0),d+=h+"\n  ",this.environment.isSimple||(e=!1))}return e?(b||!d)&&(d+="return "+(b||'""')+";\n"):(a+=", buffer = "+(c?"":this.initializeBuffer()),d+=b?"return buffer + "+b+";\n":"return buffer;\n"),a&&(d="var "+a.substring(2)+(c?"":";\n  ")+d),d},blockValue:function(a){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var b=[this.contextName(0)];this.setupParams(a,0,b);var c=this.popStack();b.splice(1,0,c),this.push("blockHelperMissing.call("+b.join(", ")+")")},ambiguousBlockValue:function(){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=[this.contextName(0)];this.setupParams("",0,a,!0),this.flushInline();var b=this.topStack();a.splice(1,0,b),this.pushSource("if (!"+this.lastHelper+") { "+b+" = blockHelperMissing.call("+a.join(", ")+"); }")},appendContent:function(a){this.pendingContent&&(a=this.pendingContent+a),this.pendingContent=a},append:function(){this.flushInline();var a=this.popStack();this.pushSource("if ("+a+" != null) { "+this.appendToBuffer(a)+" }"),this.environment.isSimple&&this.pushSource("else { "+this.appendToBuffer("''")+" }")},appendEscaped:function(){this.aliases.escapeExpression="this.escapeExpression",this.pushSource(this.appendToBuffer("escapeExpression("+this.popStack()+")"))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c){var d=0,e=a.length;for(c||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[d++]));e>d;d++)this.replaceStack(function(c){var e=this.nameLookup(c,a[d],"context");return b?" && "+e:" != null ? "+e+" : "+c})},lookupData:function(a,b){a?this.pushStackLiteral("this.data(data, "+a+")"):this.pushStackLiteral("data");for(var c=b.length,d=0;c>d;d++)this.replaceStack(function(a){return" && "+this.nameLookup(a,b[d],"data")})},resolvePossibleLambda:function(){this.aliases.lambda="this.lambda",this.push("lambda("+this.popStack()+", "+this.contextName(0)+")")},pushStringParam:function(a,b){this.pushContext(),this.pushString(b),"sexpr"!==b&&("string"==typeof a?this.pushString(a):this.pushStackLiteral(a))},emptyHash:function(){this.pushStackLiteral("{}"),this.trackIds&&this.push("{}"),this.stringParams&&(this.push("{}"),this.push("{}"))},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[],ids:[]}},popHash:function(){var a=this.hash;this.hash=this.hashes.pop(),this.trackIds&&this.push("{"+a.ids.join(",")+"}"),this.stringParams&&(this.push("{"+a.contexts.join(",")+"}"),this.push("{"+a.types.join(",")+"}")),this.push("{\n    "+a.values.join(",\n    ")+"\n  }")},pushString:function(a){this.pushStackLiteral(this.quotedString(a))},push:function(a){return this.inlineStack.push(a),a},pushLiteral:function(a){this.pushStackLiteral(a)},pushProgram:function(a){null!=a?this.pushStackLiteral(this.programExpression(a)):this.pushStackLiteral(null)},invokeHelper:function(a,b,c){this.aliases.helperMissing="helpers.helperMissing";var d=this.popStack(),e=this.setupHelper(a,b),f=(c?e.name+" || ":"")+d+" || helperMissing";this.push("(("+f+").call("+e.callParams+"))")},invokeKnownHelper:function(a,b){var c=this.setupHelper(a,b);this.push(c.name+".call("+c.callParams+")")},invokeAmbiguous:function(a,b){this.aliases.functionType='"function"',this.aliases.helperMissing="helpers.helperMissing",this.useRegister("helper");var c=this.popStack();this.emptyHash();var d=this.setupHelper(0,a,b),e=this.lastHelper=this.nameLookup("helpers",a,"helper");this.push("((helper = (helper = "+e+" || "+c+") != null ? helper : helperMissing"+(d.paramsInit?"),("+d.paramsInit:"")+"),(typeof helper === functionType ? helper.call("+d.callParams+") : helper))")},invokePartial:function(a,b){var c=[this.nameLookup("partials",a,"partial"),"'"+b+"'","'"+a+"'",this.popStack(),this.popStack(),"helpers","partials"];this.options.data?c.push("data"):this.options.compat&&c.push("undefined"),this.options.compat&&c.push("depths"),this.push("this.invokePartial("+c.join(", ")+")")},assignToHash:function(a){var b,c,d,e=this.popStack();this.trackIds&&(d=this.popStack()),this.stringParams&&(c=this.popStack(),b=this.popStack());var f=this.hash;b&&f.contexts.push("'"+a+"': "+b),c&&f.types.push("'"+a+"': "+c),d&&f.ids.push("'"+a+"': "+d),f.values.push("'"+a+"': ("+e+")")},pushId:function(a,b){"ID"===a||"DATA"===a?this.pushString(b):"sexpr"===a?this.pushStackLiteral("true"):this.pushStackLiteral("null")},compiler:d,compileChildren:function(a,b){for(var c,d,e=a.children,f=0,g=e.length;g>f;f++){c=e[f],d=new this.compiler;var h=this.matchExistingProgram(c);null==h?(this.context.programs.push(""),h=this.context.programs.length,c.index=h,c.name="program"+h,this.context.programs[h]=d.compile(c,b,this.context,!this.precompile),this.context.environments[h]=c,this.useDepths=this.useDepths||d.useDepths):(c.index=h,c.name="program"+h)}},matchExistingProgram:function(a){for(var b=0,c=this.context.environments.length;c>b;b++){var d=this.context.environments[b];if(d&&d.equals(a))return b}},programExpression:function(a){var b=this.environment.children[a],c=(b.depths.list,this.useDepths),d=[b.index,"data"];return c&&d.push("depths"),"this.program("+d.join(", ")+")"},useRegister:function(a){this.registers[a]||(this.registers[a]=!0,this.registers.list.push(a))},pushStackLiteral:function(a){return this.push(new c(a))},pushSource:function(a){this.pendingContent&&(this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent))),this.pendingContent=void 0),a&&this.source.push(a)},pushStack:function(a){this.flushInline();var b=this.incrStack();return this.pushSource(b+" = "+a+";"),this.compileStack.push(b),b},replaceStack:function(a){{var b,d,e,f="";this.isInline()}if(!this.isInline())throw new h("replaceStack on non-inline");var g=this.popStack(!0);if(g instanceof c)f=b=g.value,e=!0;else{d=!this.stackSlot;var i=d?this.incrStack():this.topStackName();f="("+this.push(i)+" = "+g+")",b=this.topStack()}var j=a.call(this,b);e||this.popStack(),d&&this.stackSlot--,this.push("("+f+j+")")},incrStack:function(){return this.stackSlot++,this.stackSlot>this.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;if(a.length){this.inlineStack=[];for(var b=0,d=a.length;d>b;b++){var e=a[b];e instanceof c?this.compileStack.push(e):this.pushStack(e)}}},isInline:function(){return this.inlineStack.length},popStack:function(a){var b=this.isInline(),d=(b?this.inlineStack:this.compileStack).pop();if(!a&&d instanceof c)return d.value;if(!b){if(!this.stackSlot)throw new h("Invalid stack pop");this.stackSlot--}return d},topStack:function(){var a=this.isInline()?this.inlineStack:this.compileStack,b=a[a.length-1];return b instanceof c?b.value:b},contextName:function(a){return this.useDepths&&a?"depths["+a+"]":"depth"+a},quotedString:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")+'"'},objectLiteral:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(this.quotedString(c)+":"+a[c]);return"{"+b.join(",")+"}"},setupHelper:function(a,b,c){var d=[],e=this.setupParams(b,a,d,c),f=this.nameLookup("helpers",b,"helper");return{params:d,paramsInit:e,name:f,callParams:[this.contextName(0)].concat(d).join(", ")}},setupOptions:function(a,b,c){var d,e,f,g={},h=[],i=[],j=[];g.name=this.quotedString(a),g.hash=this.popStack(),this.trackIds&&(g.hashIds=this.popStack()),this.stringParams&&(g.hashTypes=this.popStack(),g.hashContexts=this.popStack()),e=this.popStack(),f=this.popStack(),(f||e)&&(f||(f="this.noop"),e||(e="this.noop"),g.fn=f,g.inverse=e);for(var k=b;k--;)d=this.popStack(),c[k]=d,this.trackIds&&(j[k]=this.popStack()),this.stringParams&&(i[k]=this.popStack(),h[k]=this.popStack());return this.trackIds&&(g.ids="["+j.join(",")+"]"),this.stringParams&&(g.types="["+i.join(",")+"]",g.contexts="["+h.join(",")+"]"),this.options.data&&(g.data="data"),g},setupParams:function(a,b,c,d){var e=this.objectLiteral(this.setupOptions(a,b,c));return d?(this.useRegister("options"),c.push("options"),"options="+e):(c.push(e),"")}};for(var i="break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private transient debugger implements protected volatile double import public let yield".split(" "),j=d.RESERVED_WORDS={},k=0,l=i.length;l>k;k++)j[i[k]]=!0;return d.isValidJavaScriptVariableName=function(a){return!d.RESERVED_WORDS[a]&&/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(a)},e=d}(d,c),m=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c.parser,j=c.parse,k=d.Compiler,l=d.compile,m=d.precompile,n=e,o=g.create,p=function(){var a=o();return a.compile=function(b,c){return l(b,c,a)},a.precompile=function(b,c){return m(b,c,a)},a.AST=h,a.Compiler=k,a.JavaScriptCompiler=n,a.Parser=i,a.parse=j,a};return g=p(),g.create=p,g["default"]=g,f=g}(f,g,j,k,l);return m});
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack.js b/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack.js
new file mode 100644
index 0000000..928386d
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack.js
@@ -0,0 +1,2 @@
+/*! highlight.js v9.1.0 | BSD3 License | git.io/hljslicense */
+!function(e){"undefined"!=typeof exports?e(exports):(self.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return self.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return E(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(E(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":x.classPrefix,i='<span class="'+a,o=t?"":"</span>";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(M);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(M);r;){e+=n(M.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(M)}return e+n(M.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!R[L.sL])return n(M);var t=e?l(L.sL,M,!0,y[L.sL]):f(M,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(y[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,M=""):e.eB?(k+=n(t)+r,M=""):(k+=r,M=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(M+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(M+=t),k+=b();do L.cN&&(k+="</span>"),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),M="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"<unnamed>")+'"');return M+=t,t.length||1}var N=E(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,L=i||N,y={},k="";for(w=L;w!=N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var M="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),w=L;w.parent;w=w.parent)w.cN&&(k+="</span>");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function f(e,t){t=t||x.languages||Object.keys(R);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(E(n)){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return x.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,x.tabReplace)})),x.useBR&&(e=e.replace(/\n/g,"<br>")),e}function h(e,n,t){var r=n?w[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;x.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,o=n?l(n,r,!0):f(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){x=o(x,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=R[n]=t(e);r.aliases&&r.aliases.forEach(function(e){w[e]=n})}function N(){return Object.keys(R)}function E(e){return e=(e||"").toLowerCase(),R[e]||R[w[e]]}var x={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},R={},w={};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=E,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:i,k:t},s={b:"{",e:"}",c:[{cN:"attr",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:r}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return i.splice(i.length,0,s,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("xml",function(s){var t="[A-Za-z0-9\\._:-]+",e={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php"},r={eW:!0,i:/</,r:0,c:[e,{cN:"attr",b:t,r:0},{b:"=",r:0,c:[{cN:"string",c:[e],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"meta",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("<!--","-->",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{name:"style"},c:[r],starts:{e:"</style>",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{name:"script"},c:[r],starts:{e:"</script>",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},e,{cN:"meta",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\s*\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack_extended.js b/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack_extended.js
new file mode 100644
index 0000000..571c740
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/highlight.9.1.0.pack_extended.js
@@ -0,0 +1,34 @@
+'use strict';
+
+(function () {
+    var configure, highlightBlock;
+
+    configure = hljs.configure;
+    // "extending" hljs.configure method
+    hljs.configure = function _configure (options) {
+        var size = options.highlightSizeThreshold;
+
+        // added highlightSizeThreshold option to set maximum size
+        // of processed string. Set to null if not a number
+        hljs.highlightSizeThreshold = size === +size ? size : null;
+
+        configure.call(this, options);
+    };
+
+    highlightBlock = hljs.highlightBlock;
+
+    // "extending" hljs.highlightBlock method
+    hljs.highlightBlock = function _highlightBlock (el) {
+        var innerHTML = el.innerHTML;
+        var size = hljs.highlightSizeThreshold;
+
+        // check if highlightSizeThreshold is not set or element innerHTML
+        // is less than set option highlightSizeThreshold
+        if (size == null || size > innerHTML.length) {
+            // proceed with hljs.highlightBlock
+            highlightBlock.call(hljs, el);
+        }
+    };
+
+})();
+
diff --git a/profiles/killbill/src/main/webapp/lib/jquery-1.8.0.min.js b/profiles/killbill/src/main/webapp/lib/jquery-1.8.0.min.js
index f121291..066d72c 100644
--- a/profiles/killbill/src/main/webapp/lib/jquery-1.8.0.min.js
+++ b/profiles/killbill/src/main/webapp/lib/jquery-1.8.0.min.js
@@ -1,2 +1,2 @@
-/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
+/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
 (function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()},isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba},p.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){p.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj,g=f.selector;if(!e||e!==d&&!p.contains(d,e))a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b;return c}}}),p.support.submitBubbles||(p.event.special.submit={setup:function(){if(p.nodeName(this,"form"))return!1;p.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=p.nodeName(c,"input")||p.nodeName(c,"button")?c.form:b;d&&!p._data(d,"_submit_attached")&&(p.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),p._data(d,"_submit_attached",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&p.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(p.nodeName(this,"form"))return!1;p.event.remove(this,"._submit")}}),p.support.changeBubbles||(p.event.special.change={setup:function(){if(V.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")p.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),p.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),p.event.simulate("change",this,a,!0)});return!1}p.event.add(this,"beforeactivate._change",function(a){var b=a.target;V.test(b.nodeName)&&!p._data(b,"_change_attached")&&(p.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&p.event.simulate("change",this.parentNode,a,!0)}),p._data(b,"_change_attached",!0))})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){return p.event.remove(this,"._change"),V.test(this.nodeName)}}),p.support.focusinBubbles||p.each({focus:"focusin",blur:"focusout"},function(a,b){var c=0,d=function(a){p.event.simulate(b,a.target,p.event.fix(a),!0)};p.event.special[b]={setup:function(){c++===0&&e.addEventListener(a,d,!0)},teardown:function(){--c===0&&e.removeEventListener(a,d,!0)}}}),p.fn.extend({on:function(a,c,d,e,f){var g,h;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(h in a)this.on(h,c,d,a[h],f);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=ba;else if(!e)return this;return f===1&&(g=e,e=function(a){return p().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=p.guid++)),this.each(function(){p.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){var e,f;if(a&&a.preventDefault&&a.handleObj)return e=a.handleObj,p(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler),this;if(typeof a=="object"){for(f in a)this.off(f,c,a[f]);return this}if(c===!1||typeof c=="function")d=c,c=b;return d===!1&&(d=ba),this.each(function(){p.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){return p(this.context).on(a,this.selector,b,c),this},die:function(a,b){return p(this.context).off(a,this.selector||"**",b),this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a||"**",c)},trigger:function(a,b){return this.each(function(){p.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return p.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||p.guid++,d=0,e=function(c){var e=(p._data(this,"lastToggle"+a.guid)||0)%d;return p._data(this,"lastToggle"+a.guid,e+1),c.preventDefault(),b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),p.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){p.fn[b]=function(a,c){return c==null&&(c=a,a=null),arguments.length>0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e<f;e++)Z(a,b[e],c,d)}function be(a,b,c,d,e,f){var g,h=$.setFilters[b.toLowerCase()];return h||Z.error(b),(a||!(g=e))&&bd(a||"*",d,g=[],e),g.length>0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;a<c;a++)arguments[a]===b&&(g[a]=b)};for(;p<q;p++){s.exec(""),a=f[p],j=[],i=0,k=e;while(g=s.exec(a)){n=s.lastIndex=g.index+g[0].length;if(n>i){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="<select></select>";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="<a name='"+q+"'></a><div name='"+q+"'></div>",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},odd:function(a,b,c){var d=[],e=c?0:1,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},lt:function(a,b,c){return c?a.slice(+b):a.slice(0,+b)},gt:function(a,b,c){return c?a.slice(0,+b+1):a.slice(+b+1)},eq:function(a,b,c){var d=a.splice(+b,1);return c?a:d}}};$.setFilters.nth=$.setFilters.eq,$.filters=$.pseudos,X||($.attrHandle={href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}}),V&&($.order.push("NAME"),$.find.NAME=function(a,b){if(typeof b.getElementsByName!==j)return b.getElementsByName(a)}),Y&&($.order.splice(1,0,"CLASS"),$.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!==j&&!c)return b.getElementsByClassName(a)});try{n.call(i.childNodes,0)[0].nodeType}catch(_){n=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}var ba=Z.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},bb=Z.contains=i.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc=Z.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=bc(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=bc(b);return c};Z.attr=function(a,b){var c,d=ba(a);return d||(b=b.toLowerCase()),$.attrHandle[b]?$.attrHandle[b](a):U||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},Z.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},[0,0].sort(function(){return l=0}),i.compareDocumentPosition?e=function(a,b){return a===b?(k=!0,0):(!a.compareDocumentPosition||!b.compareDocumentPosition?a.compareDocumentPosition:a.compareDocumentPosition(b)&4)?-1:1}:(e=function(a,b){if(a===b)return k=!0,0;if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],g=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return f(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)g.unshift(j),j=j.parentNode;c=e.length,d=g.length;for(var l=0;l<c&&l<d;l++)if(e[l]!==g[l])return f(e[l],g[l]);return l===c?f(a,g[l],-1):f(e[l],b,1)},f=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),Z.uniqueSort=function(a){var b,c=1;if(e){k=l,a.sort(e);if(k)for(;b=a[c];c++)b===a[c-1]&&a.splice(c--,1)}return a};var bl=Z.compile=function(a,b,c){var d,e,f,g=O[a];if(g&&g.context===b)return g;e=bg(a,b,c);for(f=0;d=e[f];f++)e[f]=bj(d,b,c);return g=O[a]=bk(e),g.context=b,g.runs=g.dirruns=0,P.push(a),P.length>$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j<k;j++){p=$.order[j];if(s=L[p].exec(m)){h=$.find[p]((s[1]||"").replace(K,""),q,g);if(h==null)continue;m===r&&(a=a.slice(0,a.length-r.length)+m.replace(L[p],""),a||o.apply(e,n.call(h,0)));break}}}if(a){i=bl(a,b,g),d=i.dirruns++,h==null&&(h=$.find.TAG("*",G.test(a)&&b.parentNode||b));for(j=0;l=h[j];j++)c=i.runs++,i(l,b)&&e.push(l)}return e};h.querySelectorAll&&function(){var a,b=bm,c=/'|\\/g,d=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,e=[],f=[":active"],g=i.matchesSelector||i.mozMatchesSelector||i.webkitMatchesSelector||i.oMatchesSelector||i.msMatchesSelector;T(function(a){a.innerHTML="<select><option selected></option></select>",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="<p test=''></p>",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="<input type='hidden'>",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b<c;b++)if(p.contains(h[b],this))return!0});g=this.pushStack("","find",a);for(b=0,c=this.length;b<c;b++){d=g.length,p.find(a,this[b],g);if(b>0)for(e=d;e<g.length;e++)for(f=0;f<d;f++)if(g[f]===g[e]){g.splice(e--,1);break}}return g},has:function(a){var b,c=p(a,this),d=c.length;return this.filter(function(){for(b=0;b<d;b++)if(p.contains(this,c[b]))return!0})},not:function(a){return this.pushStack(bj(this,a,!1),"not",a)},filter:function(a){return this.pushStack(bj(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?bf.test(a)?p(a,this.context).index(this[0])>=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d<e;d++){c=this[d];while(c&&c.ownerDocument&&c!==b&&c.nodeType!==11){if(g?g.index(c)>-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/<tbody/i,br=/<|&#?\w+;/,bs=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,bu=new RegExp("<(?:"+bl+")[\\s/>]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,bz={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X<div>","</div>"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(f){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){return bh(this[0])?this.length?this.pushStack(p(p.isFunction(a)?a():a),"replaceWith",a):this:p.isFunction(a)?this.each(function(b){var c=p(this),d=c.html();c.replaceWith(a.call(this,b,d))}):(typeof a!="string"&&(a=p(a).detach()),this.each(function(){var b=this.nextSibling,c=this.parentNode;p(this).remove(),b?p(b).before(a):p(c).append(a)}))},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){a=[].concat.apply([],a);var e,f,g,h,i=0,j=a[0],k=[],l=this.length;if(!p.support.checkClone&&l>1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i<l;i++)d.call(c&&p.nodeName(this[i],"table")?bC(this[i],"tbody"):this[i],i===h?g:p.clone(g,!0,!0))}g=f=null,k.length&&p.each(k,function(a,b){b.src?p.ajax?p.ajax({url:b.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):p.error("no ajax"):p.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),p.buildFragment=function(a,c,d){var f,g,h,i=a[0];return c=c||e,c=(c[0]||c).ownerDocument||c[0]||c,typeof c.createDocumentFragment=="undefined"&&(c=e),a.length===1&&typeof i=="string"&&i.length<512&&c===e&&i.charAt(0)==="<"&&!bt.test(i)&&(p.support.checkClone||!bw.test(i))&&(p.support.html5Clone||!bu.test(i))&&(g=!0,f=p.fragments[i],h=f!==b),f||(f=c.createDocumentFragment(),p.clean(a,c,f,d),g&&(p.fragments[i]=h&&f)),{fragment:f,cacheable:g}},p.fragments={},p.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){p.fn[a]=function(c){var d,e=0,f=[],g=p(c),h=g.length,i=this.length===1&&this[0].parentNode;if((i==null||i&&i.nodeType===11&&i.childNodes.length===1)&&h===1)return g[b](this[0]),this;for(;e<h;e++)d=(e>0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1></$2>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]==="<table>"&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("<div>").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d<e;d++)c=a[d],cS[c]=cS[c]||[],cS[c].unshift(b)},prefilter:function(a,b){b?cR.unshift(a):cR.push(a)}}),p.Tween=cY,cY.prototype={constructor:cY,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(p.cssNumber[c]?"":"px")},cur:function(){var a=cY.propHooks[this.prop];return a&&a.get?a.get(this):cY.propHooks._default.get(this)},run:function(a){var b,c=cY.propHooks[this.prop];return this.pos=b=p.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration),this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):cY.propHooks._default.set(this),this}},cY.prototype.init.prototype=cY.prototype,cY.propHooks={_default:{get:function(a){var b;return a.elem[a.prop]==null||!!a.elem.style&&a.elem.style[a.prop]!=null?(b=p.css(a.elem,a.prop,!1,""),!b||b==="auto"?0:b):a.elem[a.prop]},set:function(a){p.fx.step[a.prop]?p.fx.step[a.prop](a):a.elem.style&&(a.elem.style[p.cssProps[a.prop]]!=null||p.cssHooks[a.prop])?p.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},cY.propHooks.scrollTop=cY.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},p.each(["toggle","show","hide"],function(a,b){var c=p.fn[b];p.fn[b]=function(d,e,f){return d==null||typeof d=="boolean"||!a&&p.isFunction(d)&&p.isFunction(e)?c.apply(this,arguments):this.animate(cZ(b,!0),d,e,f)}}),p.fn.extend({fadeTo:function(a,b,c,d){return this.filter(bY).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=p.isEmptyObject(a),f=p.speed(b,c,d),g=function(){var b=cV(this,p.extend({},a),f);e&&b.stop(!0)};return e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,c,d){var e=function(a){var b=a.stop;delete a.stop,b(d)};return typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,c=a!=null&&a+"queueHooks",f=p.timers,g=p._data(this);if(c)g[c]&&g[c].stop&&e(g[c]);else for(c in g)g[c]&&g[c].stop&&cQ.test(c)&&e(g[c]);for(c=f.length;c--;)f[c].elem===this&&(a==null||f[c].queue===a)&&(f[c].anim.stop(d),b=!1,f.splice(c,1));(b||!d)&&p.dequeue(this,a)})}}),p.each({slideDown:cZ("show"),slideUp:cZ("hide"),slideToggle:cZ("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){p.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),p.speed=function(a,b,c){var d=a&&typeof a=="object"?p.extend({},a):{complete:c||!c&&b||p.isFunction(a)&&a,duration:a,easing:c&&b||b&&!p.isFunction(b)&&b};d.duration=p.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in p.fx.speeds?p.fx.speeds[d.duration]:p.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";return d.old=d.complete,d.complete=function(){p.isFunction(d.old)&&d.old.call(this),d.queue&&p.dequeue(this,d.queue)},d},p.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},p.timers=[],p.fx=cY.prototype.init,p.fx.tick=function(){var a,b=p.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||p.fx.stop()},p.fx.timer=function(a){a()&&p.timers.push(a)&&!cN&&(cN=setInterval(p.fx.tick,p.fx.interval))},p.fx.interval=13,p.fx.stop=function(){clearInterval(cN),cN=null},p.fx.speeds={slow:600,fast:200,_default:400},p.fx.step={},p.expr&&p.expr.filters&&(p.expr.filters.animated=function(a){return p.grep(p.timers,function(b){return a===b.elem}).length});var c$=/^(?:body|html)$/i;p.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){p.offset.setOffset(this,a,b)});var c,d,e,f,g,h,i,j,k,l,m=this[0],n=m&&m.ownerDocument;if(!n)return;return(e=n.body)===m?p.offset.bodyOffset(m):(d=n.documentElement,p.contains(d,m)?(c=m.getBoundingClientRect(),f=c_(n),g=d.clientTop||e.clientTop||0,h=d.clientLeft||e.clientLeft||0,i=f.pageYOffset||d.scrollTop,j=f.pageXOffset||d.scrollLeft,k=c.top+i-g,l=c.left+j-h,{top:k,left:l}):{top:0,left:0})},p.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;return p.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(p.css(a,"marginTop"))||0,c+=parseFloat(p.css(a,"marginLeft"))||0),{top:b,left:c}},setOffset:function(a,b,c){var d=p.css(a,"position");d==="static"&&(a.style.position="relative");var e=p(a),f=e.offset(),g=p.css(a,"top"),h=p.css(a,"left"),i=(d==="absolute"||d==="fixed")&&p.inArray("auto",[g,h])>-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/jsoneditor.min.js b/profiles/killbill/src/main/webapp/lib/jsoneditor.min.js
new file mode 100644
index 0000000..343397f
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/jsoneditor.min.js
@@ -0,0 +1,11 @@
+/*! JSON Editor v0.7.22 - JSON Schema -> HTML Editor
+ * By Jeremy Dorn - https://github.com/jdorn/json-editor/
+ * Released under the MIT license
+ *
+ * Date: 2015-08-12
+ */
+!function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function(a){function d(){!b&&this.init&&this.init.apply(this,arguments)}var e=this.prototype;b=!0;var f=new this;b=!1;for(var g in a)f[g]="function"==typeof a[g]&&"function"==typeof e[g]&&c.test(a[g])?function(a,b){return function(){var c=this._super;this._super=e[a];var d=b.apply(this,arguments);return this._super=c,d}}(g,a[g]):a[g];return d.prototype=f,d.prototype.constructor=d,d.extend=arguments.callee,d},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return"object"!=typeof a||a.nodeType||null!==a&&a===a.window?!1:a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema),a.validator=new f.Validator(a);var b=a.getEditorClass(a.schema);a.root=a.createEditor(b,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);return e&&f.defaults.editors[e]?(b=e,!1):void 0}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){return this.editors?this.editors[a]:void 0},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(i){throw window.console.log(i),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a["extends"]){if(Array.isArray(a["extends"]))for(b=0;b<a["extends"].length;b++)f=this.extendSchemas(f,this.expandSchema(a["extends"][b]));else f=this.extendSchemas(f,this.expandSchema(a["extends"]));delete f["extends"]}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"===a&&"object"==typeof c&&Array.isArray(c)?f.required=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return-1!==b[a].indexOf(c)}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),f.type=c.filter(function(a){return-1!==b.type.indexOf(a)}),1===f.type.length&&"string"==typeof f.type[0]&&(f.type=f.type[0])):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options={},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a["enum"]){for(g=!1,h=0;h<a["enum"].length;h++)l===JSON.stringify(a["enum"][h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a["extends"])for(h=0;h<a["extends"].length;h++)k=k.concat(this._validateSchema(a["extends"][h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b)(a.multipleOf||a.divisibleBy)&&(g=b/(a.multipleOf||a.divisibleBy),g!==Math.floor(g)&&k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[a.multipleOf||a.divisibleBy])})),a.hasOwnProperty("maximum")&&(a.exclusiveMaximum&&b>=a.maximum?k.push({path:e,property:"maximum",message:this.translate("error_maximum_excl",[a.maximum])}):!a.exclusiveMaximum&&b>a.maximum&&k.push({path:e,property:"maximum",message:this.translate("error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(a.exclusiveMinimum&&b<=a.minimum?k.push({path:e,property:"minimum",message:this.translate("error_minimum_excl",[a.minimum])}):!a.exclusiveMinimum&&b<a.minimum&&k.push({path:e,property:"minimum",message:this.translate("error_minimum_incl",[a.minimum])}));else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern")}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var o={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),o[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}o[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var p={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(p[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var q=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&q.test(i)&&(p[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!p[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"===a?null===b:!0:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)?c=[b[0]].concat(b[1].split(".")):(c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#")),d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine);if("image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var g=document.createElement("img");this.theme.createImageLink(b,c,g),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),g.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var h=document.createElement(e);h.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,h.setAttribute("src",d)})}else b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema["default"])return this.schema["default"];if(this.schema["enum"])return this.schema["enum"][0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:this.jsoneditor.options.required_by_default?!0:!1},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.descripton:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(b){window.console.error(b)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors["null"]=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{plugins:"html"===b.input_type?"xhtml":"bbcode",emoticonsEnabled:!1,width:"100%",height:300},f.plugins.sceditor,b.options.sceditor_options||{}),window.jQuery(b.input).sceditor(a),b.sceditor_instance=window.jQuery(b.input).sceditor("instance"),b.sceditor_instance.blur(function(){var a=window.jQuery("<div>"+b.sceditor_instance.val()+"</div>");window.jQuery("#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf",a).remove(),b.input.value=a.html(),b.value=b.input.value,b.is_dirty=!0,b.onChange(!0)});else if("markdown"===this.input_type&&window.EpicEditor)this.epiceditor_container=document.createElement("div"),this.input.parentNode.insertBefore(this.epiceditor_container,this.input),this.input.style.display="none",a=c({},f.plugins.epiceditor,{container:this.epiceditor_container,clientSideStorage:!1}),this.epiceditor=new window.EpicEditor(a).load(),this.epiceditor.importFile(null,this.getValue()),this.epiceditor.on("update",function(){var a=b.epiceditor.exportFile();b.input.value=a,b.value=a,b.is_dirty=!0,b.onChange(!0);
+});else if(window.ace){var d=this.input_type;("cpp"===d||"c++"===d||"c"===d)&&(d="c_cpp"),this.ace_container=document.createElement("div"),this.ace_container.style.width="100%",this.ace_container.style.position="relative",this.ace_container.style.height="400px",this.input.parentNode.insertBefore(this.ace_container,this.input),this.input.style.display="none",this.ace_editor=window.ace.edit(this.ace_container),this.ace_editor.setValue(this.getValue()),f.plugins.ace.theme&&this.ace_editor.setTheme("ace/theme/"+f.plugins.ace.theme),d=window.ace.require("ace/mode/"+d),d&&this.ace_editor.getSession().setMode(new d.Mode),this.ace_editor.on("change",function(){var a=b.ace_editor.getValue();b.input.value=a,b.refreshValue(),b.is_dirty=!0,b.onChange(!0)})}b.theme.afterInputReady(b.input)},refreshValue:function(){this.value=this.input.value,"string"!=typeof this.value&&(this.value=""),this.serialized=this.value},destroy:function(){this.sceditor_instance?this.sceditor_instance.destroy():this.epiceditor?this.epiceditor.unload():this.ace_editor&&this.ace_editor.destroy(),this.template=null,this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this._super()},sanitize:function(a){return a},onWatchedFieldChange:function(){var a;this.template&&(a=this.getWatchedFieldValues(),this.setValue(this.template(a),!1,!0)),this._super()},showValidationErrors:function(a){var b=this;if("always"===this.jsoneditor.options.show_errors);else if(!this.is_dirty&&this.previous_error_setting===this.jsoneditor.options.show_errors)return;this.previous_error_setting=this.jsoneditor.options.show_errors;var c=[];d(a,function(a,d){d.path===b.path&&c.push(d.message)}),c.length?this.theme.addInputError(this.input,c.join(". ")+"."):this.theme.removeInputError(this.input)}}),f.defaults.editors.number=f.defaults.editors.string.extend({sanitize:function(a){return(a+"").replace(/[^0-9\.\-eE]/g,"")},getNumColumns:function(){return 2},getValue:function(){return 1*this.value}}),f.defaults.editors.integer=f.defaults.editors.number.extend({sanitize:function(a){return a+="",a.replace(/[^0-9\-]/g,"")},getNumColumns:function(){return 2}}),f.defaults.editors.object=f.AbstractEditor.extend({getDefault:function(){return c({},this.schema["default"]||{})},getChildEditors:function(){return this.editors},register:function(){if(this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].register()},unregister:function(){if(this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].unregister()},getNumColumns:function(){return Math.max(Math.min(12,this.maxwidth),3)},enable:function(){if(this.editjson_button&&(this.editjson_button.disabled=!1),this.addproperty_button&&(this.addproperty_button.disabled=!1),this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].enable()},disable:function(){if(this.editjson_button&&(this.editjson_button.disabled=!0),this.addproperty_button&&(this.addproperty_button.disabled=!0),this.hideEditJSON(),this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].disable()},layoutEditors:function(){var a,b,c=this;if(this.row_container){this.property_order=Object.keys(this.editors),this.property_order=this.property_order.sort(function(a,b){var d=c.editors[a].schema.propertyOrder,e=c.editors[b].schema.propertyOrder;return"number"!=typeof d&&(d=1e3),"number"!=typeof e&&(e=1e3),d-e});var e;if("grid"===this.format){var f=[];for(d(this.property_order,function(a,b){var d=c.editors[b];if(!d.property_removed){for(var e=!1,g=d.options.hidden?0:d.options.grid_columns||d.getNumColumns(),h=d.options.hidden?0:d.container.offsetHeight,i=0;i<f.length;i++)f[i].width+g<=12&&(!h||.5*f[i].minh<h&&2*f[i].maxh>h)&&(e=i);e===!1&&(f.push({width:0,minh:999999,maxh:0,editors:[]}),e=f.length-1),f[e].editors.push({key:b,width:g,height:h}),f[e].width+=g,f[e].minh=Math.min(f[e].minh,h),f[e].maxh=Math.max(f[e].maxh,h)}}),a=0;a<f.length;a++)if(f[a].width<12){var g=!1,h=0;for(b=0;b<f[a].editors.length;b++)g===!1?g=b:f[a].editors[b].width>f[a].editors[g].width&&(g=b),f[a].editors[b].width*=12/f[a].width,f[a].editors[b].width=Math.floor(f[a].editors[b].width),h+=f[a].editors[b].width;12>h&&(f[a].editors[g].width+=12-h),f[a].width=12}if(this.layout===JSON.stringify(f))return!1;for(this.layout=JSON.stringify(f),e=document.createElement("div"),a=0;a<f.length;a++){var i=this.theme.getGridRow();for(e.appendChild(i),b=0;b<f[a].editors.length;b++){var j=f[a].editors[b].key,k=this.editors[j];k.options.hidden?k.container.style.display="none":this.theme.setGridColumnSize(k.container,f[a].editors[b].width),i.appendChild(k.container)}}}else e=document.createElement("div"),d(this.property_order,function(a,b){var d=c.editors[b];if(!d.property_removed){var f=c.theme.getGridRow();e.appendChild(f),d.options.hidden?d.container.style.display="none":c.theme.setGridColumnSize(d.container,12),f.appendChild(d.container)}});this.row_container.innerHTML="",this.row_container.appendChild(e)}},getPropertySchema:function(a){var b=this.schema.properties[a]||{};b=c({},b);var d=this.schema.properties[a]?!0:!1;if(this.schema.patternProperties)for(var e in this.schema.patternProperties)if(this.schema.patternProperties.hasOwnProperty(e)){var f=new RegExp(e);f.test(a)&&(b.allOf=b.allOf||[],b.allOf.push(this.schema.patternProperties[e]),d=!0)}return!d&&this.schema.additionalProperties&&"object"==typeof this.schema.additionalProperties&&(b=c({},this.schema.additionalProperties)),b},preBuild:function(){this._super(),this.editors={},this.cached_editors={};var a=this;if(this.format=this.options.layout||this.options.object_layout||this.schema.format||this.jsoneditor.options.object_layout||"normal",this.schema.properties=this.schema.properties||{},this.minwidth=0,this.maxwidth=0,this.options.table_row)d(this.schema.properties,function(b,c){var d=a.jsoneditor.getEditorClass(c);a.editors[b]=a.jsoneditor.createEditor(d,{jsoneditor:a.jsoneditor,schema:c,path:a.path+"."+b,parent:a,compact:!0,required:!0}),a.editors[b].preBuild();var e=a.editors[b].options.hidden?0:a.editors[b].options.grid_columns||a.editors[b].getNumColumns();a.minwidth+=e,a.maxwidth+=e}),this.no_link_holder=!0;else{if(this.options.table)throw"Not supported yet";this.defaultProperties=this.schema.defaultProperties||Object.keys(this.schema.properties),a.maxwidth+=1,d(this.defaultProperties,function(b,c){a.addObjectProperty(c,!0),a.editors[c]&&(a.minwidth=Math.max(a.minwidth,a.editors[c].options.grid_columns||a.editors[c].getNumColumns()),a.maxwidth+=a.editors[c].options.grid_columns||a.editors[c].getNumColumns())})}this.property_order=Object.keys(this.editors),this.property_order=this.property_order.sort(function(b,c){var d=a.editors[b].schema.propertyOrder,e=a.editors[c].schema.propertyOrder;return"number"!=typeof d&&(d=1e3),"number"!=typeof e&&(e=1e3),d-e})},build:function(){var a=this;if(this.options.table_row)this.editor_holder=this.container,d(this.editors,function(b,c){var d=a.theme.getTableCell();a.editor_holder.appendChild(d),c.setContainer(d),c.build(),c.postBuild(),a.editors[b].options.hidden&&(d.style.display="none"),a.editors[b].options.input_width&&(d.style.width=a.editors[b].options.input_width)});else{if(this.options.table)throw"Not supported yet";this.header=document.createElement("span"),this.header.textContent=this.getTitle(),this.title=this.theme.getHeader(this.header),this.container.appendChild(this.title),this.container.style.position="relative",this.editjson_holder=this.theme.getModal(),this.editjson_textarea=this.theme.getTextareaInput(),this.editjson_textarea.style.height="170px",this.editjson_textarea.style.width="300px",this.editjson_textarea.style.display="block",this.editjson_save=this.getButton("Save","save","Save"),this.editjson_save.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.saveJSON()}),this.editjson_cancel=this.getButton("Cancel","cancel","Cancel"),this.editjson_cancel.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.hideEditJSON()}),this.editjson_holder.appendChild(this.editjson_textarea),this.editjson_holder.appendChild(this.editjson_save),this.editjson_holder.appendChild(this.editjson_cancel),this.addproperty_holder=this.theme.getModal(),this.addproperty_list=document.createElement("div"),this.addproperty_list.style.width="295px",this.addproperty_list.style.maxHeight="160px",this.addproperty_list.style.padding="5px 0",this.addproperty_list.style.overflowY="auto",this.addproperty_list.style.overflowX="hidden",this.addproperty_list.style.paddingLeft="5px",this.addproperty_list.setAttribute("class","property-selector"),this.addproperty_add=this.getButton("add","add","add"),this.addproperty_input=this.theme.getFormInputField("text"),this.addproperty_input.setAttribute("placeholder","Property name..."),this.addproperty_input.style.width="220px",this.addproperty_input.style.marginBottom="0",this.addproperty_input.style.display="inline-block",this.addproperty_add.addEventListener("click",function(b){if(b.preventDefault(),b.stopPropagation(),a.addproperty_input.value){if(a.editors[a.addproperty_input.value])return void window.alert("there is already a property with that name");a.addObjectProperty(a.addproperty_input.value),a.editors[a.addproperty_input.value]&&a.editors[a.addproperty_input.value].disable(),a.onChange(!0)}}),this.addproperty_holder.appendChild(this.addproperty_list),this.addproperty_holder.appendChild(this.addproperty_input),this.addproperty_holder.appendChild(this.addproperty_add);var b=document.createElement("div");b.style.clear="both",this.addproperty_holder.appendChild(b),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.error_holder=document.createElement("div"),this.container.appendChild(this.error_holder),this.editor_holder=this.theme.getIndentedPanel(),this.editor_holder.style.paddingBottom="0",this.container.appendChild(this.editor_holder),this.row_container=this.theme.getGridContainer(),this.editor_holder.appendChild(this.row_container),d(this.editors,function(b,c){var d=a.theme.getGridColumn();a.row_container.appendChild(d),c.setContainer(d),c.build(),c.postBuild()}),this.title_controls=this.theme.getHeaderButtonHolder(),this.editjson_controls=this.theme.getHeaderButtonHolder(),this.addproperty_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.title.appendChild(this.editjson_controls),this.title.appendChild(this.addproperty_controls),this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls.appendChild(this.toggle_button),this.toggle_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.collapsed?(a.editor_holder.style.display="",a.collapsed=!1,a.setButtonText(a.toggle_button,"","collapse","Collapse")):(a.editor_holder.style.display="none",a.collapsed=!0,a.setButtonText(a.toggle_button,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none"),this.editjson_button=this.getButton("JSON","edit","Edit JSON"),this.editjson_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.toggleEditJSON()}),this.editjson_controls.appendChild(this.editjson_button),this.editjson_controls.appendChild(this.editjson_holder),this.schema.options&&"undefined"!=typeof this.schema.options.disable_edit_json?this.schema.options.disable_edit_json&&(this.editjson_button.style.display="none"):this.jsoneditor.options.disable_edit_json&&(this.editjson_button.style.display="none"),this.addproperty_button=this.getButton("Properties","edit","Object Properties"),this.addproperty_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.toggleAddProperty()}),this.addproperty_controls.appendChild(this.addproperty_button),this.addproperty_controls.appendChild(this.addproperty_holder),this.refreshAddProperties()}this.options.table_row?(this.editor_holder=this.container,d(this.property_order,function(b,c){a.editor_holder.appendChild(a.editors[c].container)})):(this.layoutEditors(),this.layoutEditors())},showEditJSON:function(){this.editjson_holder&&(this.hideAddProperty(),this.editjson_holder.style.left=this.editjson_button.offsetLeft+"px",this.editjson_holder.style.top=this.editjson_button.offsetTop+this.editjson_button.offsetHeight+"px",this.editjson_textarea.value=JSON.stringify(this.getValue(),null,2),this.disable(),this.editjson_holder.style.display="",this.editjson_button.disabled=!1,this.editing_json=!0)},hideEditJSON:function(){this.editjson_holder&&this.editing_json&&(this.editjson_holder.style.display="none",this.enable(),this.editing_json=!1)},saveJSON:function(){if(this.editjson_holder)try{var a=JSON.parse(this.editjson_textarea.value);this.setValue(a),this.hideEditJSON()}catch(b){throw window.alert("invalid JSON"),b}},toggleEditJSON:function(){this.editing_json?this.hideEditJSON():this.showEditJSON()},insertPropertyControlUsingPropertyOrder:function(a,b,c){var d;this.schema.properties[a]&&(d=this.schema.properties[a].propertyOrder),"number"!=typeof d&&(d=1e3),b.propertyOrder=d;for(var e=0;e<c.childNodes.length;e++){var f=c.childNodes[e];if(b.propertyOrder<f.propertyOrder){this.addproperty_list.insertBefore(b,f),b=null;break}}b&&this.addproperty_list.appendChild(b)},addPropertyCheckbox:function(a){var b,c,d,e,f=this;return b=f.theme.getCheckbox(),b.style.width="auto",d=this.schema.properties[a]&&this.schema.properties[a].title?this.schema.properties[a].title:a,c=f.theme.getCheckboxLabel(d),e=f.theme.getFormControl(c,b),e.style.paddingBottom=e.style.marginBottom=e.style.paddingTop=e.style.marginTop=0,e.style.height="auto",this.insertPropertyControlUsingPropertyOrder(a,e,this.addproperty_list),b.checked=a in this.editors,b.addEventListener("change",function(){b.checked?f.addObjectProperty(a):f.removeObjectProperty(a),f.onChange(!0)}),f.addproperty_checkboxes[a]=b,b},showAddProperty:function(){this.addproperty_holder&&(this.hideEditJSON(),this.addproperty_holder.style.left=this.addproperty_button.offsetLeft+"px",this.addproperty_holder.style.top=this.addproperty_button.offsetTop+this.addproperty_button.offsetHeight+"px",this.disable(),this.adding_property=!0,this.addproperty_button.disabled=!1,this.addproperty_holder.style.display="",this.refreshAddProperties())},hideAddProperty:function(){this.addproperty_holder&&this.adding_property&&(this.addproperty_holder.style.display="none",this.enable(),this.adding_property=!1)},toggleAddProperty:function(){this.adding_property?this.hideAddProperty():this.showAddProperty()},removeObjectProperty:function(a){this.editors[a]&&(this.editors[a].unregister(),delete this.editors[a],this.refreshValue(),this.layoutEditors())},addObjectProperty:function(a,b){var c=this;if(!this.editors[a]){if(this.cached_editors[a]){if(this.editors[a]=this.cached_editors[a],b)return;this.editors[a].register()}else{if(!(this.canHaveAdditionalProperties()||this.schema.properties&&this.schema.properties[a]))return;var d=c.getPropertySchema(a),e=c.jsoneditor.getEditorClass(d);if(c.editors[a]=c.jsoneditor.createEditor(e,{jsoneditor:c.jsoneditor,schema:d,path:c.path+"."+a,parent:c}),c.editors[a].preBuild(),!b){var f=c.theme.getChildEditorHolder();c.editor_holder.appendChild(f),c.editors[a].setContainer(f),c.editors[a].build(),c.editors[a].postBuild()}c.cached_editors[a]=c.editors[a]}b||(c.refreshValue(),c.layoutEditors())}},onChildEditorChange:function(a){this.refreshValue(),this._super(a)},canHaveAdditionalProperties:function(){return"boolean"==typeof this.schema.additionalProperties?this.schema.additionalProperties:!this.jsoneditor.options.no_additional_properties},destroy:function(){d(this.cached_editors,function(a,b){b.destroy()}),this.editor_holder&&(this.editor_holder.innerHTML=""),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.error_holder&&this.error_holder.parentNode&&this.error_holder.parentNode.removeChild(this.error_holder),this.editors=null,this.cached_editors=null,this.editor_holder&&this.editor_holder.parentNode&&this.editor_holder.parentNode.removeChild(this.editor_holder),this.editor_holder=null,this._super()},getValue:function(){var a=this._super();if(this.jsoneditor.options.remove_empty_properties||this.options.remove_empty_properties)for(var b in a)a.hasOwnProperty(b)&&(a[b]||delete a[b]);return a},refreshValue:function(){this.value={};for(var a in this.editors)this.editors.hasOwnProperty(a)&&(this.value[a]=this.editors[a].getValue());this.adding_property&&this.refreshAddProperties()},refreshAddProperties:function(){if(this.options.disable_properties||this.options.disable_properties!==!1&&this.jsoneditor.options.disable_properties)return void(this.addproperty_controls.style.display="none");var a,b=!1,c=!1,d=0,e=!1;for(a in this.editors)this.editors.hasOwnProperty(a)&&d++;b=this.canHaveAdditionalProperties()&&!("undefined"!=typeof this.schema.maxProperties&&d>=this.schema.maxProperties),this.addproperty_checkboxes&&(this.addproperty_list.innerHTML=""),this.addproperty_checkboxes={};for(a in this.cached_editors)this.cached_editors.hasOwnProperty(a)&&(this.addPropertyCheckbox(a),this.isRequired(this.cached_editors[a])&&a in this.editors&&(this.addproperty_checkboxes[a].disabled=!0),"undefined"!=typeof this.schema.minProperties&&d<=this.schema.minProperties?(this.addproperty_checkboxes[a].disabled=this.addproperty_checkboxes[a].checked,this.addproperty_checkboxes[a].checked||(e=!0)):a in this.editors?(e=!0,c=!0):b||this.schema.properties.hasOwnProperty(a)?(this.addproperty_checkboxes[a].disabled=!1,e=!0):this.addproperty_checkboxes[a].disabled=!0);this.canHaveAdditionalProperties()&&(e=!0);for(a in this.schema.properties)this.schema.properties.hasOwnProperty(a)&&(this.cached_editors[a]||(e=!0,this.addPropertyCheckbox(a)));e?this.canHaveAdditionalProperties()?b?this.addproperty_add.disabled=!1:this.addproperty_add.disabled=!0:(this.addproperty_add.style.display="none",this.addproperty_input.style.display="none"):(this.hideAddProperty(),this.addproperty_controls.style.display="none")},isRequired:function(a){return"boolean"==typeof a.schema.required?a.schema.required:Array.isArray(this.schema.required)?this.schema.required.indexOf(a.key)>-1:this.jsoneditor.options.required_by_default?!0:!1},setValue:function(a,b){var c=this;a=a||{},("object"!=typeof a||Array.isArray(a))&&(a={}),d(this.cached_editors,function(d,e){"undefined"!=typeof a[d]?(c.addObjectProperty(d),e.setValue(a[d],b)):b||c.isRequired(e)?e.setValue(e.getDefault(),b):c.removeObjectProperty(d)}),d(a,function(a,d){c.cached_editors[a]||(c.addObjectProperty(a),c.editors[a]&&c.editors[a].setValue(d,b))}),this.refreshValue(),this.layoutEditors(),this.onChange()},showValidationErrors:function(a){var b=this,c=[],e=[];if(d(a,function(a,d){d.path===b.path?c.push(d):e.push(d)}),this.error_holder)if(c.length){this.error_holder.innerHTML="",this.error_holder.style.display="",d(c,function(a,c){b.error_holder.appendChild(b.theme.getErrorMessage(c.message))})}else this.error_holder.style.display="none";this.options.table_row&&(c.length?this.theme.addTableRowError(this.container):this.theme.removeTableRowError(this.container)),d(this.editors,function(a,b){b.showValidationErrors(e)})}}),f.defaults.editors.array=f.AbstractEditor.extend({getDefault:function(){return this.schema["default"]||[]},register:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].register()},unregister:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].unregister()},getNumColumns:function(){var a=this.getItemInfo(0);return this.tabs_holder?Math.max(Math.min(12,a.width+2),4):a.width},enable:function(){if(this.add_row_button&&(this.add_row_button.disabled=!1),this.remove_all_rows_button&&(this.remove_all_rows_button.disabled=!1),this.delete_last_row_button&&(this.delete_last_row_button.disabled=!1),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].enable(),this.rows[a].moveup_button&&(this.rows[a].moveup_button.disabled=!1),this.rows[a].movedown_button&&(this.rows[a].movedown_button.disabled=!1),this.rows[a].delete_button&&(this.rows[a].delete_button.disabled=!1);this._super()},disable:function(){if(this.add_row_button&&(this.add_row_button.disabled=!0),this.remove_all_rows_button&&(this.remove_all_rows_button.disabled=!0),this.delete_last_row_button&&(this.delete_last_row_button.disabled=!0),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].disable(),this.rows[a].moveup_button&&(this.rows[a].moveup_button.disabled=!0),this.rows[a].movedown_button&&(this.rows[a].movedown_button.disabled=!0),this.rows[a].delete_button&&(this.rows[a].delete_button.disabled=!0);this._super()},preBuild:function(){this._super(),this.rows=[],this.row_cache=[],this.hide_delete_buttons=this.options.disable_array_delete||this.jsoneditor.options.disable_array_delete,this.hide_move_buttons=this.options.disable_array_reorder||this.jsoneditor.options.disable_array_reorder,this.hide_add_button=this.options.disable_array_add||this.jsoneditor.options.disable_array_add},build:function(){this.options.compact?(this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls),this.row_holder=document.createElement("div"),this.panel.appendChild(this.row_holder)):(this.header=document.createElement("span"),this.header.textContent=this.getTitle(),this.title=this.theme.getHeader(this.header,this.isRequired()),this.container.appendChild(this.title),this.title_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.error_holder=document.createElement("div"),this.container.appendChild(this.error_holder),"tabs"===this.schema.format?(this.controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.controls),this.tabs_holder=this.theme.getTabHolder(),this.container.appendChild(this.tabs_holder),this.row_holder=this.theme.getTabContentHolder(this.tabs_holder),this.active_tab=null):(this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.row_holder=document.createElement("div"),this.panel.appendChild(this.row_holder),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls))),this.addControls()},onChildEditorChange:function(a){this.refreshValue(),this.refreshTabs(!0),this._super(a)},getItemTitle:function(){if(!this.item_title)if(this.schema.items&&!Array.isArray(this.schema.items)){var a=this.jsoneditor.expandRefs(this.schema.items);this.item_title=a.title||"item"}else this.item_title="item";return this.item_title},getItemSchema:function(a){return Array.isArray(this.schema.items)?a>=this.schema.items.length?this.schema.additionalItems===!0?{}:this.schema.additionalItems?c({},this.schema.additionalItems):void 0:c({},this.schema.items[a]):this.schema.items?c({},this.schema.items):{}},getItemInfo:function(a){var b=this.getItemSchema(a);this.item_info=this.item_info||{};var c=JSON.stringify(b);return"undefined"!=typeof this.item_info[c]?this.item_info[c]:(b=this.jsoneditor.expandRefs(b),this.item_info[c]={title:b.title||"item","default":b["default"],width:12,child_editors:b.properties||b.items},this.item_info[c])},getElementEditor:function(a){var b=this.getItemInfo(a),c=this.getItemSchema(a);c=this.jsoneditor.expandRefs(c),c.title=b.title+" "+(a+1);var d,e=this.jsoneditor.getEditorClass(c);d=this.tabs_holder?this.theme.getTabContent():b.child_editors?this.theme.getChildEditorHolder():this.theme.getIndentedPanel(),this.row_holder.appendChild(d);var f=this.jsoneditor.createEditor(e,{jsoneditor:this.jsoneditor,schema:c,container:d,path:this.path+"."+a,parent:this,required:!0});return f.preBuild(),f.build(),f.postBuild(),f.title_controls||(f.array_controls=this.theme.getButtonHolder(),d.appendChild(f.array_controls)),f},destroy:function(){this.empty(!0),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.row_holder&&this.row_holder.parentNode&&this.row_holder.parentNode.removeChild(this.row_holder),this.controls&&this.controls.parentNode&&this.controls.parentNode.removeChild(this.controls),this.panel&&this.panel.parentNode&&this.panel.parentNode.removeChild(this.panel),this.rows=this.row_cache=this.title=this.description=this.row_holder=this.panel=this.controls=null,this._super()},empty:function(a){if(this.rows){var b=this;d(this.rows,function(c,d){a&&(d.tab&&d.tab.parentNode&&d.tab.parentNode.removeChild(d.tab),b.destroyRow(d,!0),b.row_cache[c]=null),b.rows[c]=null}),b.rows=[],a&&(b.row_cache=[])}},destroyRow:function(a,b){var c=a.container;b?(a.destroy(),c.parentNode&&c.parentNode.removeChild(c),a.tab&&a.tab.parentNode&&a.tab.parentNode.removeChild(a.tab)):(a.tab&&(a.tab.style.display="none"),c.style.display="none",a.unregister())},getMax:function(){return Array.isArray(this.schema.items)&&this.schema.additionalItems===!1?Math.min(this.schema.items.length,this.schema.maxItems||1/0):this.schema.maxItems||1/0},refreshTabs:function(a){var b=this;d(this.rows,function(c,d){d.tab&&(a?d.tab_text.textContent=d.getHeaderText():d.tab===b.active_tab?(b.theme.markTabActive(d.tab),d.container.style.display=""):(b.theme.markTabInactive(d.tab),d.container.style.display="none"))})},setValue:function(a,b){a=a||[],Array.isArray(a)||(a=[a]);var c=JSON.stringify(a);if(c!==this.serialized){if(this.schema.minItems)for(;a.length<this.schema.minItems;)a.push(this.getItemInfo(a.length)["default"]);this.getMax()&&a.length>this.getMax()&&(a=a.slice(0,this.getMax()));var e=this;d(a,function(a,c){e.rows[a]?e.rows[a].setValue(c,b):e.row_cache[a]?(e.rows[a]=e.row_cache[a],e.rows[a].setValue(c,b),e.rows[a].container.style.display="",e.rows[a].tab&&(e.rows[a].tab.style.display=""),e.rows[a].register()):e.addRow(c,b)});for(var f=a.length;f<e.rows.length;f++)e.destroyRow(e.rows[f]),e.rows[f]=null;e.rows=e.rows.slice(0,a.length);var g=null;d(e.rows,function(a,b){return b.tab===e.active_tab?(g=b.tab,!1):void 0}),!g&&e.rows.length&&(g=e.rows[0].tab),e.active_tab=g,e.refreshValue(b),e.refreshTabs(!0),e.refreshTabs(),e.onChange()}},refreshValue:function(a){var b=this,c=this.value?this.value.length:0;if(this.value=[],d(this.rows,function(a,c){b.value[a]=c.getValue()}),c!==this.value.length||a){var e=this.schema.minItems&&this.schema.minItems>=this.rows.length;d(this.rows,function(a,c){c.movedown_button&&(a===b.rows.length-1?c.movedown_button.style.display="none":c.movedown_button.style.display=""),c.delete_button&&(e?c.delete_button.style.display="none":c.delete_button.style.display=""),b.value[a]=c.getValue()});var f=!1;this.value.length?1===this.value.length?(this.remove_all_rows_button.style.display="none",e||this.hide_delete_buttons?this.delete_last_row_button.style.display="none":(this.delete_last_row_button.style.display="",f=!0)):e||this.hide_delete_buttons?(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"):(this.delete_last_row_button.style.display="",this.remove_all_rows_button.style.display="",f=!0):(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"),this.getMax()&&this.getMax()<=this.rows.length||this.hide_add_button?this.add_row_button.style.display="none":(this.add_row_button.style.display="",f=!0),!this.collapsed&&f?this.controls.style.display="inline-block":this.controls.style.display="none"}},addRow:function(a,b){var c=this,e=this.rows.length;c.rows[e]=this.getElementEditor(e),c.row_cache[e]=c.rows[e],c.tabs_holder&&(c.rows[e].tab_text=document.createElement("span"),c.rows[e].tab_text.textContent=c.rows[e].getHeaderText(),c.rows[e].tab=c.theme.getTab(c.rows[e].tab_text),c.rows[e].tab.addEventListener("click",function(a){c.active_tab=c.rows[e].tab,c.refreshTabs(),a.preventDefault(),a.stopPropagation()}),c.theme.addTab(c.tabs_holder,c.rows[e].tab));var f=c.rows[e].title_controls||c.rows[e].array_controls;c.hide_delete_buttons||(c.rows[e].delete_button=this.getButton(c.getItemTitle(),"delete","Delete "+c.getItemTitle()),c.rows[e].delete_button.className+=" delete",c.rows[e].delete_button.setAttribute("data-i",e),c.rows[e].delete_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i"),e=c.getValue(),f=[],g=null;d(e,function(a,d){return a===b?void(c.rows[a].tab===c.active_tab&&(c.rows[a+1]?g=c.rows[a].tab:a&&(g=c.rows[a-1].tab))):void f.push(d)}),c.setValue(f),g&&(c.active_tab=g,c.refreshTabs()),c.onChange(!0)}),f&&f.appendChild(c.rows[e].delete_button)),e&&!c.hide_move_buttons&&(c.rows[e].moveup_button=this.getButton("","moveup","Move up"),c.rows[e].moveup_button.className+=" moveup",c.rows[e].moveup_button.setAttribute("data-i",e),c.rows[e].moveup_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i");if(!(0>=b)){var d=c.getValue(),e=d[b-1];d[b-1]=d[b],d[b]=e,c.setValue(d),c.active_tab=c.rows[b-1].tab,c.refreshTabs(),c.onChange(!0)}}),f&&f.appendChild(c.rows[e].moveup_button)),c.hide_move_buttons||(c.rows[e].movedown_button=this.getButton("","movedown","Move down"),c.rows[e].movedown_button.className+=" movedown",c.rows[e].movedown_button.setAttribute("data-i",e),c.rows[e].movedown_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i"),d=c.getValue();if(!(b>=d.length-1)){var e=d[b+1];d[b+1]=d[b],d[b]=e,c.setValue(d),c.active_tab=c.rows[b+1].tab,c.refreshTabs(),c.onChange(!0)}}),f&&f.appendChild(c.rows[e].movedown_button)),a&&c.rows[e].setValue(a,b),c.refreshTabs()},addControls:function(){var a=this;this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls.appendChild(this.toggle_button);var b=a.row_holder.style.display,c=a.controls.style.display;this.toggle_button.addEventListener("click",function(d){d.preventDefault(),d.stopPropagation(),a.collapsed?(a.collapsed=!1,a.panel&&(a.panel.style.display=""),a.row_holder.style.display=b,a.tabs_holder&&(a.tabs_holder.style.display=""),a.controls.style.display=c,a.setButtonText(this,"","collapse","Collapse")):(a.collapsed=!0,a.row_holder.style.display="none",a.tabs_holder&&(a.tabs_holder.style.display="none"),a.controls.style.display="none",a.panel&&(a.panel.style.display="none"),a.setButtonText(this,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none"),this.add_row_button=this.getButton(this.getItemTitle(),"add","Add "+this.getItemTitle()),this.add_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.rows.length;a.row_cache[c]?(a.rows[c]=a.row_cache[c],a.rows[c].setValue(a.rows[c].getDefault()),a.rows[c].container.style.display="",a.rows[c].tab&&(a.rows[c].tab.style.display=""),a.rows[c].register()):a.addRow(),a.active_tab=a.rows[c].tab,a.refreshTabs(),a.refreshValue(),a.onChange(!0)}),a.controls.appendChild(this.add_row_button),this.delete_last_row_button=this.getButton("Last "+this.getItemTitle(),"delete","Delete Last "+this.getItemTitle()),
+this.delete_last_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.getValue(),d=null;a.rows.length>1&&a.rows[a.rows.length-1].tab===a.active_tab&&(d=a.rows[a.rows.length-2].tab),c.pop(),a.setValue(c),d&&(a.active_tab=d,a.refreshTabs()),a.onChange(!0)}),a.controls.appendChild(this.delete_last_row_button),this.remove_all_rows_button=this.getButton("All","delete","Delete All"),this.remove_all_rows_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.setValue([]),a.onChange(!0)}),a.controls.appendChild(this.remove_all_rows_button),a.tabs&&(this.add_row_button.style.width="100%",this.add_row_button.style.textAlign="left",this.add_row_button.style.marginBottom="3px",this.delete_last_row_button.style.width="100%",this.delete_last_row_button.style.textAlign="left",this.delete_last_row_button.style.marginBottom="3px",this.remove_all_rows_button.style.width="100%",this.remove_all_rows_button.style.textAlign="left",this.remove_all_rows_button.style.marginBottom="3px")},showValidationErrors:function(a){var b=this,c=[],e=[];if(d(a,function(a,d){d.path===b.path?c.push(d):e.push(d)}),this.error_holder)if(c.length){this.error_holder.innerHTML="",this.error_holder.style.display="",d(c,function(a,c){b.error_holder.appendChild(b.theme.getErrorMessage(c.message))})}else this.error_holder.style.display="none";d(this.rows,function(a,b){b.showValidationErrors(e)})}}),f.defaults.editors.table=f.defaults.editors.array.extend({register:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].register()},unregister:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].unregister()},getNumColumns:function(){return Math.max(Math.min(12,this.width),3)},preBuild:function(){var a=this.jsoneditor.expandRefs(this.schema.items||{});this.item_title=a.title||"row",this.item_default=a["default"]||null,this.item_has_child_editors=a.properties||a.items,this.width=12,this._super()},build:function(){var a=this;this.table=this.theme.getTable(),this.container.appendChild(this.table),this.thead=this.theme.getTableHead(),this.table.appendChild(this.thead),this.header_row=this.theme.getTableRow(),this.thead.appendChild(this.header_row),this.row_holder=this.theme.getTableBody(),this.table.appendChild(this.row_holder);var b=this.getElementEditor(0,!0);if(this.item_default=b.getDefault(),this.width=b.getNumColumns()+2,this.options.compact?(this.panel=document.createElement("div"),this.container.appendChild(this.panel)):(this.title=this.theme.getHeader(this.getTitle(),this.isRequired()),this.container.appendChild(this.title),this.title_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.error_holder=document.createElement("div"),this.panel.appendChild(this.error_holder)),this.panel.appendChild(this.table),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls),this.item_has_child_editors)for(var c=b.getChildEditors(),d=b.property_order||Object.keys(c),e=0;e<d.length;e++){var f=a.theme.getTableHeaderCell(c[d[e]].getTitle());c[d[e]].options.hidden&&(f.style.display="none"),a.header_row.appendChild(f)}else a.header_row.appendChild(a.theme.getTableHeaderCell(this.item_title));b.destroy(),this.row_holder.innerHTML="",this.controls_header_cell=a.theme.getTableHeaderCell(" "),a.header_row.appendChild(this.controls_header_cell),this.addControls()},onChildEditorChange:function(a){this.refreshValue(),this._super()},getItemDefault:function(){return c({},{"default":this.item_default})["default"]},getItemTitle:function(){return this.item_title},getElementEditor:function(a,b){var d=c({},this.schema.items),e=this.jsoneditor.getEditorClass(d,this.jsoneditor),f=this.row_holder.appendChild(this.theme.getTableRow()),g=f;this.item_has_child_editors||(g=this.theme.getTableCell(),f.appendChild(g));var h=this.jsoneditor.createEditor(e,{jsoneditor:this.jsoneditor,schema:d,container:g,path:this.path+"."+a,parent:this,compact:!0,table_row:!0});return h.preBuild(),b||(h.build(),h.postBuild(),h.controls_cell=f.appendChild(this.theme.getTableCell()),h.row=f,h.table_controls=this.theme.getButtonHolder(),h.controls_cell.appendChild(h.table_controls),h.table_controls.style.margin=0,h.table_controls.style.padding=0),h},destroy:function(){this.innerHTML="",this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.row_holder&&this.row_holder.parentNode&&this.row_holder.parentNode.removeChild(this.row_holder),this.table&&this.table.parentNode&&this.table.parentNode.removeChild(this.table),this.panel&&this.panel.parentNode&&this.panel.parentNode.removeChild(this.panel),this.rows=this.title=this.description=this.row_holder=this.table=this.panel=null,this._super()},setValue:function(a,b){if(a=a||[],this.schema.minItems)for(;a.length<this.schema.minItems;)a.push(this.getItemDefault());this.schema.maxItems&&a.length>this.schema.maxItems&&(a=a.slice(0,this.schema.maxItems));var c=JSON.stringify(a);if(c!==this.serialized){var e=!1,f=this;d(a,function(a,b){f.rows[a]?f.rows[a].setValue(b):(f.addRow(b),e=!0)});for(var g=a.length;g<f.rows.length;g++){var h=f.rows[g].container;f.item_has_child_editors||f.rows[g].row.parentNode.removeChild(f.rows[g].row),f.rows[g].destroy(),h.parentNode&&h.parentNode.removeChild(h),f.rows[g]=null,e=!0}f.rows=f.rows.slice(0,a.length),f.refreshValue(),(e||b)&&f.refreshRowButtons(),f.onChange()}},refreshRowButtons:function(){var a=this,b=this.schema.minItems&&this.schema.minItems>=this.rows.length,c=!1;d(this.rows,function(d,e){e.movedown_button&&(d===a.rows.length-1?e.movedown_button.style.display="none":(c=!0,e.movedown_button.style.display="")),e.delete_button&&(b?e.delete_button.style.display="none":(c=!0,e.delete_button.style.display="")),e.moveup_button&&(c=!0)}),d(this.rows,function(a,b){c?b.controls_cell.style.display="":b.controls_cell.style.display="none"}),c?this.controls_header_cell.style.display="":this.controls_header_cell.style.display="none";var e=!1;this.value.length?1===this.value.length||this.hide_delete_buttons?(this.table.style.display="",this.remove_all_rows_button.style.display="none",b||this.hide_delete_buttons?this.delete_last_row_button.style.display="none":(this.delete_last_row_button.style.display="",e=!0)):(this.table.style.display="",b||this.hide_delete_buttons?(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"):(this.delete_last_row_button.style.display="",this.remove_all_rows_button.style.display="",e=!0)):(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none",this.table.style.display="none"),this.schema.maxItems&&this.schema.maxItems<=this.rows.length||this.hide_add_button?this.add_row_button.style.display="none":(this.add_row_button.style.display="",e=!0),e?this.controls.style.display="":this.controls.style.display="none"},refreshValue:function(){var a=this;this.value=[],d(this.rows,function(b,c){a.value[b]=c.getValue()}),this.serialized=JSON.stringify(this.value)},addRow:function(a){var b=this,c=this.rows.length;b.rows[c]=this.getElementEditor(c);var e=b.rows[c].table_controls;this.hide_delete_buttons||(b.rows[c].delete_button=this.getButton("","delete","Delete"),b.rows[c].delete_button.className+=" delete",b.rows[c].delete_button.setAttribute("data-i",c),b.rows[c].delete_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i"),e=b.getValue(),f=[];d(e,function(a,b){a!==c&&f.push(b)}),b.setValue(f),b.onChange(!0)}),e.appendChild(b.rows[c].delete_button)),c&&!this.hide_move_buttons&&(b.rows[c].moveup_button=this.getButton("","moveup","Move up"),b.rows[c].moveup_button.className+=" moveup",b.rows[c].moveup_button.setAttribute("data-i",c),b.rows[c].moveup_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i");if(!(0>=c)){var d=b.getValue(),e=d[c-1];d[c-1]=d[c],d[c]=e,b.setValue(d),b.onChange(!0)}}),e.appendChild(b.rows[c].moveup_button)),this.hide_move_buttons||(b.rows[c].movedown_button=this.getButton("","movedown","Move down"),b.rows[c].movedown_button.className+=" movedown",b.rows[c].movedown_button.setAttribute("data-i",c),b.rows[c].movedown_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i"),d=b.getValue();if(!(c>=d.length-1)){var e=d[c+1];d[c+1]=d[c],d[c]=e,b.setValue(d),b.onChange(!0)}}),e.appendChild(b.rows[c].movedown_button)),a&&b.rows[c].setValue(a)},addControls:function(){var a=this;this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls&&(this.title_controls.appendChild(this.toggle_button),this.toggle_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.collapsed?(a.collapsed=!1,a.panel.style.display="",a.setButtonText(this,"","collapse","Collapse")):(a.collapsed=!0,a.panel.style.display="none",a.setButtonText(this,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none")),this.add_row_button=this.getButton(this.getItemTitle(),"add","Add "+this.getItemTitle()),this.add_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.addRow(),a.refreshValue(),a.refreshRowButtons(),a.onChange(!0)}),a.controls.appendChild(this.add_row_button),this.delete_last_row_button=this.getButton("Last "+this.getItemTitle(),"delete","Delete Last "+this.getItemTitle()),this.delete_last_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.getValue();c.pop(),a.setValue(c),a.onChange(!0)}),a.controls.appendChild(this.delete_last_row_button),this.remove_all_rows_button=this.getButton("All","delete","Delete All"),this.remove_all_rows_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.setValue([]),a.onChange(!0)}),a.controls.appendChild(this.remove_all_rows_button)}}),f.defaults.editors.multiple=f.AbstractEditor.extend({register:function(){if(this.editors){for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].unregister();this.editors[this.type]&&this.editors[this.type].register()}this._super()},unregister:function(){if(this._super(),this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].unregister()},getNumColumns:function(){return this.editors[this.type]?Math.max(this.editors[this.type].getNumColumns(),4):4},enable:function(){if(this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].enable();this.switcher.disabled=!1,this._super()},disable:function(){if(this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].disable();this.switcher.disabled=!0,this._super()},switchEditor:function(a){var b=this;this.editors[a]||this.buildChildEditor(a),b.type=a,b.register();var c=b.getValue();d(b.editors,function(a,d){d&&(b.type===a?(b.keep_values&&d.setValue(c,!0),d.container.style.display=""):d.container.style.display="none")}),b.refreshValue(),b.refreshHeaderText()},buildChildEditor:function(a){var b=this,d=this.types[a],e=b.theme.getChildEditorHolder();b.editor_holder.appendChild(e);var f;"string"==typeof d?(f=c({},b.schema),f.type=d):(f=c({},b.schema,d),f=b.jsoneditor.expandRefs(f),d.required&&Array.isArray(d.required)&&b.schema.required&&Array.isArray(b.schema.required)&&(f.required=b.schema.required.concat(d.required)));var g=b.jsoneditor.getEditorClass(f);b.editors[a]=b.jsoneditor.createEditor(g,{jsoneditor:b.jsoneditor,schema:f,container:e,path:b.path,parent:b,required:!0}),b.editors[a].preBuild(),b.editors[a].build(),b.editors[a].postBuild(),b.editors[a].header&&(b.editors[a].header.style.display="none"),b.editors[a].option=b.switcher_options[a],e.addEventListener("change_header_text",function(){b.refreshHeaderText()}),a!==b.type&&(e.style.display="none")},preBuild:function(){if(this.types=[],this.type=0,this.editors=[],this.validators=[],this.keep_values=!0,"undefined"!=typeof this.jsoneditor.options.keep_oneof_values&&(this.keep_values=this.jsoneditor.options.keep_oneof_values),"undefined"!=typeof this.options.keep_oneof_values&&(this.keep_values=this.options.keep_oneof_values),this.schema.oneOf)this.oneOf=!0,this.types=this.schema.oneOf,d(this.types,function(a,b){}),delete this.schema.oneOf;else{if(this.schema.type&&"any"!==this.schema.type)Array.isArray(this.schema.type)?this.types=this.schema.type:this.types=[this.schema.type];else if(this.types=["string","number","integer","boolean","object","array","null"],this.schema.disallow){var a=this.schema.disallow;"object"==typeof a&&Array.isArray(a)||(a=[a]);var b=[];d(this.types,function(c,d){-1===a.indexOf(d)&&b.push(d)}),this.types=b}delete this.schema.type}this.display_text=this.getDisplayText(this.types)},build:function(){var a=this,b=this.container;this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.container.appendChild(this.header),this.switcher=this.theme.getSwitcher(this.display_text),b.appendChild(this.switcher),this.switcher.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.switchEditor(a.display_text.indexOf(this.value)),a.onChange(!0)}),this.editor_holder=document.createElement("div"),b.appendChild(this.editor_holder),this.switcher_options=this.theme.getSwitcherOptions(this.switcher),d(this.types,function(b,d){a.editors[b]=!1;var e;"string"==typeof d?(e=c({},a.schema),e.type=d):(e=c({},a.schema,d),d.required&&Array.isArray(d.required)&&a.schema.required&&Array.isArray(a.schema.required)&&(e.required=a.schema.required.concat(d.required))),a.validators[b]=new f.Validator(a.jsoneditor,e)}),this.switchEditor(0)},onChildEditorChange:function(a){this.editors[this.type]&&(this.refreshValue(),this.refreshHeaderText()),this._super()},refreshHeaderText:function(){var a=this.getDisplayText(this.types);d(this.switcher_options,function(b,c){c.textContent=a[b]})},refreshValue:function(){this.value=this.editors[this.type].getValue()},setValue:function(a,b){var c=this;d(this.validators,function(b,d){return d.validate(a).length?void 0:(c.type=b,c.switcher.value=c.display_text[b],!1)}),this.switchEditor(this.type),this.editors[this.type].setValue(a,b),this.refreshValue(),c.onChange()},destroy:function(){d(this.editors,function(a,b){b&&b.destroy()}),this.editor_holder&&this.editor_holder.parentNode&&this.editor_holder.parentNode.removeChild(this.editor_holder),this.switcher&&this.switcher.parentNode&&this.switcher.parentNode.removeChild(this.switcher),this._super()},showValidationErrors:function(a){var b=this;this.oneOf?d(this.editors,function(e,f){if(f){var g=b.path+".oneOf["+e+"]",h=[];d(a,function(a,d){if(d.path.substr(0,g.length)===g){var e=c({},d);e.path=b.path+e.path.substr(g.length),h.push(e)}}),f.showValidationErrors(h)}}):d(this.editors,function(b,c){c&&c.showValidationErrors(a)})}}),f.defaults.editors["enum"]=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){this.container;this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.container.appendChild(this.title),this.options.enum_titles=this.options.enum_titles||[],this["enum"]=this.schema["enum"],this.selected=0,this.select_options=[],this.html_values=[];for(var a=this,b=0;b<this["enum"].length;b++)this.select_options[b]=this.options.enum_titles[b]||"Value "+(b+1),this.html_values[b]=this.getHTML(this["enum"][b]);this.switcher=this.theme.getSwitcher(this.select_options),this.container.appendChild(this.switcher),this.display_area=this.theme.getIndentedPanel(),this.container.appendChild(this.display_area),this.options.hide_display&&(this.display_area.style.display="none"),this.switcher.addEventListener("change",function(){a.selected=a.select_options.indexOf(this.value),a.value=a["enum"][a.selected],a.refreshValue(),a.onChange(!0)}),this.value=this["enum"][0],this.refreshValue(),1===this["enum"].length&&(this.switcher.style.display="none")},refreshValue:function(){var a=this;a.selected=-1;var b=JSON.stringify(this.value);return d(this["enum"],function(c,d){return b===JSON.stringify(d)?(a.selected=c,!1):void 0}),a.selected<0?void a.setValue(a["enum"][0]):(this.switcher.value=this.select_options[this.selected],void(this.display_area.innerHTML=this.html_values[this.selected]))},enable:function(){this.always_disabled||(this.switcher.disabled=!1),this._super()},disable:function(){this.switcher.disabled=!0,this._super()},getHTML:function(a){var b=this;if(null===a)return"<em>null</em>";if("object"==typeof a){var c="";return d(a,function(d,e){var f=b.getHTML(e);Array.isArray(a)||(f="<div><em>"+d+"</em>: "+f+"</div>"),c+="<li>"+f+"</li>"}),c=Array.isArray(a)?"<ol>"+c+"</ol>":"<ul style='margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;'>"+c+"</ul>"}return"boolean"==typeof a?a?"true":"false":"string"==typeof a?a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):a},setValue:function(a){this.value!==a&&(this.value=a,this.refreshValue(),this.onChange())},destroy:function(){this.display_area&&this.display_area.parentNode&&this.display_area.parentNode.removeChild(this.display_area),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.switcher&&this.switcher.parentNode&&this.switcher.parentNode.removeChild(this.switcher),this._super()}}),f.defaults.editors.select=f.AbstractEditor.extend({setValue:function(a,b){a=this.typecast(a||"");var c=a;this.enum_values.indexOf(c)<0&&(c=this.enum_values[0]),this.value!==c&&(this.input.value=this.enum_options[this.enum_values.indexOf(c)],this.select2&&this.select2.select2("val",this.input.value),this.value=c,this.onChange())},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){if(!this.enum_options)return 3;for(var a=this.getTitle().length,b=0;b<this.enum_options.length;b++)a=Math.max(a,this.enum_options[b].length+4);return Math.min(12,Math.max(a/7,2))},typecast:function(a){return"boolean"===this.schema.type?!!a:"number"===this.schema.type?1*a:"integer"===this.schema.type?Math.floor(1*a):""+a},getValue:function(){return this.value},preBuild:function(){var a=this;if(this.input_type="select",this.enum_options=[],this.enum_values=[],this.enum_display=[],this.schema["enum"]){var b=this.schema.options&&this.schema.options.enum_titles||[];d(this.schema["enum"],function(c,d){a.enum_options[c]=""+d,a.enum_display[c]=""+(b[c]||d),a.enum_values[c]=a.typecast(d)}),this.isRequired()||(a.enum_display.unshift(" "),a.enum_options.unshift("undefined"),a.enum_values.unshift(void 0))}else if("boolean"===this.schema.type)a.enum_display=this.schema.options&&this.schema.options.enum_titles||["true","false"],a.enum_options=["1",""],a.enum_values=[!0,!1],this.isRequired()||(a.enum_display.unshift(" "),a.enum_options.unshift("undefined"),a.enum_values.unshift(void 0));else{if(!this.schema.enumSource)throw"'select' editor requires the enum property to be set.";if(this.enumSource=[],this.enum_display=[],this.enum_options=[],this.enum_values=[],Array.isArray(this.schema.enumSource))for(h=0;h<this.schema.enumSource.length;h++)"string"==typeof this.schema.enumSource[h]?this.enumSource[h]={source:this.schema.enumSource[h]}:Array.isArray(this.schema.enumSource[h])?this.enumSource[h]=this.schema.enumSource[h]:this.enumSource[h]=c({},this.schema.enumSource[h]);else this.schema.enumValue?this.enumSource=[{source:this.schema.enumSource,value:this.schema.enumValue}]:this.enumSource=[{source:this.schema.enumSource}];for(h=0;h<this.enumSource.length;h++)this.enumSource[h].value&&(this.enumSource[h].value=this.jsoneditor.compileTemplate(this.enumSource[h].value,this.template_engine)),this.enumSource[h].title&&(this.enumSource[h].title=this.jsoneditor.compileTemplate(this.enumSource[h].title,this.template_engine)),this.enumSource[h].filter&&(this.enumSource[h].filter=this.jsoneditor.compileTemplate(this.enumSource[h].filter,this.template_engine))}},build:function(){var a=this;this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.options.compact&&(this.container.className+=" compact"),this.input=this.theme.getSelectInput(this.enum_options),this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display),(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.onInputChange()}),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),this.value=this.enum_values[0]},onInputChange:function(){var a=this.input.value,b=a;-1===this.enum_options.indexOf(a)&&(b=this.enum_options[0]),this.value=this.enum_values[this.enum_options.indexOf(a)],this.onChange(!0)},setupSelect2:function(){if(window.jQuery&&window.jQuery.fn&&window.jQuery.fn.select2&&(this.enum_options.length>2||this.enum_options.length&&this.enumSource)){var a=c({},f.plugins.select2);this.schema.options&&this.schema.options.select2_options&&(a=c(a,this.schema.options.select2_options)),this.select2=window.jQuery(this.input).select2(a);var b=this;this.select2.on("select2-blur",function(){b.input.value=b.select2.select2("val"),b.onInputChange()})}else this.select2=null},postBuild:function(){this._super(),this.theme.afterInputReady(this.input),this.setupSelect2()},onWatchedFieldChange:function(){var a,b;if(this.enumSource){a=this.getWatchedFieldValues();for(var c=[],d=[],e=0;e<this.enumSource.length;e++)if(Array.isArray(this.enumSource[e]))c=c.concat(this.enumSource[e]),d=d.concat(this.enumSource[e]);else{var f=[];if(f=Array.isArray(this.enumSource[e].source)?this.enumSource[e].source:a[this.enumSource[e].source]){if(this.enumSource[e].slice&&(f=Array.prototype.slice.apply(f,this.enumSource[e].slice)),this.enumSource[e].filter){var g=[];for(b=0;b<f.length;b++)this.enumSource[e].filter({i:b,item:f[b],watched:a})&&g.push(f[b]);f=g}var h=[],i=[];for(b=0;b<f.length;b++){var j=f[b];this.enumSource[e].value?i[b]=this.enumSource[e].value({i:b,item:j}):i[b]=f[b],this.enumSource[e].title?h[b]=this.enumSource[e].title({i:b,item:j}):h[b]=i[b]}c=c.concat(i),d=d.concat(h)}}var k=this.value;this.theme.setSelectOptions(this.input,c,d),this.enum_options=c,this.enum_display=d,this.enum_values=c,this.select2&&this.select2.select2("destroy"),-1!==c.indexOf(k)?(this.input.value=k,this.value=k):(this.input.value=c[0],this.value=c[0]||"",this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange(),this.jsoneditor.notifyWatchers(this.path)),this.setupSelect2()}this._super()},enable:function(){this.always_disabled||(this.input.disabled=!1,this.select2&&this.select2.select2("enable",!0)),this._super()},disable:function(){this.input.disabled=!0,this.select2&&this.select2.select2("enable",!1),this._super()},destroy:function(){this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.select2&&(this.select2.select2("destroy"),this.select2=null),this._super()}}),f.defaults.editors.multiselect=f.AbstractEditor.extend({preBuild:function(){this._super(),this.select_options={},this.select_values={};var a=this.jsoneditor.expandRefs(this.schema.items||{}),b=a["enum"]||[];for(this.option_keys=[],h=0;h<b.length;h++)this.sanitize(b[h])===b[h]&&(this.option_keys.push(b[h]+""),this.select_values[b[h]+""]=b[h])},build:function(){var a,b=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),!this.schema.format&&this.option_keys.length<8||"checkbox"===this.schema.format){for(this.input_type="checkboxes",this.inputs={},this.controls={},a=0;a<this.option_keys.length;a++){this.inputs[this.option_keys[a]]=this.theme.getCheckbox(),this.select_options[this.option_keys[a]]=this.inputs[this.option_keys[a]];var c=this.theme.getCheckboxLabel(this.option_keys[a]);this.controls[this.option_keys[a]]=this.theme.getFormControl(c,this.inputs[this.option_keys[a]])}this.control=this.theme.getMultiCheckboxHolder(this.controls,this.label,this.description)}else{for(this.input_type="select",this.input=this.theme.getSelectInput(this.option_keys),this.input.multiple=!0,this.input.size=Math.min(10,this.option_keys.length),a=0;a<this.option_keys.length;a++)this.select_options[this.option_keys[a]]=this.input.children[a];(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.control=this.theme.getFormControl(this.label,this.input,this.description)}this.container.appendChild(this.control),this.control.addEventListener("change",function(c){c.preventDefault(),c.stopPropagation();var d=[];for(a=0;a<b.option_keys.length;a++)(b.select_options[b.option_keys[a]].selected||b.select_options[b.option_keys[a]].checked)&&d.push(b.select_values[b.option_keys[a]]);b.updateValue(d),b.onChange(!0)})},setValue:function(a,b){var c;for(a=a||[],"object"!=typeof a?a=[a]:Array.isArray(a)||(a=[]),c=0;c<a.length;c++)"string"!=typeof a[c]&&(a[c]+="");for(c in this.select_options)this.select_options.hasOwnProperty(c)&&(this.select_options[c]["select"===this.input_type?"selected":"checked"]=-1!==a.indexOf(c));this.updateValue(a),this.onChange()},setupSelect2:function(){if(window.jQuery&&window.jQuery.fn&&window.jQuery.fn.select2){var a=window.jQuery.extend({},f.plugins.select2);this.schema.options&&this.schema.options.select2_options&&(a=c(a,this.schema.options.select2_options)),this.select2=window.jQuery(this.input).select2(a);var b=this;this.select2.on("select2-blur",function(){var a=b.select2.select2("val");b.value=a,b.onChange(!0)})}else this.select2=null},onInputChange:function(){this.value=this.input.value,this.onChange(!0)},postBuild:function(){this._super(),this.setupSelect2()},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){var a=this.getTitle().length;for(var b in this.select_values)this.select_values.hasOwnProperty(b)&&(a=Math.max(a,(this.select_values[b]+"").length+4));return Math.min(12,Math.max(a/7,2))},updateValue:function(a){for(var b=!1,c=[],d=0;d<a.length;d++)if(this.select_options[a[d]+""]){var e=this.sanitize(this.select_values[a[d]]);c.push(e),e!==a[d]&&(b=!0)}else b=!0;return this.value=c,this.select2&&this.select2.select2("val",this.value),b},sanitize:function(a){return"number"===this.schema.items.type?1*a:"integer"===this.schema.items.type?Math.floor(1*a):""+a},enable:function(){if(!this.always_disabled){if(this.input)this.input.disabled=!1;else if(this.inputs)for(var a in this.inputs)this.inputs.hasOwnProperty(a)&&(this.inputs[a].disabled=!1);this.select2&&this.select2.select2("enable",!0)}this._super()},disable:function(){if(this.input)this.input.disabled=!0;else if(this.inputs)for(var a in this.inputs)this.inputs.hasOwnProperty(a)&&(this.inputs[a].disabled=!0);this.select2&&this.select2.select2("enable",!1),this._super()},destroy:function(){this.select2&&(this.select2.select2("destroy"),this.select2=null),this._super()}}),f.defaults.editors.base64=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){var a=this;if(this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.input=this.theme.getFormInputField("hidden"),this.container.appendChild(this.input),!this.schema.readOnly&&!this.schema.readonly){if(!window.FileReader)throw"FileReader required for base64 editor";this.uploader=this.theme.getFormInputField("file"),this.uploader.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),this.files&&this.files.length){var c=new FileReader;c.onload=function(b){a.value=b.target.result,a.refreshPreview(),a.onChange(!0),c=null},c.readAsDataURL(this.files[0])}})}this.preview=this.theme.getFormInputDescription(this.schema.description),this.container.appendChild(this.preview),this.control=this.theme.getFormControl(this.label,this.uploader||this.input,this.preview),this.container.appendChild(this.control)},refreshPreview:function(){if(this.last_preview!==this.value&&(this.last_preview=this.value,this.preview.innerHTML="",this.value)){var a=this.value.match(/^data:([^;,]+)[;,]/);if(a&&(a=a[1]),a){if(this.preview.innerHTML="<strong>Type:</strong> "+a+", <strong>Size:</strong> "+Math.floor((this.value.length-this.value.split(",")[0].length-1)/1.33333)+" bytes","image"===a.substr(0,5)){this.preview.innerHTML+="<br>";var b=document.createElement("img");b.style.maxWidth="100%",b.style.maxHeight="100px",b.src=this.value,this.preview.appendChild(b)}}else this.preview.innerHTML="<em>Invalid data URI</em>"}},enable:function(){this.uploader&&(this.uploader.disabled=!1),this._super()},disable:function(){this.uploader&&(this.uploader.disabled=!0),this._super()},setValue:function(a){this.value!==a&&(this.value=a,this.input.value=this.value,this.refreshPreview(),this.onChange())},destroy:function(){this.preview&&this.preview.parentNode&&this.preview.parentNode.removeChild(this.preview),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.uploader&&this.uploader.parentNode&&this.uploader.parentNode.removeChild(this.uploader),this._super()}}),f.defaults.editors.upload=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){var a=this;if(this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.input=this.theme.getFormInputField("hidden"),this.container.appendChild(this.input),!this.schema.readOnly&&!this.schema.readonly){if(!this.jsoneditor.options.upload)throw"Upload handler required for upload editor";this.uploader=this.theme.getFormInputField("file"),this.uploader.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),this.files&&this.files.length){var c=new FileReader;c.onload=function(b){a.preview_value=b.target.result,a.refreshPreview(),a.onChange(!0),c=null},c.readAsDataURL(this.files[0])}})}var b=this.schema.description;b||(b=""),this.preview=this.theme.getFormInputDescription(b),this.container.appendChild(this.preview),this.control=this.theme.getFormControl(this.label,this.uploader||this.input,this.preview),this.container.appendChild(this.control)},refreshPreview:function(){if(this.last_preview!==this.preview_value&&(this.last_preview=this.preview_value,this.preview.innerHTML="",this.preview_value)){var a=this,b=this.preview_value.match(/^data:([^;,]+)[;,]/);b&&(b=b[1]),b||(b="unknown");var c=this.uploader.files[0];if(this.preview.innerHTML="<strong>Type:</strong> "+b+", <strong>Size:</strong> "+c.size+" bytes","image"===b.substr(0,5)){this.preview.innerHTML+="<br>";var d=document.createElement("img");d.style.maxWidth="100%",d.style.maxHeight="100px",d.src=this.preview_value,
+this.preview.appendChild(d)}this.preview.innerHTML+="<br>";var e=this.getButton("Upload","upload","Upload");this.preview.appendChild(e),e.addEventListener("click",function(b){b.preventDefault(),e.setAttribute("disabled","disabled"),a.theme.removeInputError(a.uploader),a.theme.getProgressBar&&(a.progressBar=a.theme.getProgressBar(),a.preview.appendChild(a.progressBar)),a.jsoneditor.options.upload(a.path,c,{success:function(b){a.setValue(b),a.parent?a.parent.onChildEditorChange(a):a.jsoneditor.onChange(),a.progressBar&&a.preview.removeChild(a.progressBar),e.removeAttribute("disabled")},failure:function(b){a.theme.addInputError(a.uploader,b),a.progressBar&&a.preview.removeChild(a.progressBar),e.removeAttribute("disabled")},updateProgress:function(b){a.progressBar&&(b?a.theme.updateProgressBar(a.progressBar,b):a.theme.updateProgressBarUnknown(a.progressBar))}})})}},enable:function(){this.uploader&&(this.uploader.disabled=!1),this._super()},disable:function(){this.uploader&&(this.uploader.disabled=!0),this._super()},setValue:function(a){this.value!==a&&(this.value=a,this.input.value=this.value,this.onChange())},destroy:function(){this.preview&&this.preview.parentNode&&this.preview.parentNode.removeChild(this.preview),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.uploader&&this.uploader.parentNode&&this.uploader.parentNode.removeChild(this.uploader),this._super()}}),f.defaults.editors.checkbox=f.AbstractEditor.extend({setValue:function(a,b){this.value=!!a,this.input.checked=this.value,this.onChange()},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){return Math.min(12,Math.max(this.getTitle().length/7,2))},build:function(){var a=this;this.options.compact||(this.label=this.header=this.theme.getCheckboxLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.options.compact&&(this.container.className+=" compact"),this.input=this.theme.getCheckbox(),this.control=this.theme.getFormControl(this.label,this.input,this.description),(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.value=this.checked,a.onChange(!0)}),this.container.appendChild(this.control)},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},destroy:function(){this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this._super()}});var g=function(){var a=document.documentElement;return a.matches?"matches":a.webkitMatchesSelector?"webkitMatchesSelector":a.mozMatchesSelector?"mozMatchesSelector":a.msMatchesSelector?"msMatchesSelector":a.oMatchesSelector?"oMatchesSelector":void 0}();f.AbstractTheme=a.extend({getContainer:function(){return document.createElement("div")},getFloatRightLinkHolder:function(){var a=document.createElement("div");return a.style=a.style||{},a.style.cssFloat="right",a.style.marginLeft="10px",a},getModal:function(){var a=document.createElement("div");return a.style.backgroundColor="white",a.style.border="1px solid black",a.style.boxShadow="3px 3px black",a.style.position="absolute",a.style.zIndex="10",a.style.display="none",a},getGridContainer:function(){var a=document.createElement("div");return a},getGridRow:function(){var a=document.createElement("div");return a.className="row",a},getGridColumn:function(){var a=document.createElement("div");return a},setGridColumnSize:function(a,b){},getLink:function(a){var b=document.createElement("a");return b.setAttribute("href","#"),b.appendChild(document.createTextNode(a)),b},disableHeader:function(a){a.style.color="#ccc"},disableLabel:function(a){a.style.color="#ccc"},enableHeader:function(a){a.style.color=""},enableLabel:function(a){a.style.color=""},getFormInputLabel:function(a){var b=document.createElement("label");return b.appendChild(document.createTextNode(a)),b},getCheckboxLabel:function(a){var b=this.getFormInputLabel(a);return b.style.fontWeight="normal",b},getHeader:function(a,b){var c=document.createElement("h3");return"string"==typeof a?c.textContent=a:c.appendChild(a),b&&(c.className+=" required"),c},getCheckbox:function(){var a=this.getFormInputField("checkbox");return a.style.display="inline-block",a.style.width="auto",a},getMultiCheckboxHolder:function(a,b,c){var d=document.createElement("div");b&&(b.style.display="block",d.appendChild(b));for(var e in a)a.hasOwnProperty(e)&&(a[e].style.display="inline-block",a[e].style.marginRight="20px",d.appendChild(a[e]));return c&&d.appendChild(c),d},getSelectInput:function(a){var b=document.createElement("select");return a&&this.setSelectOptions(b,a),b},getSwitcher:function(a){var b=this.getSelectInput(a);return b.style.backgroundColor="transparent",b.style.display="inline-block",b.style.fontStyle="italic",b.style.fontWeight="normal",b.style.height="auto",b.style.marginBottom=0,b.style.marginLeft="5px",b.style.padding="0 0 0 3px",b.style.width="auto",b},getSwitcherOptions:function(a){return a.getElementsByTagName("option")},setSwitcherOptions:function(a,b,c){this.setSelectOptions(a,b,c)},setSelectOptions:function(a,b,c){c=c||[],a.innerHTML="";for(var d=0;d<b.length;d++){var e=document.createElement("option");e.setAttribute("value",b[d]),e.textContent=c[d]||b[d],a.appendChild(e)}},getTextareaInput:function(){var a=document.createElement("textarea");return a.style=a.style||{},a.style.width="100%",a.style.height="300px",a.style.boxSizing="border-box",a},getRangeInput:function(a,b,c){var d=this.getFormInputField("range");return d.setAttribute("min",a),d.setAttribute("max",b),d.setAttribute("step",c),d},getFormInputField:function(a){var b=document.createElement("input");return b.setAttribute("type",a),b},afterInputReady:function(a){},getFormControl:function(a,b,c){var d=document.createElement("div");return d.className="form-control",a&&d.appendChild(a),"checkbox"===b.type?a.insertBefore(b,a.firstChild):d.appendChild(b),c&&d.appendChild(c),d},getIndentedPanel:function(){var a=document.createElement("div");return a.style=a.style||{},a.style.paddingLeft="10px",a.style.marginLeft="10px",a.style.borderLeft="1px solid #ccc",a},getChildEditorHolder:function(){return document.createElement("div")},getDescription:function(a){var b=document.createElement("p");return b.innerHTML=a,b},getCheckboxDescription:function(a){return this.getDescription(a)},getFormInputDescription:function(a){return this.getDescription(a)},getHeaderButtonHolder:function(){return this.getButtonHolder()},getButtonHolder:function(){return document.createElement("div")},getButton:function(a,b,c){var d=document.createElement("button");return d.type="button",this.setButtonText(d,a,b,c),d},setButtonText:function(a,b,c,d){a.innerHTML="",c&&(a.appendChild(c),a.innerHTML+=" "),a.appendChild(document.createTextNode(b)),d&&a.setAttribute("title",d)},getTable:function(){return document.createElement("table")},getTableRow:function(){return document.createElement("tr")},getTableHead:function(){return document.createElement("thead")},getTableBody:function(){return document.createElement("tbody")},getTableHeaderCell:function(a){var b=document.createElement("th");return b.textContent=a,b},getTableCell:function(){var a=document.createElement("td");return a},getErrorMessage:function(a){var b=document.createElement("p");return b.style=b.style||{},b.style.color="red",b.appendChild(document.createTextNode(a)),b},addInputError:function(a,b){},removeInputError:function(a){},addTableRowError:function(a){},removeTableRowError:function(a){},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<div style='float: left; width: 130px;' class='tabs'></div><div class='content' style='margin-left: 130px;'></div><div style='clear:both;'></div>",a},applyStyles:function(a,b){a.style=a.style||{};for(var c in b)b.hasOwnProperty(c)&&(a.style[c]=b[c])},closest:function(a,b){for(;a&&a!==document;){if(!g)return!1;if(a[g](b))return a;a=a.parentNode}return!1},getTab:function(a){var b=document.createElement("div");return b.appendChild(a),b.style=b.style||{},this.applyStyles(b,{border:"1px solid #ccc",borderWidth:"1px 0 1px 1px",textAlign:"center",lineHeight:"30px",borderRadius:"5px",borderBottomRightRadius:0,borderTopRightRadius:0,fontWeight:"bold",cursor:"pointer"}),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){return this.getIndentedPanel()},markTabActive:function(a){this.applyStyles(a,{opacity:1,background:"white"})},markTabInactive:function(a){this.applyStyles(a,{opacity:.5,background:""})},addTab:function(a,b){a.children[0].appendChild(b)},getBlockLink:function(){var a=document.createElement("a");return a.style.display="block",a},getBlockLinkHolder:function(){var a=document.createElement("div");return a},getLinksHolder:function(){var a=document.createElement("div");return a},createMediaLink:function(a,b,c){a.appendChild(b),c.style.width="100%",a.appendChild(c)},createImageLink:function(a,b,c){a.appendChild(b),b.appendChild(c)}}),f.defaults.themes.bootstrap2=f.AbstractTheme.extend({getRangeInput:function(a,b,c){return this._super(a,b,c)},getGridContainer:function(){var a=document.createElement("div");return a.className="container-fluid",a},getGridRow:function(){var a=document.createElement("div");return a.className="row-fluid",a},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="inline-block",c.style.fontWeight="bold",b&&(c.className+=" required"),c},setGridColumnSize:function(a,b){a.className="span"+b},getSelectInput:function(a){var b=this._super(a);return b.style.width="auto",b.style.maxWidth="98%",b},getFormInputField:function(a){var b=this._super(a);return b.style.width="98%",b},afterInputReady:function(a){a.controlgroup||(a.controlgroup=this.closest(a,".control-group"),a.controls=this.closest(a,".controls"),this.closest(a,".compact")&&(a.controlgroup.className=a.controlgroup.className.replace(/control-group/g,"").replace(/[ ]{2,}/g," "),a.controls.className=a.controlgroup.className.replace(/controls/g,"").replace(/[ ]{2,}/g," "),a.style.marginBottom=0))},getIndentedPanel:function(){var a=document.createElement("div");return a.className="well well-small",a},getFormInputDescription:function(a){var b=document.createElement("p");return b.className="help-inline",b.textContent=a,b},getFormControl:function(a,b,c){var d=document.createElement("div");d.className="control-group";var e=document.createElement("div");return e.className="controls",a&&"checkbox"===b.getAttribute("type")?(d.appendChild(e),a.className+=" checkbox",a.appendChild(b),e.appendChild(a),e.style.height="30px"):(a&&(a.className+=" control-label",d.appendChild(a)),e.appendChild(b),d.appendChild(e)),c&&e.appendChild(c),d},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="btn-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+=" btn btn-default",d},getTable:function(){var a=document.createElement("table");return a.className="table table-bordered",a.style.width="auto",a.style.maxWidth="none",a},addInputError:function(a,b){a.controlgroup&&a.controls&&(a.controlgroup.className+=" error",a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("p"),a.errmsg.className="help-block errormsg",a.controls.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none",a.controlgroup.className=a.controlgroup.className.replace(/\s?error/g,""))},getTabHolder:function(){var a=document.createElement("div");return a.className="tabbable tabs-left",a.innerHTML="<ul class='nav nav-tabs span2' style='margin-right: 0;'></ul><div class='tab-content span10' style='overflow:visible;'></div>",a},getTab:function(a){var b=document.createElement("li"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="tab-pane active",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s?active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)},getProgressBar:function(){var a=document.createElement("div");a.className="progress";var b=document.createElement("div");return b.className="bar",b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.className="progress progress-striped active",a.firstChild.style.width="100%")}}),f.defaults.themes.bootstrap3=f.AbstractTheme.extend({getSelectInput:function(a){var b=this._super(a);return b.className+="form-control",b},setGridColumnSize:function(a,b){a.className="col-md-"+b},afterInputReady:function(a){a.controlgroup||(a.controlgroup=this.closest(a,".form-group"),this.closest(a,".compact")&&(a.controlgroup.style.marginBottom=0))},getTextareaInput:function(){var a=document.createElement("textarea");return a.className="form-control",a},getRangeInput:function(a,b,c){return this._super(a,b,c)},getFormInputField:function(a){var b=this._super(a);return"checkbox"!==a&&(b.className+="form-control"),b},getFormControl:function(a,b,c){var d=document.createElement("div");return a&&"checkbox"===b.type?(d.className+=" checkbox",a.appendChild(b),a.style.fontSize="14px",d.style.marginTop="0",d.appendChild(a),b.style.position="relative",b.style.cssFloat="left"):(d.className+=" form-group",a&&(a.className+=" control-label",d.appendChild(a)),d.appendChild(b)),c&&d.appendChild(c),d},getIndentedPanel:function(){var a=document.createElement("div");return a.className="well well-sm",a},getFormInputDescription:function(a){var b=document.createElement("p");return b.className="help-block",b.innerHTML=a,b},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="btn-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+="btn btn-default",d},getTable:function(){var a=document.createElement("table");return a.className="table table-bordered",a.style.width="auto",a.style.maxWidth="none",a},addInputError:function(a,b){a.controlgroup&&(a.controlgroup.className+=" has-error",a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("p"),a.errmsg.className="help-block errormsg",a.controlgroup.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none",a.controlgroup.className=a.controlgroup.className.replace(/\s?has-error/g,""))},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<div class='tabs list-group col-md-2'></div><div class='col-md-10'></div>",a.className="rows",a},getTab:function(a){var b=document.createElement("a");return b.className="list-group-item",b.setAttribute("href","#"),b.appendChild(a),b},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s?active/g,"")},getProgressBar:function(){var a=0,b=100,c=0,d=document.createElement("div");d.className="progress";var e=document.createElement("div");return e.className="progress-bar",e.setAttribute("role","progressbar"),e.setAttribute("aria-valuenow",c),e.setAttribute("aria-valuemin",a),e.setAttribute("aria-valuenax",b),e.innerHTML=c+"%",d.appendChild(e),d},updateProgressBar:function(a,b){if(a){var c=a.firstChild,d=b+"%";c.setAttribute("aria-valuenow",b),c.style.width=d,c.innerHTML=d}},updateProgressBarUnknown:function(a){if(a){var b=a.firstChild;a.className="progress progress-striped active",b.removeAttribute("aria-valuenow"),b.style.width="100%",b.innerHTML=""}}}),f.defaults.themes.foundation=f.AbstractTheme.extend({getChildEditorHolder:function(){var a=document.createElement("div");return a.style.marginBottom="15px",a},getSelectInput:function(a){var b=this._super(a);return b.style.minWidth="none",b.style.padding="5px",b.style.marginTop="3px",b},getSwitcher:function(a){var b=this._super(a);return b.style.paddingRight="8px",b},afterInputReady:function(a){this.closest(a,".compact")&&(a.style.marginBottom=0),a.group=this.closest(a,".form-control")},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="inline-block",b&&(c.className+=" required"),c},getFormInputField:function(a){var b=this._super(a);return b.style.width="100%",b.style.marginBottom="checkbox"===a?"0":"12px",b},getFormInputDescription:function(a){var b=document.createElement("p");return b.textContent=a,b.style.marginTop="-10px",b.style.fontStyle="italic",b},getIndentedPanel:function(){var a=document.createElement("div");return a.className="panel",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.display="inline-block",a.style.marginLeft="10px",a.style.verticalAlign="middle",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="button-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+=" small button",d},addInputError:function(a,b){a.group&&(a.group.className+=" error",a.errmsg?a.errmsg.style.display="":(a.insertAdjacentHTML("afterend",'<small class="error"></small>'),a.errmsg=a.parentNode.getElementsByClassName("error")[0]),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.group.className=a.group.className.replace(/ error/g,""),a.errmsg.style.display="none")},getProgressBar:function(){var a=document.createElement("div");a.className="progress";var b=document.createElement("span");return b.className="meter",b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.firstChild.style.width="100%")}}),f.defaults.themes.foundation3=f.defaults.themes.foundation.extend({getHeaderButtonHolder:function(){var a=this._super();return a.style.fontSize=".6em",a},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.fontWeight="bold",b&&(c.className+=" required"),c},getTabHolder:function(){var a=document.createElement("div");return a.className="row",a.innerHTML="<dl class='tabs vertical two columns'></dl><div class='tabs-content ten columns'></div>",a},setGridColumnSize:function(a,b){var c=["zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];a.className="columns "+c[b]},getTab:function(a){var b=document.createElement("dd"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="content active",a.style.paddingLeft="5px",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s*active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)}}),f.defaults.themes.foundation4=f.defaults.themes.foundation.extend({getHeaderButtonHolder:function(){var a=this._super();return a.style.fontSize=".6em",a},setGridColumnSize:function(a,b){a.className="columns large-"+b},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.fontWeight="bold",b&&(c.className+=" required"),c}}),f.defaults.themes.foundation5=f.defaults.themes.foundation.extend({getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},setGridColumnSize:function(a,b){a.className="columns medium-"+b},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className=d.className.replace(/\s*small/g,"")+" tiny",d},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<dl class='tabs vertical'></dl><div class='tabs-content vertical'></div>",a},getTab:function(a){var b=document.createElement("dd"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="content active",a.style.paddingLeft="5px",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s*active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)}}),f.defaults.themes.html=f.AbstractTheme.extend({getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="block",c.style.marginBottom="3px",c.style.fontWeight="bold",b&&(c.className+=" required"),c},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8em",b.style.margin=0,b.style.display="inline-block",b.style.fontStyle="italic",b},getIndentedPanel:function(){var a=this._super();return a.style.border="1px solid #ddd",a.style.padding="5px",a.style.margin="5px",a.style.borderRadius="3px",a},getChildEditorHolder:function(){var a=this._super();return a.style.marginBottom="8px",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.display="inline-block",a.style.marginLeft="10px",a.style.fontSize=".8em",a.style.verticalAlign="middle",a},getTable:function(){var a=this._super();return a.style.borderBottom="1px solid #ccc",a.style.marginBottom="5px",a},addInputError:function(a,b){if(a.style.borderColor="red",a.errmsg)a.errmsg.style.display="block";else{var c=this.closest(a,".form-control");a.errmsg=document.createElement("div"),a.errmsg.setAttribute("class","errmsg"),a.errmsg.style=a.errmsg.style||{},a.errmsg.style.color="red",c.appendChild(a.errmsg)}a.errmsg.innerHTML="",a.errmsg.appendChild(document.createTextNode(b))},removeInputError:function(a){a.style.borderColor="",a.errmsg&&(a.errmsg.style.display="none")},getProgressBar:function(){var a=100,b=0,c=document.createElement("progress");return c.setAttribute("max",a),c.setAttribute("value",b),c},updateProgressBar:function(a,b){a&&a.setAttribute("value",b)},updateProgressBarUnknown:function(a){a&&a.removeAttribute("value")}}),f.defaults.themes.jqueryui=f.AbstractTheme.extend({getTable:function(){var a=this._super();return a.setAttribute("cellpadding",5),a.setAttribute("cellspacing",0),a},getTableHeaderCell:function(a){var b=this._super(a);return b.className="ui-state-active",b.style.fontWeight="bold",b},getTableCell:function(){var a=this._super();return a.className="ui-widget-content",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a.style.fontSize=".6em",a.style.display="inline-block",a},getFormInputDescription:function(a){var b=this.getDescription(a);return b.style.marginLeft="10px",b.style.display="inline-block",b},getFormControl:function(a,b,c){var d=this._super(a,b,c);return"checkbox"===b.type?(d.style.lineHeight="25px",d.style.padding="3px 0"):d.style.padding="4px 0 8px 0",d},getDescription:function(a){var b=document.createElement("span");return b.style.fontSize=".8em",b.style.fontStyle="italic",b.textContent=a,b},getButtonHolder:function(){var a=document.createElement("div");return a.className="ui-buttonset",a.style.fontSize=".7em",a},getFormInputLabel:function(a,b){var c=document.createElement("label");return c.style.fontWeight="bold",c.style.display="block",b&&(c.className+=" required"),c.textContent=a,c},getButton:function(a,b,c){var d=document.createElement("button");d.className="ui-button ui-widget ui-state-default ui-corner-all",b&&!a?(d.className+=" ui-button-icon-only",b.className+=" ui-button-icon-primary ui-icon-primary",d.appendChild(b)):b?(d.className+=" ui-button-text-icon-primary",b.className+=" ui-button-icon-primary ui-icon-primary",d.appendChild(b)):d.className+=" ui-button-text-only";var e=document.createElement("span");return e.className="ui-button-text",e.textContent=a||c||".",d.appendChild(e),d.setAttribute("title",c),d},setButtonText:function(a,b,c,d){a.innerHTML="",a.className="ui-button ui-widget ui-state-default ui-corner-all",c&&!b?(a.className+=" ui-button-icon-only",c.className+=" ui-button-icon-primary ui-icon-primary",a.appendChild(c)):c?(a.className+=" ui-button-text-icon-primary",c.className+=" ui-button-icon-primary ui-icon-primary",a.appendChild(c)):a.className+=" ui-button-text-only";var e=document.createElement("span");e.className="ui-button-text",e.textContent=b||d||".",a.appendChild(e),a.setAttribute("title",d)},getIndentedPanel:function(){var a=document.createElement("div");return a.className="ui-widget-content ui-corner-all",a.style.padding="1em 1.4em",a.style.marginBottom="20px",a},afterInputReady:function(a){a.controls||(a.controls=this.closest(a,".form-control"))},addInputError:function(a,b){a.controls&&(a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("div"),a.errmsg.className="ui-state-error",a.controls.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none")},markTabActive:function(a){a.className=a.className.replace(/\s*ui-widget-header/g,"")+" ui-state-active"},markTabInactive:function(a){a.className=a.className.replace(/\s*ui-state-active/g,"")+" ui-widget-header"}}),f.AbstractIconLib=a.extend({mapping:{collapse:"",expand:"","delete":"",edit:"",add:"",cancel:"",save:"",moveup:"",movedown:""},icon_prefix:"",getIconClass:function(a){return this.mapping[a]?this.icon_prefix+this.mapping[a]:null},getIcon:function(a){var b=this.getIconClass(a);if(!b)return null;var c=document.createElement("i");return c.className=b,c}}),f.defaults.iconlibs.bootstrap2=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-up","delete":"trash",edit:"pencil",add:"plus",cancel:"ban-circle",save:"ok",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"icon-"}),f.defaults.iconlibs.bootstrap3=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-right","delete":"remove",edit:"pencil",add:"plus",cancel:"floppy-remove",save:"floppy-saved",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"glyphicon glyphicon-"}),f.defaults.iconlibs.fontawesome3=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-right","delete":"remove",edit:"pencil",add:"plus",cancel:"ban-circle",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"icon-"}),f.defaults.iconlibs.fontawesome4=f.AbstractIconLib.extend({mapping:{collapse:"caret-square-o-down",expand:"caret-square-o-right","delete":"times",edit:"pencil",add:"plus",cancel:"ban",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"fa fa-"}),f.defaults.iconlibs.foundation2=f.AbstractIconLib.extend({mapping:{collapse:"minus",expand:"plus","delete":"remove",edit:"edit",add:"add-doc",cancel:"error",save:"checkmark",moveup:"up-arrow",movedown:"down-arrow"},icon_prefix:"foundicon-"}),f.defaults.iconlibs.foundation3=f.AbstractIconLib.extend({mapping:{collapse:"minus",expand:"plus","delete":"x",edit:"pencil",add:"page-add",cancel:"x-circle",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"fi-"}),f.defaults.iconlibs.jqueryui=f.AbstractIconLib.extend({mapping:{collapse:"triangle-1-s",expand:"triangle-1-e","delete":"trash",edit:"pencil",add:"plusthick",cancel:"closethick",save:"disk",moveup:"arrowthick-1-n",movedown:"arrowthick-1-s"},icon_prefix:"ui-icon ui-icon-"}),f.defaults.templates["default"]=function(){return{compile:function(a){var b=a.match(/{{\s*([a-zA-Z0-9\-_ \.]+)\s*}}/g),c=b&&b.length;if(!c)return function(){return a};for(var d=[],e=function(a){var c,e=b[a].replace(/[{}]+/g,"").trim().split("."),f=e.length;if(f>1){var g;c=function(b){for(g=b,a=0;f>a&&(g=g[e[a]],g);a++);return g}}else e=e[0],c=function(a){return a[e]};d.push({s:b[a],r:c})},f=0;c>f;f++)e(f);return function(b){var e,g=a+"";for(f=0;c>f;f++)e=d[f],g=g.replace(e.s,e.r(b));return g}}}},f.defaults.templates.ejs=function(){return window.EJS?{compile:function(a){var b=new window.EJS({text:a});return function(a){return b.render(a)}}}:!1},f.defaults.templates.handlebars=function(){return window.Handlebars},f.defaults.templates.hogan=function(){return window.Hogan?{compile:function(a){var b=window.Hogan.compile(a);return function(a){return b.render(a)}}}:!1},f.defaults.templates.markup=function(){return window.Mark&&window.Mark.up?{compile:function(a){return function(b){return window.Mark.up(a,b)}}}:!1},f.defaults.templates.mustache=function(){return window.Mustache?{compile:function(a){return function(b){return window.Mustache.render(a,b)}}}:!1},f.defaults.templates.swig=function(){return window.swig},f.defaults.templates.underscore=function(){return window._?{compile:function(a){return function(b){return window._.template(a,b)}}}:!1},f.defaults.theme="html",f.defaults.template="default",f.defaults.options={},f.defaults.translate=function(a,b){var c=f.defaults.languages[f.defaults.language];if(!c)throw"Unknown language "+f.defaults.language;var d=c[a]||f.defaults.languages[f.defaults.default_language][a];if("undefined"==typeof d)throw"Unknown translate string "+a;if(b)for(var e=0;e<b.length;e++)d=d.replace(new RegExp("\\{\\{"+e+"}}","g"),b[e]);return d},f.defaults.default_language="en",f.defaults.language=f.defaults.default_language,f.defaults.languages.en={error_notset:"Property must be set",error_notempty:"Value required",error_enum:"Value must be one of the enumerated values",error_anyOf:"Value must validate against at least one of the provided schemas",error_oneOf:"Value must validate against exactly one of the provided schemas. It currently validates against {{0}} of the schemas.",error_not:"Value must not validate against the provided schema",error_type_union:"Value must be one of the provided types",error_type:"Value must be of type {{0}}",error_disallow_union:"Value must not be one of the provided disallowed types",error_disallow:"Value must not be of type {{0}}",error_multipleOf:"Value must be a multiple of {{0}}",error_maximum_excl:"Value must be less than {{0}}",error_maximum_incl:"Value must at most {{0}}",error_minimum_excl:"Value must be greater than {{0}}",error_minimum_incl:"Value must be at least {{0}}",error_maxLength:"Value must be at most {{0}} characters long",error_minLength:"Value must be at least {{0}} characters long",error_pattern:"Value must match the provided pattern",error_additionalItems:"No additional items allowed in this array",error_maxItems:"Value must have at most {{0}} items",error_minItems:"Value must have at least {{0}} items",error_uniqueItems:"Array must have unique items",error_maxProperties:"Object must have at most {{0}} properties",error_minProperties:"Object must have at least {{0}} properties",error_required:"Object is missing the required property '{{0}}'",error_additional_properties:"No additional properties allowed, but property {{0}} is set",error_dependency:"Must have property {{0}}"},f.plugins={ace:{theme:""},epiceditor:{},sceditor:{},select2:{}};for(var h in f.defaults.editors)f.defaults.editors.hasOwnProperty(h)&&(f.defaults.editors[h].options=f.defaults.editors.options||{});f.defaults.resolvers.unshift(function(a){return"string"!=typeof a.type?"multiple":void 0}),f.defaults.resolvers.unshift(function(a){return!a.type&&a.properties?"object":void 0}),f.defaults.resolvers.unshift(function(a){return"string"==typeof a.type?a.type:void 0}),f.defaults.resolvers.unshift(function(a){return"boolean"===a.type?"checkbox"===a.format||a.options&&a.options.checkbox?"checkbox":"select":void 0;
+}),f.defaults.resolvers.unshift(function(a){return"any"===a.type?"multiple":void 0}),f.defaults.resolvers.unshift(function(a){return"string"===a.type&&a.media&&"base64"===a.media.binaryEncoding?"base64":void 0}),f.defaults.resolvers.unshift(function(a){return"string"===a.type&&"url"===a.format&&a.options&&a.options.upload===!0&&window.FileReader?"upload":void 0}),f.defaults.resolvers.unshift(function(a){return"array"==a.type&&"table"==a.format?"table":void 0}),f.defaults.resolvers.unshift(function(a){return a.enumSource?"select":void 0}),f.defaults.resolvers.unshift(function(a){if(a["enum"]){if("array"===a.type||"object"===a.type)return"enum";if("number"===a.type||"integer"===a.type||"string"===a.type)return"select"}}),f.defaults.resolvers.unshift(function(a){return"array"===a.type&&a.items&&!Array.isArray(a.items)&&a.uniqueItems&&a.items["enum"]&&["string","number","integer"].indexOf(a.items.type)>=0?"multiselect":void 0}),f.defaults.resolvers.unshift(function(a){return a.oneOf?"multiple":void 0}),function(){if(window.jQuery||window.Zepto){var a=window.jQuery||window.Zepto;a.jsoneditor=f.defaults,a.fn.jsoneditor=function(a){var b=this,c=this.data("jsoneditor");if("value"===a){if(!c)throw"Must initialize jsoneditor before getting/setting the value";if(!(arguments.length>1))return c.getValue();c.setValue(arguments[1])}else{if("validate"===a){if(!c)throw"Must initialize jsoneditor before validating";return arguments.length>1?c.validate(arguments[1]):c.validate()}"destroy"===a?c&&(c.destroy(),this.data("jsoneditor",null)):(c&&c.destroy(),c=new f(this.get(0),a),this.data("jsoneditor",c),c.on("change",function(){b.trigger("change")}),c.on("ready",function(){b.trigger("ready")}))}return this}}}(),window.JSONEditor=f}();
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/js-yaml.min.js b/profiles/killbill/src/main/webapp/lib/js-yaml.min.js
new file mode 100644
index 0000000..c3d07ad
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/js-yaml.min.js
@@ -0,0 +1,3 @@
+/* js-yaml 3.4.6 https://github.com/nodeca/js-yaml */
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.jsyaml=t()}}(function(){var t;return function e(t,n,i){function r(a,s){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(o)return o(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};t[a][0].call(l.exports,function(e){var n=t[a][1][e];return r(n?n:e)},l,l.exports,e,t,n,i)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a<i.length;a++)r(i[a]);return r}({1:[function(t,e,n){"use strict";function i(t){return function(){throw new Error("Function "+t+" is deprecated and cannot be used.")}}var r=t("./js-yaml/loader"),o=t("./js-yaml/dumper");e.exports.Type=t("./js-yaml/type"),e.exports.Schema=t("./js-yaml/schema"),e.exports.FAILSAFE_SCHEMA=t("./js-yaml/schema/failsafe"),e.exports.JSON_SCHEMA=t("./js-yaml/schema/json"),e.exports.CORE_SCHEMA=t("./js-yaml/schema/core"),e.exports.DEFAULT_SAFE_SCHEMA=t("./js-yaml/schema/default_safe"),e.exports.DEFAULT_FULL_SCHEMA=t("./js-yaml/schema/default_full"),e.exports.load=r.load,e.exports.loadAll=r.loadAll,e.exports.safeLoad=r.safeLoad,e.exports.safeLoadAll=r.safeLoadAll,e.exports.dump=o.dump,e.exports.safeDump=o.safeDump,e.exports.YAMLException=t("./js-yaml/exception"),e.exports.MINIMAL_SCHEMA=t("./js-yaml/schema/failsafe"),e.exports.SAFE_SCHEMA=t("./js-yaml/schema/default_safe"),e.exports.DEFAULT_SCHEMA=t("./js-yaml/schema/default_full"),e.exports.scan=i("scan"),e.exports.parse=i("parse"),e.exports.compose=i("compose"),e.exports.addConstructor=i("addConstructor")},{"./js-yaml/dumper":3,"./js-yaml/exception":4,"./js-yaml/loader":5,"./js-yaml/schema":7,"./js-yaml/schema/core":8,"./js-yaml/schema/default_full":9,"./js-yaml/schema/default_safe":10,"./js-yaml/schema/failsafe":11,"./js-yaml/schema/json":12,"./js-yaml/type":13}],2:[function(t,e,n){"use strict";function i(t){return"undefined"==typeof t||null===t}function r(t){return"object"==typeof t&&null!==t}function o(t){return Array.isArray(t)?t:i(t)?[]:[t]}function a(t,e){var n,i,r,o;if(e)for(o=Object.keys(e),n=0,i=o.length;i>n;n+=1)r=o[n],t[r]=e[r];return t}function s(t,e){var n,i="";for(n=0;e>n;n+=1)i+=t;return i}function c(t){return 0===t&&Number.NEGATIVE_INFINITY===1/t}e.exports.isNothing=i,e.exports.isObject=r,e.exports.toArray=o,e.exports.repeat=s,e.exports.isNegativeZero=c,e.exports.extend=a},{}],3:[function(t,e,n){"use strict";function i(t,e){var n,i,r,o,a,s,c;if(null===e)return{};for(n={},i=Object.keys(e),r=0,o=i.length;o>r;r+=1)a=i[r],s=String(e[a]),"!!"===a.slice(0,2)&&(a="tag:yaml.org,2002:"+a.slice(2)),c=t.compiledTypeMap[a],c&&E.call(c.styleAliases,s)&&(s=c.styleAliases[s]),n[a]=s;return n}function r(t){var e,n,i;if(e=t.toString(16).toUpperCase(),255>=t)n="x",i=2;else if(65535>=t)n="u",i=4;else{if(!(4294967295>=t))throw new O("code point within a string may not be greater than 0xFFFFFFFF");n="U",i=8}return"\\"+n+j.repeat("0",i-e.length)+e}function o(t){this.schema=t.schema||S,this.indent=Math.max(1,t.indent||2),this.skipInvalid=t.skipInvalid||!1,this.flowLevel=j.isNothing(t.flowLevel)?-1:t.flowLevel,this.styleMap=i(this.schema,t.styles||null),this.sortKeys=t.sortKeys||!1,this.lineWidth=t.lineWidth||80,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function a(t,e){for(var n,i=j.repeat(" ",e),r=0,o=-1,a="",s=t.length;s>r;)o=t.indexOf("\n",r),-1===o?(n=t.slice(r),r=s):(n=t.slice(r,o+1),r=o+1),n.length&&"\n"!==n&&(a+=i),a+=n;return a}function s(t,e){return"\n"+j.repeat(" ",t.indent*e)}function c(t,e){var n,i,r;for(n=0,i=t.implicitTypes.length;i>n;n+=1)if(r=t.implicitTypes[n],r.resolve(e))return!0;return!1}function u(t){this.source=t,this.result="",this.checkpoint=0}function l(t,e,n,i){var r,o,s,l,f,m,g,y,v,x,A,b,w,k,C,j,O,S,_,I,E;if(0===e.length)return void(t.dump="''");if(-1!==et.indexOf(e))return void(t.dump="'"+e+"'");for(r=!0,o=e.length?e.charCodeAt(0):0,s=M===o||M===e.charCodeAt(e.length-1),(K===o||W===o||G===o||z===o)&&(r=!1),s?(r=!1,l=!1,f=!1):(l=!i,f=!i),m=!0,g=new u(e),y=!1,v=0,x=0,A=t.indent*n,b=t.lineWidth,-1===b&&(b=9007199254740991),40>A?b-=A:b=40,k=0;k<e.length;k++){if(w=e.charCodeAt(k),r){if(h(w))continue;r=!1}m&&w===P&&(m=!1),C=tt[w],j=d(w),(C||j)&&(w!==N&&w!==D&&w!==P?(l=!1,f=!1):w===N&&(y=!0,m=!1,k>0&&(O=e.charCodeAt(k-1),O===M&&(f=!1,l=!1)),l&&(S=k-v,v=k,S>x&&(x=S))),w!==D&&(m=!1),g.takeUpTo(k),g.escapeChar())}if(r&&c(t,e)&&(r=!1),_="",(l||f)&&(I=0,e.charCodeAt(e.length-1)===N&&(I+=1,e.charCodeAt(e.length-2)===N&&(I+=1)),0===I?_="-":2===I&&(_="+")),f&&b>x&&(l=!1),y||(f=!1),r)t.dump=e;else if(m)t.dump="'"+e+"'";else if(l)E=p(e,b),t.dump=">"+_+"\n"+a(E,A);else if(f)_||(e=e.replace(/\n$/,"")),t.dump="|"+_+"\n"+a(e,A);else{if(!g)throw new Error("Failed to dump scalar value");g.finish(),t.dump='"'+g.result+'"'}}function p(t,e){var n,i="",r=0,o=t.length,a=/\n+$/.exec(t);for(a&&(o=a.index+1);o>r;)n=t.indexOf("\n",r),n>o||-1===n?(i&&(i+="\n\n"),i+=f(t.slice(r,o),e),r=o):(i&&(i+="\n\n"),i+=f(t.slice(r,n),e),r=n+1);return a&&"\n"!==a[0]&&(i+=a[0]),i}function f(t,e){if(""===t)return t;for(var n,i,r,o=/[^\s] [^\s]/g,a="",s=0,c=0,u=o.exec(t);u;)n=u.index,n-c>e&&(i=s!==c?s:n,a&&(a+="\n"),r=t.slice(c,i),a+=r,c=i+1),s=n+1,u=o.exec(t);return a&&(a+="\n"),a+=c!==s&&t.length-c>e?t.slice(c,s)+"\n"+t.slice(s+1):t.slice(c)}function h(t){return F!==t&&N!==t&&T!==t&&B!==t&&V!==t&&Z!==t&&J!==t&&X!==t&&U!==t&&Y!==t&&$!==t&&L!==t&&Q!==t&&R!==t&&P!==t&&D!==t&&q!==t&&H!==t&&!tt[t]&&!d(t)}function d(t){return!(t>=32&&126>=t||133===t||t>=160&&55295>=t||t>=57344&&65533>=t||t>=65536&&1114111>=t)}function m(t,e,n){var i,r,o="",a=t.tag;for(i=0,r=n.length;r>i;i+=1)A(t,e,n[i],!1,!1)&&(0!==i&&(o+=", "),o+=t.dump);t.tag=a,t.dump="["+o+"]"}function g(t,e,n,i){var r,o,a="",c=t.tag;for(r=0,o=n.length;o>r;r+=1)A(t,e+1,n[r],!0,!0)&&(i&&0===r||(a+=s(t,e)),a+="- "+t.dump);t.tag=c,t.dump=a||"[]"}function y(t,e,n){var i,r,o,a,s,c="",u=t.tag,l=Object.keys(n);for(i=0,r=l.length;r>i;i+=1)s="",0!==i&&(s+=", "),o=l[i],a=n[o],A(t,e,o,!1,!1)&&(t.dump.length>1024&&(s+="? "),s+=t.dump+": ",A(t,e,a,!1,!1)&&(s+=t.dump,c+=s));t.tag=u,t.dump="{"+c+"}"}function v(t,e,n,i){var r,o,a,c,u,l,p="",f=t.tag,h=Object.keys(n);if(t.sortKeys===!0)h.sort();else if("function"==typeof t.sortKeys)h.sort(t.sortKeys);else if(t.sortKeys)throw new O("sortKeys must be a boolean or a function");for(r=0,o=h.length;o>r;r+=1)l="",i&&0===r||(l+=s(t,e)),a=h[r],c=n[a],A(t,e+1,a,!0,!0,!0)&&(u=null!==t.tag&&"?"!==t.tag||t.dump&&t.dump.length>1024,u&&(l+=t.dump&&N===t.dump.charCodeAt(0)?"?":"? "),l+=t.dump,u&&(l+=s(t,e)),A(t,e+1,c,!0,u)&&(l+=t.dump&&N===t.dump.charCodeAt(0)?":":": ",l+=t.dump,p+=l));t.tag=f,t.dump=p||"{}"}function x(t,e,n){var i,r,o,a,s,c;for(r=n?t.explicitTypes:t.implicitTypes,o=0,a=r.length;a>o;o+=1)if(s=r[o],(s.instanceOf||s.predicate)&&(!s.instanceOf||"object"==typeof e&&e instanceof s.instanceOf)&&(!s.predicate||s.predicate(e))){if(t.tag=n?s.tag:"?",s.represent){if(c=t.styleMap[s.tag]||s.defaultStyle,"[object Function]"===I.call(s.represent))i=s.represent(e,c);else{if(!E.call(s.represent,c))throw new O("!<"+s.tag+'> tag resolver accepts not "'+c+'" style');i=s.represent[c](e,c)}t.dump=i}return!0}return!1}function A(t,e,n,i,r,o){t.tag=null,t.dump=n,x(t,n,!1)||x(t,n,!0);var a=I.call(t.dump);i&&(i=0>t.flowLevel||t.flowLevel>e);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(s=t.duplicates.indexOf(n),c=-1!==s),(null!==t.tag&&"?"!==t.tag||c||2!==t.indent&&e>0)&&(r=!1),c&&t.usedDuplicates[s])t.dump="*ref_"+s;else{if(u&&c&&!t.usedDuplicates[s]&&(t.usedDuplicates[s]=!0),"[object Object]"===a)i&&0!==Object.keys(t.dump).length?(v(t,e,t.dump,r),c&&(t.dump="&ref_"+s+t.dump)):(y(t,e,t.dump),c&&(t.dump="&ref_"+s+" "+t.dump));else if("[object Array]"===a)i&&0!==t.dump.length?(g(t,e,t.dump,r),c&&(t.dump="&ref_"+s+t.dump)):(m(t,e,t.dump),c&&(t.dump="&ref_"+s+" "+t.dump));else{if("[object String]"!==a){if(t.skipInvalid)return!1;throw new O("unacceptable kind of an object to dump "+a)}"?"!==t.tag&&l(t,t.dump,e,o)}null!==t.tag&&"?"!==t.tag&&(t.dump="!<"+t.tag+"> "+t.dump)}return!0}function b(t,e){var n,i,r=[],o=[];for(w(t,r,o),n=0,i=o.length;i>n;n+=1)e.duplicates.push(r[o[n]]);e.usedDuplicates=new Array(i)}function w(t,e,n){var i,r,o;if(null!==t&&"object"==typeof t)if(r=e.indexOf(t),-1!==r)-1===n.indexOf(r)&&n.push(r);else if(e.push(t),Array.isArray(t))for(r=0,o=t.length;o>r;r+=1)w(t[r],e,n);else for(i=Object.keys(t),r=0,o=i.length;o>r;r+=1)w(t[i[r]],e,n)}function k(t,e){e=e||{};var n=new o(e);return b(t,n),A(n,0,t,!0,!0)?n.dump+"\n":""}function C(t,e){return k(t,j.extend({schema:_},e))}var j=t("./common"),O=t("./exception"),S=t("./schema/default_full"),_=t("./schema/default_safe"),I=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=9,N=10,T=13,M=32,L=33,D=34,U=35,q=37,Y=38,P=39,$=42,B=44,K=45,H=58,R=62,W=63,G=64,V=91,Z=93,z=96,J=123,Q=124,X=125,tt={};tt[0]="\\0",tt[7]="\\a",tt[8]="\\b",tt[9]="\\t",tt[10]="\\n",tt[11]="\\v",tt[12]="\\f",tt[13]="\\r",tt[27]="\\e",tt[34]='\\"',tt[92]="\\\\",tt[133]="\\N",tt[160]="\\_",tt[8232]="\\L",tt[8233]="\\P";var et=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];u.prototype.takeUpTo=function(t){var e;if(t<this.checkpoint)throw e=new Error("position should be > checkpoint"),e.position=t,e.checkpoint=this.checkpoint,e;return this.result+=this.source.slice(this.checkpoint,t),this.checkpoint=t,this},u.prototype.escapeChar=function(){var t,e;return t=this.source.charCodeAt(this.checkpoint),e=tt[t]||r(t),this.result+=e,this.checkpoint+=1,this},u.prototype.finish=function(){this.source.length>this.checkpoint&&this.takeUpTo(this.source.length)},e.exports.dump=k,e.exports.safeDump=C},{"./common":2,"./exception":4,"./schema/default_full":9,"./schema/default_safe":10}],4:[function(t,e,n){"use strict";function i(t,e){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||"",this.name="YAMLException",this.reason=t,this.mark=e,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():"")}var r=t("inherit");r(i,Error),i.prototype.toString=function(t){var e=this.name+": ";return e+=this.reason||"(unknown reason)",!t&&this.mark&&(e+=" "+this.mark.toString()),e},e.exports=i},{inherit:31}],5:[function(t,e,n){"use strict";function i(t){return 10===t||13===t}function r(t){return 9===t||32===t}function o(t){return 9===t||32===t||10===t||13===t}function a(t){return 44===t||91===t||93===t||123===t||125===t}function s(t){var e;return t>=48&&57>=t?t-48:(e=32|t,e>=97&&102>=e?e-97+10:-1)}function c(t){return 120===t?2:117===t?4:85===t?8:0}function u(t){return t>=48&&57>=t?t-48:-1}function l(t){return 48===t?"\x00":97===t?"":98===t?"\b":116===t?"	":9===t?"	":110===t?"\n":118===t?"":102===t?"\f":114===t?"\r":101===t?"":32===t?" ":34===t?'"':47===t?"/":92===t?"\\":78===t?"…":95===t?" ":76===t?"\u2028":80===t?"\u2029":""}function p(t){return 65535>=t?String.fromCharCode(t):String.fromCharCode((t-65536>>10)+55296,(t-65536&1023)+56320)}function f(t,e){this.input=t,this.filename=e.filename||null,this.schema=e.schema||H,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function h(t,e){return new $(e,new B(t.filename,t.input,t.position,t.line,t.position-t.lineStart))}function d(t,e){throw h(t,e)}function m(t,e){t.onWarning&&t.onWarning.call(null,h(t,e))}function g(t,e,n,i){var r,o,a,s;if(n>e){if(s=t.input.slice(e,n),i)for(r=0,o=s.length;o>r;r+=1)a=s.charCodeAt(r),9===a||a>=32&&1114111>=a||d(t,"expected valid JSON character");else X.test(s)&&d(t,"the stream contains non-printable characters");t.result+=s}}function y(t,e,n){var i,r,o,a;for(P.isObject(n)||d(t,"cannot merge mappings; the provided source object is unacceptable"),i=Object.keys(n),o=0,a=i.length;a>o;o+=1)r=i[o],R.call(e,r)||(e[r]=n[r])}function v(t,e,n,i,r){var o,a;if(i=String(i),null===e&&(e={}),"tag:yaml.org,2002:merge"===n)if(Array.isArray(r))for(o=0,a=r.length;a>o;o+=1)y(t,e,r[o]);else y(t,e,r);else e[i]=r;return e}function x(t){var e;e=t.input.charCodeAt(t.position),10===e?t.position++:13===e?(t.position++,10===t.input.charCodeAt(t.position)&&t.position++):d(t,"a line break is expected"),t.line+=1,t.lineStart=t.position}function A(t,e,n){for(var o=0,a=t.input.charCodeAt(t.position);0!==a;){for(;r(a);)a=t.input.charCodeAt(++t.position);if(e&&35===a)do a=t.input.charCodeAt(++t.position);while(10!==a&&13!==a&&0!==a);if(!i(a))break;for(x(t),a=t.input.charCodeAt(t.position),o++,t.lineIndent=0;32===a;)t.lineIndent++,a=t.input.charCodeAt(++t.position)}return-1!==n&&0!==o&&t.lineIndent<n&&m(t,"deficient indentation"),o}function b(t){var e,n=t.position;return e=t.input.charCodeAt(n),45!==e&&46!==e||t.input.charCodeAt(n+1)!==e||t.input.charCodeAt(n+2)!==e||(n+=3,e=t.input.charCodeAt(n),0!==e&&!o(e))?!1:!0}function w(t,e){1===e?t.result+=" ":e>1&&(t.result+=P.repeat("\n",e-1))}function k(t,e,n){var s,c,u,l,p,f,h,d,m,y=t.kind,v=t.result;if(m=t.input.charCodeAt(t.position),o(m)||a(m)||35===m||38===m||42===m||33===m||124===m||62===m||39===m||34===m||37===m||64===m||96===m)return!1;if((63===m||45===m)&&(c=t.input.charCodeAt(t.position+1),o(c)||n&&a(c)))return!1;for(t.kind="scalar",t.result="",u=l=t.position,p=!1;0!==m;){if(58===m){if(c=t.input.charCodeAt(t.position+1),o(c)||n&&a(c))break}else if(35===m){if(s=t.input.charCodeAt(t.position-1),o(s))break}else{if(t.position===t.lineStart&&b(t)||n&&a(m))break;if(i(m)){if(f=t.line,h=t.lineStart,d=t.lineIndent,A(t,!1,-1),t.lineIndent>=e){p=!0,m=t.input.charCodeAt(t.position);continue}t.position=l,t.line=f,t.lineStart=h,t.lineIndent=d;break}}p&&(g(t,u,l,!1),w(t,t.line-f),u=l=t.position,p=!1),r(m)||(l=t.position+1),m=t.input.charCodeAt(++t.position)}return g(t,u,l,!1),t.result?!0:(t.kind=y,t.result=v,!1)}function C(t,e){var n,r,o;if(n=t.input.charCodeAt(t.position),39!==n)return!1;for(t.kind="scalar",t.result="",t.position++,r=o=t.position;0!==(n=t.input.charCodeAt(t.position));)if(39===n){if(g(t,r,t.position,!0),n=t.input.charCodeAt(++t.position),39!==n)return!0;r=o=t.position,t.position++}else i(n)?(g(t,r,o,!0),w(t,A(t,!1,e)),r=o=t.position):t.position===t.lineStart&&b(t)?d(t,"unexpected end of the document within a single quoted scalar"):(t.position++,o=t.position);d(t,"unexpected end of the stream within a single quoted scalar")}function j(t,e){var n,r,o,a,u,l;if(l=t.input.charCodeAt(t.position),34!==l)return!1;for(t.kind="scalar",t.result="",t.position++,n=r=t.position;0!==(l=t.input.charCodeAt(t.position));){if(34===l)return g(t,n,t.position,!0),t.position++,!0;if(92===l){if(g(t,n,t.position,!0),l=t.input.charCodeAt(++t.position),i(l))A(t,!1,e);else if(256>l&&rt[l])t.result+=ot[l],t.position++;else if((u=c(l))>0){for(o=u,a=0;o>0;o--)l=t.input.charCodeAt(++t.position),(u=s(l))>=0?a=(a<<4)+u:d(t,"expected hexadecimal character");t.result+=p(a),t.position++}else d(t,"unknown escape sequence");n=r=t.position}else i(l)?(g(t,n,r,!0),w(t,A(t,!1,e)),n=r=t.position):t.position===t.lineStart&&b(t)?d(t,"unexpected end of the document within a double quoted scalar"):(t.position++,r=t.position)}d(t,"unexpected end of the stream within a double quoted scalar")}function O(t,e){var n,i,r,a,s,c,u,l,p,f,h,m=!0,g=t.tag,y=t.anchor;if(h=t.input.charCodeAt(t.position),91===h)a=93,u=!1,i=[];else{if(123!==h)return!1;a=125,u=!0,i={}}for(null!==t.anchor&&(t.anchorMap[t.anchor]=i),h=t.input.charCodeAt(++t.position);0!==h;){if(A(t,!0,e),h=t.input.charCodeAt(t.position),h===a)return t.position++,t.tag=g,t.anchor=y,t.kind=u?"mapping":"sequence",t.result=i,!0;m||d(t,"missed comma between flow collection entries"),p=l=f=null,s=c=!1,63===h&&(r=t.input.charCodeAt(t.position+1),o(r)&&(s=c=!0,t.position++,A(t,!0,e))),n=t.line,T(t,e,W,!1,!0),p=t.tag,l=t.result,A(t,!0,e),h=t.input.charCodeAt(t.position),!c&&t.line!==n||58!==h||(s=!0,h=t.input.charCodeAt(++t.position),A(t,!0,e),T(t,e,W,!1,!0),f=t.result),u?v(t,i,p,l,f):i.push(s?v(t,null,p,l,f):l),A(t,!0,e),h=t.input.charCodeAt(t.position),44===h?(m=!0,h=t.input.charCodeAt(++t.position)):m=!1}d(t,"unexpected end of the stream within a flow collection")}function S(t,e){var n,o,a,s,c=z,l=!1,p=e,f=0,h=!1;if(s=t.input.charCodeAt(t.position),124===s)o=!1;else{if(62!==s)return!1;o=!0}for(t.kind="scalar",t.result="";0!==s;)if(s=t.input.charCodeAt(++t.position),43===s||45===s)z===c?c=43===s?Q:J:d(t,"repeat of a chomping mode identifier");else{if(!((a=u(s))>=0))break;0===a?d(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):l?d(t,"repeat of an indentation width identifier"):(p=e+a-1,l=!0)}if(r(s)){do s=t.input.charCodeAt(++t.position);while(r(s));if(35===s)do s=t.input.charCodeAt(++t.position);while(!i(s)&&0!==s)}for(;0!==s;){for(x(t),t.lineIndent=0,s=t.input.charCodeAt(t.position);(!l||t.lineIndent<p)&&32===s;)t.lineIndent++,s=t.input.charCodeAt(++t.position);if(!l&&t.lineIndent>p&&(p=t.lineIndent),i(s))f++;else{if(t.lineIndent<p){c===Q?t.result+=P.repeat("\n",f):c===z&&l&&(t.result+="\n");break}for(o?r(s)?(h=!0,t.result+=P.repeat("\n",f+1)):h?(h=!1,t.result+=P.repeat("\n",f+1)):0===f?l&&(t.result+=" "):t.result+=P.repeat("\n",f):l?t.result+=P.repeat("\n",f+1):t.result+=P.repeat("\n",f),l=!0,f=0,n=t.position;!i(s)&&0!==s;)s=t.input.charCodeAt(++t.position);g(t,n,t.position,!1)}}return!0}function _(t,e){var n,i,r,a=t.tag,s=t.anchor,c=[],u=!1;for(null!==t.anchor&&(t.anchorMap[t.anchor]=c),r=t.input.charCodeAt(t.position);0!==r&&45===r&&(i=t.input.charCodeAt(t.position+1),o(i));)if(u=!0,t.position++,A(t,!0,-1)&&t.lineIndent<=e)c.push(null),r=t.input.charCodeAt(t.position);else if(n=t.line,T(t,e,V,!1,!0),c.push(t.result),A(t,!0,-1),r=t.input.charCodeAt(t.position),(t.line===n||t.lineIndent>e)&&0!==r)d(t,"bad indentation of a sequence entry");else if(t.lineIndent<e)break;return u?(t.tag=a,t.anchor=s,t.kind="sequence",t.result=c,!0):!1}function I(t,e,n){var i,a,s,c,u=t.tag,l=t.anchor,p={},f=null,h=null,m=null,g=!1,y=!1;for(null!==t.anchor&&(t.anchorMap[t.anchor]=p),c=t.input.charCodeAt(t.position);0!==c;){if(i=t.input.charCodeAt(t.position+1),s=t.line,63!==c&&58!==c||!o(i)){if(!T(t,n,G,!1,!0))break;if(t.line===s){for(c=t.input.charCodeAt(t.position);r(c);)c=t.input.charCodeAt(++t.position);if(58===c)c=t.input.charCodeAt(++t.position),o(c)||d(t,"a whitespace character is expected after the key-value separator within a block mapping"),g&&(v(t,p,f,h,null),f=h=m=null),y=!0,g=!1,a=!1,f=t.tag,h=t.result;else{if(!y)return t.tag=u,t.anchor=l,!0;d(t,"can not read an implicit mapping pair; a colon is missed")}}else{if(!y)return t.tag=u,t.anchor=l,!0;d(t,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===c?(g&&(v(t,p,f,h,null),f=h=m=null),y=!0,g=!0,a=!0):g?(g=!1,a=!0):d(t,"incomplete explicit mapping pair; a key node is missed"),t.position+=1,c=i;if((t.line===s||t.lineIndent>e)&&(T(t,e,Z,!0,a)&&(g?h=t.result:m=t.result),g||(v(t,p,f,h,m),f=h=m=null),A(t,!0,-1),c=t.input.charCodeAt(t.position)),t.lineIndent>e&&0!==c)d(t,"bad indentation of a mapping entry");else if(t.lineIndent<e)break}return g&&v(t,p,f,h,null),y&&(t.tag=u,t.anchor=l,t.kind="mapping",t.result=p),y}function E(t){var e,n,i,r,a=!1,s=!1;if(r=t.input.charCodeAt(t.position),33!==r)return!1;if(null!==t.tag&&d(t,"duplication of a tag property"),r=t.input.charCodeAt(++t.position),60===r?(a=!0,r=t.input.charCodeAt(++t.position)):33===r?(s=!0,n="!!",r=t.input.charCodeAt(++t.position)):n="!",e=t.position,a){do r=t.input.charCodeAt(++t.position);while(0!==r&&62!==r);t.position<t.length?(i=t.input.slice(e,t.position),r=t.input.charCodeAt(++t.position)):d(t,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!o(r);)33===r&&(s?d(t,"tag suffix cannot contain exclamation marks"):(n=t.input.slice(e-1,t.position+1),nt.test(n)||d(t,"named tag handle cannot contain such characters"),s=!0,e=t.position+1)),r=t.input.charCodeAt(++t.position);i=t.input.slice(e,t.position),et.test(i)&&d(t,"tag suffix cannot contain flow indicator characters")}return i&&!it.test(i)&&d(t,"tag name cannot contain such characters: "+i),a?t.tag=i:R.call(t.tagMap,n)?t.tag=t.tagMap[n]+i:"!"===n?t.tag="!"+i:"!!"===n?t.tag="tag:yaml.org,2002:"+i:d(t,'undeclared tag handle "'+n+'"'),!0}function F(t){var e,n;if(n=t.input.charCodeAt(t.position),38!==n)return!1;for(null!==t.anchor&&d(t,"duplication of an anchor property"),n=t.input.charCodeAt(++t.position),e=t.position;0!==n&&!o(n)&&!a(n);)n=t.input.charCodeAt(++t.position);return t.position===e&&d(t,"name of an anchor node must contain at least one character"),t.anchor=t.input.slice(e,t.position),!0}function N(t){var e,n,i;if(i=t.input.charCodeAt(t.position),42!==i)return!1;for(i=t.input.charCodeAt(++t.position),e=t.position;0!==i&&!o(i)&&!a(i);)i=t.input.charCodeAt(++t.position);return t.position===e&&d(t,"name of an alias node must contain at least one character"),n=t.input.slice(e,t.position),t.anchorMap.hasOwnProperty(n)||d(t,'unidentified alias "'+n+'"'),t.result=t.anchorMap[n],A(t,!0,-1),!0}function T(t,e,n,i,r){var o,a,s,c,u,l,p,f,h=1,m=!1,g=!1;if(t.tag=null,t.anchor=null,t.kind=null,t.result=null,o=a=s=Z===n||V===n,i&&A(t,!0,-1)&&(m=!0,t.lineIndent>e?h=1:t.lineIndent===e?h=0:t.lineIndent<e&&(h=-1)),1===h)for(;E(t)||F(t);)A(t,!0,-1)?(m=!0,s=o,t.lineIndent>e?h=1:t.lineIndent===e?h=0:t.lineIndent<e&&(h=-1)):s=!1;if(s&&(s=m||r),(1===h||Z===n)&&(p=W===n||G===n?e:e+1,f=t.position-t.lineStart,1===h?s&&(_(t,f)||I(t,f,p))||O(t,p)?g=!0:(a&&S(t,p)||C(t,p)||j(t,p)?g=!0:N(t)?(g=!0,(null!==t.tag||null!==t.anchor)&&d(t,"alias node should not have any properties")):k(t,p,W===n)&&(g=!0,null===t.tag&&(t.tag="?")),null!==t.anchor&&(t.anchorMap[t.anchor]=t.result)):0===h&&(g=s&&_(t,f))),null!==t.tag&&"!"!==t.tag)if("?"===t.tag){for(c=0,u=t.implicitTypes.length;u>c;c+=1)if(l=t.implicitTypes[c],l.resolve(t.result)){t.result=l.construct(t.result),t.tag=l.tag,null!==t.anchor&&(t.anchorMap[t.anchor]=t.result);break}}else R.call(t.typeMap,t.tag)?(l=t.typeMap[t.tag],null!==t.result&&l.kind!==t.kind&&d(t,"unacceptable node kind for !<"+t.tag+'> tag; it should be "'+l.kind+'", not "'+t.kind+'"'),l.resolve(t.result)?(t.result=l.construct(t.result),null!==t.anchor&&(t.anchorMap[t.anchor]=t.result)):d(t,"cannot resolve a node with !<"+t.tag+"> explicit tag")):d(t,"unknown tag !<"+t.tag+">");return null!==t.tag||null!==t.anchor||g}function M(t){var e,n,a,s,c=t.position,u=!1;for(t.version=null,t.checkLineBreaks=t.legacy,t.tagMap={},t.anchorMap={};0!==(s=t.input.charCodeAt(t.position))&&(A(t,!0,-1),s=t.input.charCodeAt(t.position),!(t.lineIndent>0||37!==s));){for(u=!0,s=t.input.charCodeAt(++t.position),e=t.position;0!==s&&!o(s);)s=t.input.charCodeAt(++t.position);for(n=t.input.slice(e,t.position),a=[],n.length<1&&d(t,"directive name must not be less than one character in length");0!==s;){for(;r(s);)s=t.input.charCodeAt(++t.position);if(35===s){do s=t.input.charCodeAt(++t.position);while(0!==s&&!i(s));break}if(i(s))break;for(e=t.position;0!==s&&!o(s);)s=t.input.charCodeAt(++t.position);a.push(t.input.slice(e,t.position))}0!==s&&x(t),R.call(st,n)?st[n](t,n,a):m(t,'unknown document directive "'+n+'"')}return A(t,!0,-1),0===t.lineIndent&&45===t.input.charCodeAt(t.position)&&45===t.input.charCodeAt(t.position+1)&&45===t.input.charCodeAt(t.position+2)?(t.position+=3,A(t,!0,-1)):u&&d(t,"directives end mark is expected"),T(t,t.lineIndent-1,Z,!1,!0),A(t,!0,-1),t.checkLineBreaks&&tt.test(t.input.slice(c,t.position))&&m(t,"non-ASCII line breaks are interpreted as content"),t.documents.push(t.result),t.position===t.lineStart&&b(t)?void(46===t.input.charCodeAt(t.position)&&(t.position+=3,A(t,!0,-1))):void(t.position<t.length-1&&d(t,"end of the stream or a document separator is expected"))}function L(t,e){t=String(t),e=e||{},0!==t.length&&(10!==t.charCodeAt(t.length-1)&&13!==t.charCodeAt(t.length-1)&&(t+="\n"),65279===t.charCodeAt(0)&&(t=t.slice(1)));var n=new f(t,e);for(n.input+="\x00";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)M(n);return n.documents}function D(t,e,n){var i,r,o=L(t,n);for(i=0,r=o.length;r>i;i+=1)e(o[i])}function U(t,e){var n=L(t,e);if(0===n.length)return void 0;if(1===n.length)return n[0];throw new $("expected a single document in the stream, but found more")}function q(t,e,n){D(t,e,P.extend({schema:K},n))}function Y(t,e){return U(t,P.extend({schema:K},e))}for(var P=t("./common"),$=t("./exception"),B=t("./mark"),K=t("./schema/default_safe"),H=t("./schema/default_full"),R=Object.prototype.hasOwnProperty,W=1,G=2,V=3,Z=4,z=1,J=2,Q=3,X=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,tt=/[\x85\u2028\u2029]/,et=/[,\[\]\{\}]/,nt=/^(?:!|!!|![a-z\-]+!)$/i,it=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i,rt=new Array(256),ot=new Array(256),at=0;256>at;at++)rt[at]=l(at)?1:0,ot[at]=l(at);var st={YAML:function(t,e,n){var i,r,o;null!==t.version&&d(t,"duplication of %YAML directive"),1!==n.length&&d(t,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===i&&d(t,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&d(t,"unacceptable YAML version of the document"),t.version=n[0],t.checkLineBreaks=2>o,1!==o&&2!==o&&m(t,"unsupported YAML version of the document")},TAG:function(t,e,n){var i,r;2!==n.length&&d(t,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],nt.test(i)||d(t,"ill-formed tag handle (first argument) of the TAG directive"),R.call(t.tagMap,i)&&d(t,'there is a previously declared suffix for "'+i+'" tag handle'),it.test(r)||d(t,"ill-formed tag prefix (second argument) of the TAG directive"),t.tagMap[i]=r}};e.exports.loadAll=D,e.exports.load=U,e.exports.safeLoadAll=q,e.exports.safeLoad=Y},{"./common":2,"./exception":4,"./mark":6,"./schema/default_full":9,"./schema/default_safe":10}],6:[function(t,e,n){"use strict";function i(t,e,n,i,r){this.name=t,this.buffer=e,this.position=n,this.line=i,this.column=r}var r=t("./common");i.prototype.getSnippet=function(t,e){var n,i,o,a,s;if(!this.buffer)return null;for(t=t||4,e=e||75,n="",i=this.position;i>0&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(i-1));)if(i-=1,this.position-i>e/2-1){n=" ... ",i+=5;break}for(o="",a=this.position;a<this.buffer.length&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(a));)if(a+=1,a-this.position>e/2-1){o=" ... ",a-=5;break}return s=this.buffer.slice(i,a),r.repeat(" ",t)+n+s+o+"\n"+r.repeat(" ",t+this.position-i+n.length)+"^"},i.prototype.toString=function(t){var e,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),t||(e=this.getSnippet(),e&&(n+=":\n"+e)),n},e.exports=i},{"./common":2}],7:[function(t,e,n){"use strict";function i(t,e,n){var r=[];return t.include.forEach(function(t){n=i(t,e,n)}),t[e].forEach(function(t){n.forEach(function(e,n){e.tag===t.tag&&r.push(n)}),n.push(t)}),n.filter(function(t,e){return-1===r.indexOf(e)})}function r(){function t(t){i[t.tag]=t}var e,n,i={};for(e=0,n=arguments.length;n>e;e+=1)arguments[e].forEach(t);return i}function o(t){this.include=t.include||[],this.implicit=t.implicit||[],this.explicit=t.explicit||[],this.implicit.forEach(function(t){if(t.loadKind&&"scalar"!==t.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=i(this,"implicit",[]),this.compiledExplicit=i(this,"explicit",[]),this.compiledTypeMap=r(this.compiledImplicit,this.compiledExplicit)}var a=t("./common"),s=t("./exception"),c=t("./type");o.DEFAULT=null,o.create=function(){var t,e;switch(arguments.length){case 1:t=o.DEFAULT,e=arguments[0];break;case 2:t=arguments[0],e=arguments[1];break;default:throw new s("Wrong number of arguments for Schema.create function")}if(t=a.toArray(t),e=a.toArray(e),!t.every(function(t){return t instanceof o}))throw new s("Specified list of super schemas (or a single Schema object) contains a non-Schema object.");if(!e.every(function(t){return t instanceof c}))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");return new o({include:t,explicit:e})},e.exports=o},{"./common":2,"./exception":4,"./type":13}],8:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./json")]})},{"../schema":7,"./json":12}],9:[function(t,e,n){"use strict";var i=t("../schema");e.exports=i.DEFAULT=new i({include:[t("./default_safe")],explicit:[t("../type/js/undefined"),t("../type/js/regexp"),t("../type/js/function")]})},{"../schema":7,"../type/js/function":18,"../type/js/regexp":19,"../type/js/undefined":20,"./default_safe":10}],10:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./core")],implicit:[t("../type/timestamp"),t("../type/merge")],explicit:[t("../type/binary"),t("../type/omap"),t("../type/pairs"),t("../type/set")]})},{"../schema":7,"../type/binary":14,"../type/merge":22,"../type/omap":24,"../type/pairs":25,"../type/set":27,"../type/timestamp":29,"./core":8}],11:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({explicit:[t("../type/str"),t("../type/seq"),t("../type/map")]})},{"../schema":7,"../type/map":21,"../type/seq":26,"../type/str":28}],12:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./failsafe")],implicit:[t("../type/null"),t("../type/bool"),t("../type/int"),t("../type/float")]})},{"../schema":7,"../type/bool":15,"../type/float":16,"../type/int":17,"../type/null":23,"./failsafe":11}],13:[function(t,e,n){"use strict";function i(t){var e={};return null!==t&&Object.keys(t).forEach(function(n){t[n].forEach(function(t){e[String(t)]=n})}),e}function r(t,e){if(e=e||{},Object.keys(e).forEach(function(e){if(-1===a.indexOf(e))throw new o('Unknown option "'+e+'" is met in definition of "'+t+'" YAML type.')}),this.tag=t,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(t){return t},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.defaultStyle=e.defaultStyle||null,this.styleAliases=i(e.styleAliases||null),-1===s.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}var o=t("./exception"),a=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],s=["scalar","sequence","mapping"];e.exports=r},{"./exception":4}],14:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;var e,n,i=0,r=t.length,o=u;for(n=0;r>n;n++)if(e=o.indexOf(t.charAt(n)),!(e>64)){if(0>e)return!1;i+=6}return i%8===0}function r(t){var e,n,i=t.replace(/[\r\n=]/g,""),r=i.length,o=u,a=0,c=[];for(e=0;r>e;e++)e%4===0&&e&&(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)),a=a<<6|o.indexOf(i.charAt(e));return n=r%4*6,0===n?(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)):18===n?(c.push(a>>10&255),c.push(a>>2&255)):12===n&&c.push(a>>4&255),s?new s(c):c}function o(t){var e,n,i="",r=0,o=t.length,a=u;for(e=0;o>e;e++)e%3===0&&e&&(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+t[e];return n=o%3,0===n?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}function a(t){return s&&s.isBuffer(t)}var s=t("buffer").Buffer,c=t("../type"),u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";e.exports=new c("tag:yaml.org,2002:binary",{kind:"scalar",resolve:i,
+construct:r,predicate:a,represent:o})},{"../type":13,buffer:30}],15:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;var e=t.length;return 4===e&&("true"===t||"True"===t||"TRUE"===t)||5===e&&("false"===t||"False"===t||"FALSE"===t)}function r(t){return"true"===t||"True"===t||"TRUE"===t}function o(t){return"[object Boolean]"===Object.prototype.toString.call(t)}var a=t("../type");e.exports=new a("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{lowercase:function(t){return t?"true":"false"},uppercase:function(t){return t?"TRUE":"FALSE"},camelcase:function(t){return t?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(t,e,n){"use strict";function i(t){return null===t?!1:u.test(t)?!0:!1}function r(t){var e,n,i,r;return e=t.replace(/_/g,"").toLowerCase(),n="-"===e[0]?-1:1,r=[],0<="+-".indexOf(e[0])&&(e=e.slice(1)),".inf"===e?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===e?NaN:0<=e.indexOf(":")?(e.split(":").forEach(function(t){r.unshift(parseFloat(t,10))}),e=0,i=1,r.forEach(function(t){e+=t*i,i*=60}),n*e):n*parseFloat(e,10)}function o(t,e){var n;if(isNaN(t))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===t)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===t)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(t))return"-0.0";return n=t.toString(10),l.test(n)?n.replace("e",".e"):n}function a(t){return"[object Number]"===Object.prototype.toString.call(t)&&(0!==t%1||s.isNegativeZero(t))}var s=t("../common"),c=t("../type"),u=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),l=/^[-+]?[0-9]+e/;e.exports=new c("tag:yaml.org,2002:float",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o,defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(t,e,n){"use strict";function i(t){return t>=48&&57>=t||t>=65&&70>=t||t>=97&&102>=t}function r(t){return t>=48&&55>=t}function o(t){return t>=48&&57>=t}function a(t){if(null===t)return!1;var e,n=t.length,a=0,s=!1;if(!n)return!1;if(e=t[a],("-"===e||"+"===e)&&(e=t[++a]),"0"===e){if(a+1===n)return!0;if(e=t[++a],"b"===e){for(a++;n>a;a++)if(e=t[a],"_"!==e){if("0"!==e&&"1"!==e)return!1;s=!0}return s}if("x"===e){for(a++;n>a;a++)if(e=t[a],"_"!==e){if(!i(t.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(e=t[a],"_"!==e){if(!r(t.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(e=t[a],"_"!==e){if(":"===e)break;if(!o(t.charCodeAt(a)))return!1;s=!0}return s?":"!==e?!0:/^(:[0-5]?[0-9])+$/.test(t.slice(a)):!1}function s(t){var e,n,i=t,r=1,o=[];return-1!==i.indexOf("_")&&(i=i.replace(/_/g,"")),e=i[0],("-"===e||"+"===e)&&("-"===e&&(r=-1),i=i.slice(1),e=i[0]),"0"===i?0:"0"===e?"b"===i[1]?r*parseInt(i.slice(2),2):"x"===i[1]?r*parseInt(i,16):r*parseInt(i,8):-1!==i.indexOf(":")?(i.split(":").forEach(function(t){o.unshift(parseInt(t,10))}),i=0,n=1,o.forEach(function(t){i+=t*n,n*=60}),r*i):r*parseInt(i,10)}function c(t){return"[object Number]"===Object.prototype.toString.call(t)&&0===t%1&&!u.isNegativeZero(t)}var u=t("../common"),l=t("../type");e.exports=new l("tag:yaml.org,2002:int",{kind:"scalar",resolve:a,construct:s,predicate:c,represent:{binary:function(t){return"0b"+t.toString(2)},octal:function(t){return"0"+t.toString(8)},decimal:function(t){return t.toString(10)},hexadecimal:function(t){return"0x"+t.toString(16).toUpperCase()}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},{"../common":2,"../type":13}],18:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;try{var e="("+t+")",n=s.parse(e,{range:!0});return"Program"!==n.type||1!==n.body.length||"ExpressionStatement"!==n.body[0].type||"FunctionExpression"!==n.body[0].expression.type?!1:!0}catch(i){return!1}}function r(t){var e,n="("+t+")",i=s.parse(n,{range:!0}),r=[];if("Program"!==i.type||1!==i.body.length||"ExpressionStatement"!==i.body[0].type||"FunctionExpression"!==i.body[0].expression.type)throw new Error("Failed to resolve function");return i.body[0].expression.params.forEach(function(t){r.push(t.name)}),e=i.body[0].expression.body.range,new Function(r,n.slice(e[0]+1,e[1]-1))}function o(t){return t.toString()}function a(t){return"[object Function]"===Object.prototype.toString.call(t)}var s;try{s=t("esprima")}catch(c){"undefined"!=typeof window&&(s=window.esprima)}var u=t("../../type");e.exports=new u("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13,esprima:"esprima"}],19:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;if(0===t.length)return!1;var e=t,n=/\/([gim]*)$/.exec(t),i="";if("/"===e[0]){if(n&&(i=n[1]),i.length>3)return!1;if("/"!==e[e.length-i.length-1])return!1;e=e.slice(1,e.length-i.length-1)}try{return!0}catch(r){return!1}}function r(t){var e=t,n=/\/([gim]*)$/.exec(t),i="";return"/"===e[0]&&(n&&(i=n[1]),e=e.slice(1,e.length-i.length-1)),new RegExp(e,i)}function o(t){var e="/"+t.source+"/";return t.global&&(e+="g"),t.multiline&&(e+="m"),t.ignoreCase&&(e+="i"),e}function a(t){return"[object RegExp]"===Object.prototype.toString.call(t)}var s=t("../../type");e.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],20:[function(t,e,n){"use strict";function i(){return!0}function r(){return void 0}function o(){return""}function a(t){return"undefined"==typeof t}var s=t("../../type");e.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],21:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:map",{kind:"mapping",construct:function(t){return null!==t?t:{}}})},{"../type":13}],22:[function(t,e,n){"use strict";function i(t){return"<<"===t||null===t}var r=t("../type");e.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},{"../type":13}],23:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e=t.length;return 1===e&&"~"===t||4===e&&("null"===t||"Null"===t||"NULL"===t)}function r(){return null}function o(t){return null===t}var a=t("../type");e.exports=new a("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},{"../type":13}],24:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n,i,r,o,c=[],u=t;for(e=0,n=u.length;n>e;e+=1){if(i=u[e],o=!1,"[object Object]"!==s.call(i))return!1;for(r in i)if(a.call(i,r)){if(o)return!1;o=!0}if(!o)return!1;if(-1!==c.indexOf(r))return!1;c.push(r)}return!0}function r(t){return null!==t?t:[]}var o=t("../type"),a=Object.prototype.hasOwnProperty,s=Object.prototype.toString;e.exports=new o("tag:yaml.org,2002:omap",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],25:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n,i,r,o,s=t;for(o=new Array(s.length),e=0,n=s.length;n>e;e+=1){if(i=s[e],"[object Object]"!==a.call(i))return!1;if(r=Object.keys(i),1!==r.length)return!1;o[e]=[r[0],i[r[0]]]}return!0}function r(t){if(null===t)return[];var e,n,i,r,o,a=t;for(o=new Array(a.length),e=0,n=a.length;n>e;e+=1)i=a[e],r=Object.keys(i),o[e]=[r[0],i[r[0]]];return o}var o=t("../type"),a=Object.prototype.toString;e.exports=new o("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],26:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(t){return null!==t?t:[]}})},{"../type":13}],27:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n=t;for(e in n)if(a.call(n,e)&&null!==n[e])return!1;return!0}function r(t){return null!==t?t:{}}var o=t("../type"),a=Object.prototype.hasOwnProperty;e.exports=new o("tag:yaml.org,2002:set",{kind:"mapping",resolve:i,construct:r})},{"../type":13}],28:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:str",{kind:"scalar",construct:function(t){return null!==t?t:""}})},{"../type":13}],29:[function(t,e,n){"use strict";function i(t){return null===t?!1:null===s.exec(t)?!1:!0}function r(t){var e,n,i,r,o,a,c,u,l,p,f=0,h=null;if(e=s.exec(t),null===e)throw new Error("Date resolve error");if(n=+e[1],i=+e[2]-1,r=+e[3],!e[4])return new Date(Date.UTC(n,i,r));if(o=+e[4],a=+e[5],c=+e[6],e[7]){for(f=e[7].slice(0,3);f.length<3;)f+="0";f=+f}return e[9]&&(u=+e[10],l=+(e[11]||0),h=6e4*(60*u+l),"-"===e[9]&&(h=-h)),p=new Date(Date.UTC(n,i,r,o,a,c,f)),h&&p.setTime(p.getTime()-h),p}function o(t){return t.toISOString()}var a=t("../type"),s=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?)?$");e.exports=new a("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:r,instanceOf:Date,represent:o})},{"../type":13}],30:[function(t,e,n){},{}],31:[function(t,e,n){e.exports=t("./lib/inherit")},{"./lib/inherit":32}],32:[function(e,n,i){!function(e){function r(t){var e=f(t);if(v)for(var n,i=0;n=b[i++];)t.hasOwnProperty(n)&&e.push(n);return e}function o(t,e,n){for(var i,o,a=r(n),s=0,u=a.length;u>s;)"__self"!==(i=a[s++])&&(o=n[i],g(o)&&(!c||o.toString().indexOf(".__base")>-1)?e[i]=function(n,i){var r=t[n]?t[n]:"__constructor"===n?e.__self.__parent:y;return function(){var t=this.__base;this.__base=r;var e=i.apply(this,arguments);return this.__base=t,e}}(i,o):e[i]=o)}function a(t,e){for(var n,i=1;n=t[i++];)e?g(n)?s.self(e,n.prototype,n):s.self(e,n):e=g(n)?s(t[0],n.prototype,n):s(t[0],n);return e||t[0]}function s(){var t=arguments,e=m(t[0]),n=e||g(t[0]),i=n?e?a(t[0]):t[0]:u,r=t[n?1:0]||{},s=t[n?2:1],c=r.__constructor||n&&i.prototype.__constructor?function(){return this.__constructor.apply(this,arguments)}:n?function(){return i.apply(this,arguments)}:function(){};if(!n)return c.prototype=r,c.prototype.__self=c.prototype.constructor=c,h(c,s);h(c,i),c.__parent=i;var l=i.prototype,f=c.prototype=p(l);return f.__self=f.constructor=c,r&&o(l,f,r),s&&o(i,c,s),c}var c=function(){"_"}.toString().indexOf("_")>-1,u=function(){},l=Object.prototype.hasOwnProperty,p=Object.create||function(t){var e=function(){};return e.prototype=t,new e},f=Object.keys||function(t){var e=[];for(var n in t)l.call(t,n)&&e.push(n);return e},h=function(t,e){for(var n in e)l.call(e,n)&&(t[n]=e[n]);return t},d=Object.prototype.toString,m=Array.isArray||function(t){return"[object Array]"===d.call(t)},g=function(t){return"[object Function]"===d.call(t)},y=function(){},v=!0,x={toString:""};for(var A in x)x.hasOwnProperty(A)&&(v=!1);var b=v?["toString","valueOf"]:null;s.self=function(){var t=arguments,e=m(t[0]),n=e?a(t[0],t[0][0]):t[0],i=t[1],r=t[2],s=n.prototype;return i&&o(s,s,i),r&&o(n,n,r),n};var w=!0;"object"==typeof i&&(n.exports=s,w=!1),"object"==typeof modules&&(modules.define("inherit",function(t){t(s)}),w=!1),"function"==typeof t&&(t(function(t,e,n){n.exports=s}),w=!1),w&&(e.inherit=s)}(this)},{}],"/":[function(t,e,n){"use strict";var i=t("./lib/js-yaml.js");e.exports=i},{"./lib/js-yaml.js":1}]},{},[])("/")});
diff --git a/profiles/killbill/src/main/webapp/lib/lodash.min.js b/profiles/killbill/src/main/webapp/lib/lodash.min.js
new file mode 100644
index 0000000..05870d1
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/lodash.min.js
@@ -0,0 +1,102 @@
+/**
+ * @license
+ * lodash 3.10.1 (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
+ * Build: `lodash compat -o ./lodash.js`
+ */
+;(function(){function n(n,t){if(n!==t){var r=null===n,e=n===w,u=n===n,o=null===t,i=t===w,f=t===t;if(n>t&&!o||!u||r&&!i&&f||e&&f)return 1;if(n<t&&!r||!f||o&&!e&&u||i&&u)return-1}return 0}function t(n,t,r){for(var e=n.length,u=r?e:-1;r?u--:++u<e;)if(t(n[u],u,n))return u;return-1}function r(n,t,r){if(t!==t)return p(n,r);r-=1;for(var e=n.length;++r<e;)if(n[r]===t)return r;return-1}function e(n){return typeof n=="function"||false}function u(n){return null==n?"":n+""}function o(n,t){for(var r=-1,e=n.length;++r<e&&-1<t.indexOf(n.charAt(r)););
+return r}function i(n,t){for(var r=n.length;r--&&-1<t.indexOf(n.charAt(r)););return r}function f(t,r){return n(t.a,r.a)||t.b-r.b}function a(n){return Nn[n]}function c(n){return Tn[n]}function l(n,t,r){return t?n=Bn[n]:r&&(n=Dn[n]),"\\"+n}function s(n){return"\\"+Dn[n]}function p(n,t,r){var e=n.length;for(t+=r?0:-1;r?t--:++t<e;){var u=n[t];if(u!==u)return t}return-1}function h(n){return!!n&&typeof n=="object"}function _(n){return 160>=n&&9<=n&&13>=n||32==n||160==n||5760==n||6158==n||8192<=n&&(8202>=n||8232==n||8233==n||8239==n||8287==n||12288==n||65279==n);
+}function v(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;)n[r]===t&&(n[r]=P,o[++u]=r);return o}function g(n){for(var t=-1,r=n.length;++t<r&&_(n.charCodeAt(t)););return t}function y(n){for(var t=n.length;t--&&_(n.charCodeAt(t)););return t}function d(n){return Pn[n]}function m(_){function Nn(n){if(h(n)&&!(Wo(n)||n instanceof zn)){if(n instanceof Pn)return n;if(eu.call(n,"__chain__")&&eu.call(n,"__wrapped__"))return qr(n)}return new Pn(n)}function Tn(){}function Pn(n,t,r){this.__wrapped__=n,this.__actions__=r||[],
+this.__chain__=!!t}function zn(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=false,this.__iteratees__=[],this.__takeCount__=Cu,this.__views__=[]}function Bn(){this.__data__={}}function Dn(n){var t=n?n.length:0;for(this.data={hash:mu(null),set:new hu};t--;)this.push(n[t])}function Mn(n,t){var r=n.data;return(typeof t=="string"||de(t)?r.set.has(t):r.hash[t])?0:-1}function qn(n,t){var r=-1,e=n.length;for(t||(t=De(e));++r<e;)t[r]=n[r];return t}function Kn(n,t){for(var r=-1,e=n.length;++r<e&&false!==t(n[r],r,n););
+return n}function Vn(n,t){for(var r=-1,e=n.length;++r<e;)if(!t(n[r],r,n))return false;return true}function Zn(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;){var i=n[r];t(i,r,n)&&(o[++u]=i)}return o}function Xn(n,t){for(var r=-1,e=n.length,u=De(e);++r<e;)u[r]=t(n[r],r,n);return u}function Hn(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];return n}function Qn(n,t,r,e){var u=-1,o=n.length;for(e&&o&&(r=n[++u]);++u<o;)r=t(r,n[u],u,n);return r}function nt(n,t){for(var r=-1,e=n.length;++r<e;)if(t(n[r],r,n))return true;
+return false}function tt(n,t,r,e){return n!==w&&eu.call(e,r)?n:t}function rt(n,t,r){for(var e=-1,u=Ko(t),o=u.length;++e<o;){var i=u[e],f=n[i],a=r(f,t[i],i,n,t);(a===a?a===f:f!==f)&&(f!==w||i in n)||(n[i]=a)}return n}function et(n,t){return null==t?n:ot(t,Ko(t),n)}function ut(n,t){for(var r=-1,e=null==n,u=!e&&Sr(n),o=u?n.length:0,i=t.length,f=De(i);++r<i;){var a=t[r];f[r]=u?Ur(a,o)?n[a]:w:e?w:n[a]}return f}function ot(n,t,r){r||(r={});for(var e=-1,u=t.length;++e<u;){var o=t[e];r[o]=n[o]}return r}function it(n,t,r){
+var e=typeof n;return"function"==e?t===w?n:Dt(n,t,r):null==n?Ne:"object"==e?At(n):t===w?Be(n):jt(n,t)}function ft(n,t,r,e,u,o,i){var f;if(r&&(f=u?r(n,e,u):r(n)),f!==w)return f;if(!de(n))return n;if(e=Wo(n)){if(f=Ir(n),!t)return qn(n,f)}else{var a=ou.call(n),c=a==K;if(a!=Z&&a!=z&&(!c||u))return Ln[a]?Er(n,a,t):u?n:{};if(Gn(n))return u?n:{};if(f=Rr(c?{}:n),!t)return et(f,n)}for(o||(o=[]),i||(i=[]),u=o.length;u--;)if(o[u]==n)return i[u];return o.push(n),i.push(f),(e?Kn:gt)(n,function(e,u){f[u]=ft(e,t,r,u,n,o,i);
+}),f}function at(n,t,r){if(typeof n!="function")throw new Xe(T);return _u(function(){n.apply(w,r)},t)}function ct(n,t){var e=n?n.length:0,u=[];if(!e)return u;var o=-1,i=jr(),f=i===r,a=f&&t.length>=F&&mu&&hu?new Dn(t):null,c=t.length;a&&(i=Mn,f=false,t=a);n:for(;++o<e;)if(a=n[o],f&&a===a){for(var l=c;l--;)if(t[l]===a)continue n;u.push(a)}else 0>i(t,a,0)&&u.push(a);return u}function lt(n,t){var r=true;return zu(n,function(n,e,u){return r=!!t(n,e,u)}),r}function st(n,t,r,e){var u=e,o=u;return zu(n,function(n,i,f){
+i=+t(n,i,f),(r(i,u)||i===e&&i===o)&&(u=i,o=n)}),o}function pt(n,t){var r=[];return zu(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function ht(n,t,r,e){var u;return r(n,function(n,r,o){return t(n,r,o)?(u=e?r:n,false):void 0}),u}function _t(n,t,r,e){e||(e=[]);for(var u=-1,o=n.length;++u<o;){var i=n[u];h(i)&&Sr(i)&&(r||Wo(i)||_e(i))?t?_t(i,t,r,e):Hn(e,i):r||(e[e.length]=i)}return e}function vt(n,t){return Du(n,t,Ee)}function gt(n,t){return Du(n,t,Ko)}function yt(n,t){return Mu(n,t,Ko)}function dt(n,t){for(var r=-1,e=t.length,u=-1,o=[];++r<e;){
+var i=t[r];ye(n[i])&&(o[++u]=i)}return o}function mt(n,t,r){if(null!=n){n=Dr(n),r!==w&&r in n&&(t=[r]),r=0;for(var e=t.length;null!=n&&r<e;)n=Dr(n)[t[r++]];return r&&r==e?n:w}}function wt(n,t,r,e,u,o){if(n===t)return true;if(null==n||null==t||!de(n)&&!h(t))return n!==n&&t!==t;n:{var i=wt,f=Wo(n),a=Wo(t),c=B,l=B;f||(c=ou.call(n),c==z?c=Z:c!=Z&&(f=je(n))),a||(l=ou.call(t),l==z?l=Z:l!=Z&&je(t));var s=c==Z&&!Gn(n),a=l==Z&&!Gn(t),l=c==l;if(!l||f||s){if(!e&&(c=s&&eu.call(n,"__wrapped__"),a=a&&eu.call(t,"__wrapped__"),
+c||a)){n=i(c?n.value():n,a?t.value():t,r,e,u,o);break n}if(l){for(u||(u=[]),o||(o=[]),c=u.length;c--;)if(u[c]==n){n=o[c]==t;break n}u.push(n),o.push(t),n=(f?mr:xr)(n,t,i,r,e,u,o),u.pop(),o.pop()}else n=false}else n=wr(n,t,c)}return n}function xt(n,t,r){var e=t.length,u=e,o=!r;if(null==n)return!u;for(n=Dr(n);e--;){var i=t[e];if(o&&i[2]?i[1]!==n[i[0]]:!(i[0]in n))return false}for(;++e<u;){var i=t[e],f=i[0],a=n[f],c=i[1];if(o&&i[2]){if(a===w&&!(f in n))return false}else if(i=r?r(a,c,f):w,i===w?!wt(c,a,r,true):!i)return false;
+}return true}function bt(n,t){var r=-1,e=Sr(n)?De(n.length):[];return zu(n,function(n,u,o){e[++r]=t(n,u,o)}),e}function At(n){var t=kr(n);if(1==t.length&&t[0][2]){var r=t[0][0],e=t[0][1];return function(n){return null==n?false:(n=Dr(n),n[r]===e&&(e!==w||r in n))}}return function(n){return xt(n,t)}}function jt(n,t){var r=Wo(n),e=Wr(n)&&t===t&&!de(t),u=n+"";return n=Mr(n),function(o){if(null==o)return false;var i=u;if(o=Dr(o),!(!r&&e||i in o)){if(o=1==n.length?o:mt(o,St(n,0,-1)),null==o)return false;i=Gr(n),o=Dr(o);
+}return o[i]===t?t!==w||i in o:wt(t,o[i],w,true)}}function kt(n,t,r,e,u){if(!de(n))return n;var o=Sr(t)&&(Wo(t)||je(t)),i=o?w:Ko(t);return Kn(i||t,function(f,a){if(i&&(a=f,f=t[a]),h(f)){e||(e=[]),u||(u=[]);n:{for(var c=a,l=e,s=u,p=l.length,_=t[c];p--;)if(l[p]==_){n[c]=s[p];break n}var p=n[c],v=r?r(p,_,c,n,t):w,g=v===w;g&&(v=_,Sr(_)&&(Wo(_)||je(_))?v=Wo(p)?p:Sr(p)?qn(p):[]:xe(_)||_e(_)?v=_e(p)?Ie(p):xe(p)?p:{}:g=false),l.push(_),s.push(v),g?n[c]=kt(v,_,r,l,s):(v===v?v!==p:p===p)&&(n[c]=v)}}else c=n[a],
+l=r?r(c,f,a,n,t):w,(s=l===w)&&(l=f),l===w&&(!o||a in n)||!s&&(l===l?l===c:c!==c)||(n[a]=l)}),n}function Ot(n){return function(t){return null==t?w:Dr(t)[n]}}function It(n){var t=n+"";return n=Mr(n),function(r){return mt(r,n,t)}}function Rt(n,t){for(var r=n?t.length:0;r--;){var e=t[r];if(e!=u&&Ur(e)){var u=e;vu.call(n,e,1)}}return n}function Et(n,t){return n+wu(Ru()*(t-n+1))}function Ct(n,t,r,e,u){return u(n,function(n,u,o){r=e?(e=false,n):t(r,n,u,o)}),r}function St(n,t,r){var e=-1,u=n.length;for(t=null==t?0:+t||0,
+0>t&&(t=-t>u?0:u+t),r=r===w||r>u?u:+r||0,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=De(u);++e<u;)r[e]=n[e+t];return r}function Ut(n,t){var r;return zu(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function $t(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].c;return n}function Wt(t,r,e){var u=br(),o=-1;return r=Xn(r,function(n){return u(n)}),t=bt(t,function(n){return{a:Xn(r,function(t){return t(n)}),b:++o,c:n}}),$t(t,function(t,r){var u;n:{for(var o=-1,i=t.a,f=r.a,a=i.length,c=e.length;++o<a;)if(u=n(i[o],f[o])){
+if(o>=c)break n;o=e[o],u*="asc"===o||true===o?1:-1;break n}u=t.b-r.b}return u})}function Ft(n,t){var r=0;return zu(n,function(n,e,u){r+=+t(n,e,u)||0}),r}function Lt(n,t){var e=-1,u=jr(),o=n.length,i=u===r,f=i&&o>=F,a=f&&mu&&hu?new Dn(void 0):null,c=[];a?(u=Mn,i=false):(f=false,a=t?[]:c);n:for(;++e<o;){var l=n[e],s=t?t(l,e,n):l;if(i&&l===l){for(var p=a.length;p--;)if(a[p]===s)continue n;t&&a.push(s),c.push(l)}else 0>u(a,s,0)&&((t||f)&&a.push(s),c.push(l))}return c}function Nt(n,t){for(var r=-1,e=t.length,u=De(e);++r<e;)u[r]=n[t[r]];
+return u}function Tt(n,t,r,e){for(var u=n.length,o=e?u:-1;(e?o--:++o<u)&&t(n[o],o,n););return r?St(n,e?0:o,e?o+1:u):St(n,e?o+1:0,e?u:o)}function Pt(n,t){var r=n;r instanceof zn&&(r=r.value());for(var e=-1,u=t.length;++e<u;)var o=t[e],r=o.func.apply(o.thisArg,Hn([r],o.args));return r}function zt(n,t,r){var e=0,u=n?n.length:e;if(typeof t=="number"&&t===t&&u<=Uu){for(;e<u;){var o=e+u>>>1,i=n[o];(r?i<=t:i<t)&&null!==i?e=o+1:u=o}return u}return Bt(n,t,Ne,r)}function Bt(n,t,r,e){t=r(t);for(var u=0,o=n?n.length:0,i=t!==t,f=null===t,a=t===w;u<o;){
+var c=wu((u+o)/2),l=r(n[c]),s=l!==w,p=l===l;(i?p||e:f?p&&s&&(e||null!=l):a?p&&(e||s):null==l?0:e?l<=t:l<t)?u=c+1:o=c}return ku(o,Su)}function Dt(n,t,r){if(typeof n!="function")return Ne;if(t===w)return n;switch(r){case 1:return function(r){return n.call(t,r)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,o){return n.call(t,r,e,u,o)};case 5:return function(r,e,u,o,i){return n.call(t,r,e,u,o,i)}}return function(){return n.apply(t,arguments)}}function Mt(n){var t=new au(n.byteLength);
+return new gu(t).set(new gu(n)),t}function qt(n,t,r){for(var e=r.length,u=-1,o=ju(n.length-e,0),i=-1,f=t.length,a=De(f+o);++i<f;)a[i]=t[i];for(;++u<e;)a[r[u]]=n[u];for(;o--;)a[i++]=n[u++];return a}function Kt(n,t,r){for(var e=-1,u=r.length,o=-1,i=ju(n.length-u,0),f=-1,a=t.length,c=De(i+a);++o<i;)c[o]=n[o];for(i=o;++f<a;)c[i+f]=t[f];for(;++e<u;)c[i+r[e]]=n[o++];return c}function Vt(n,t){return function(r,e,u){var o=t?t():{};if(e=br(e,u,3),Wo(r)){u=-1;for(var i=r.length;++u<i;){var f=r[u];n(o,f,e(f,u,r),r);
+}}else zu(r,function(t,r,u){n(o,t,e(t,r,u),u)});return o}}function Zt(n){return pe(function(t,r){var e=-1,u=null==t?0:r.length,o=2<u?r[u-2]:w,i=2<u?r[2]:w,f=1<u?r[u-1]:w;for(typeof o=="function"?(o=Dt(o,f,5),u-=2):(o=typeof f=="function"?f:w,u-=o?1:0),i&&$r(r[0],r[1],i)&&(o=3>u?w:o,u=1);++e<u;)(i=r[e])&&n(t,i,o);return t})}function Yt(n,t){return function(r,e){var u=r?Vu(r):0;if(!Lr(u))return n(r,e);for(var o=t?u:-1,i=Dr(r);(t?o--:++o<u)&&false!==e(i[o],o,i););return r}}function Gt(n){return function(t,r,e){
+var u=Dr(t);e=e(t);for(var o=e.length,i=n?o:-1;n?i--:++i<o;){var f=e[i];if(false===r(u[f],f,u))break}return t}}function Jt(n,t){function r(){return(this&&this!==Yn&&this instanceof r?e:n).apply(t,arguments)}var e=Ht(n);return r}function Xt(n){return function(t){var r=-1;t=Fe(Ue(t));for(var e=t.length,u="";++r<e;)u=n(u,t[r],r);return u}}function Ht(n){return function(){var t=arguments;switch(t.length){case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:return new n(t[0],t[1],t[2]);
+case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=Pu(n.prototype),t=n.apply(r,t);return de(t)?t:r}}function Qt(n){function t(r,e,u){return u&&$r(r,e,u)&&(e=w),r=dr(r,n,w,w,w,w,w,e),r.placeholder=t.placeholder,r}return t}function nr(n,t){return pe(function(r){var e=r[0];return null==e?e:(r.push(t),n.apply(w,r))})}function tr(n,t){return function(r,e,u){
+if(u&&$r(r,e,u)&&(e=w),e=br(e,u,3),1==e.length){u=r=Wo(r)?r:Br(r);for(var o=e,i=-1,f=u.length,a=t,c=a;++i<f;){var l=u[i],s=+o(l);n(s,a)&&(a=s,c=l)}if(u=c,!r.length||u!==t)return u}return st(r,e,n,t)}}function rr(n,r){return function(e,u,o){return u=br(u,o,3),Wo(e)?(u=t(e,u,r),-1<u?e[u]:w):ht(e,u,n)}}function er(n){return function(r,e,u){return r&&r.length?(e=br(e,u,3),t(r,e,n)):-1}}function ur(n){return function(t,r,e){return r=br(r,e,3),ht(t,r,n,true)}}function or(n){return function(){for(var t,r=arguments.length,e=n?r:-1,u=0,o=De(r);n?e--:++e<r;){
+var i=o[u++]=arguments[e];if(typeof i!="function")throw new Xe(T);!t&&Pn.prototype.thru&&"wrapper"==Ar(i)&&(t=new Pn([],true))}for(e=t?-1:r;++e<r;){var i=o[e],u=Ar(i),f="wrapper"==u?Ku(i):w;t=f&&Fr(f[0])&&f[1]==(E|k|I|C)&&!f[4].length&&1==f[9]?t[Ar(f[0])].apply(t,f[3]):1==i.length&&Fr(i)?t[u]():t.thru(i)}return function(){var n=arguments,e=n[0];if(t&&1==n.length&&Wo(e)&&e.length>=F)return t.plant(e).value();for(var u=0,n=r?o[u].apply(this,n):e;++u<r;)n=o[u].call(this,n);return n}}}function ir(n,t){
+return function(r,e,u){return typeof e=="function"&&u===w&&Wo(r)?n(r,e):t(r,Dt(e,u,3))}}function fr(n){return function(t,r,e){return(typeof r!="function"||e!==w)&&(r=Dt(r,e,3)),n(t,r,Ee)}}function ar(n){return function(t,r,e){return(typeof r!="function"||e!==w)&&(r=Dt(r,e,3)),n(t,r)}}function cr(n){return function(t,r,e){var u={};return r=br(r,e,3),gt(t,function(t,e,o){o=r(t,e,o),e=n?o:e,t=n?t:o,u[e]=t}),u}}function lr(n){return function(t,r,e){return t=u(t),(n?t:"")+_r(t,r,e)+(n?"":t)}}function sr(n){
+var t=pe(function(r,e){var u=v(e,t.placeholder);return dr(r,n,w,e,u)});return t}function pr(n,t){return function(r,e,u,o){var i=3>arguments.length;return typeof e=="function"&&o===w&&Wo(r)?n(r,e,u,i):Ct(r,br(e,o,4),u,i,t)}}function hr(n,t,r,e,u,o,i,f,a,c){function l(){for(var m=arguments.length,x=m,j=De(m);x--;)j[x]=arguments[x];if(e&&(j=qt(j,e,u)),o&&(j=Kt(j,o,i)),_||y){var x=l.placeholder,k=v(j,x),m=m-k.length;if(m<c){var O=f?qn(f):w,m=ju(c-m,0),E=_?k:w,k=_?w:k,C=_?j:w,j=_?w:j;return t|=_?I:R,t&=~(_?R:I),
+g||(t&=~(b|A)),j=[n,t,r,C,E,j,k,O,a,m],O=hr.apply(w,j),Fr(n)&&Zu(O,j),O.placeholder=x,O}}if(x=p?r:this,O=h?x[n]:n,f)for(m=j.length,E=ku(f.length,m),k=qn(j);E--;)C=f[E],j[E]=Ur(C,m)?k[C]:w;return s&&a<j.length&&(j.length=a),this&&this!==Yn&&this instanceof l&&(O=d||Ht(n)),O.apply(x,j)}var s=t&E,p=t&b,h=t&A,_=t&k,g=t&j,y=t&O,d=h?w:Ht(n);return l}function _r(n,t,r){return n=n.length,t=+t,n<t&&bu(t)?(t-=n,r=null==r?" ":r+"",$e(r,du(t/r.length)).slice(0,t)):""}function vr(n,t,r,e){function u(){for(var t=-1,f=arguments.length,a=-1,c=e.length,l=De(c+f);++a<c;)l[a]=e[a];
+for(;f--;)l[a++]=arguments[++t];return(this&&this!==Yn&&this instanceof u?i:n).apply(o?r:this,l)}var o=t&b,i=Ht(n);return u}function gr(n){var t=Ve[n];return function(n,r){return(r=r===w?0:+r||0)?(r=su(10,r),t(n*r)/r):t(n)}}function yr(n){return function(t,r,e,u){var o=br(e);return null==e&&o===it?zt(t,r,n):Bt(t,r,o(e,u,1),n)}}function dr(n,t,r,e,u,o,i,f){var a=t&A;if(!a&&typeof n!="function")throw new Xe(T);var c=e?e.length:0;if(c||(t&=~(I|R),e=u=w),c-=u?u.length:0,t&R){var l=e,s=u;e=u=w}var p=a?w:Ku(n);
+return r=[n,t,r,e,u,l,s,o,i,f],p&&(e=r[1],t=p[1],f=e|t,u=t==E&&e==k||t==E&&e==C&&r[7].length<=p[8]||t==(E|C)&&e==k,(f<E||u)&&(t&b&&(r[2]=p[2],f|=e&b?0:j),(e=p[3])&&(u=r[3],r[3]=u?qt(u,e,p[4]):qn(e),r[4]=u?v(r[3],P):qn(p[4])),(e=p[5])&&(u=r[5],r[5]=u?Kt(u,e,p[6]):qn(e),r[6]=u?v(r[5],P):qn(p[6])),(e=p[7])&&(r[7]=qn(e)),t&E&&(r[8]=null==r[8]?p[8]:ku(r[8],p[8])),null==r[9]&&(r[9]=p[9]),r[0]=p[0],r[1]=f),t=r[1],f=r[9]),r[9]=null==f?a?0:n.length:ju(f-c,0)||0,n=t==b?Jt(r[0],r[2]):t!=I&&t!=(b|I)||r[4].length?hr.apply(w,r):vr.apply(w,r),
+(p?qu:Zu)(n,r)}function mr(n,t,r,e,u,o,i){var f=-1,a=n.length,c=t.length;if(a!=c&&(!u||c<=a))return false;for(;++f<a;){var l=n[f],c=t[f],s=e?e(u?c:l,u?l:c,f):w;if(s!==w){if(s)continue;return false}if(u){if(!nt(t,function(n){return l===n||r(l,n,e,u,o,i)}))return false}else if(l!==c&&!r(l,c,e,u,o,i))return false}return true}function wr(n,t,r){switch(r){case D:case M:return+n==+t;case q:return n.name==t.name&&n.message==t.message;case V:return n!=+n?t!=+t:n==+t;case Y:case G:return n==t+""}return false}function xr(n,t,r,e,u,o,i){
+var f=Ko(n),a=f.length,c=Ko(t).length;if(a!=c&&!u)return false;for(c=a;c--;){var l=f[c];if(!(u?l in t:eu.call(t,l)))return false}for(var s=u;++c<a;){var l=f[c],p=n[l],h=t[l],_=e?e(u?h:p,u?p:h,l):w;if(_===w?!r(p,h,e,u,o,i):!_)return false;s||(s="constructor"==l)}return s||(r=n.constructor,e=t.constructor,!(r!=e&&"constructor"in n&&"constructor"in t)||typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)?true:false}function br(n,t,r){var e=Nn.callback||Le,e=e===Le?it:e;return r?e(n,t,r):e}function Ar(n){
+for(var t=n.name+"",r=Fu[t],e=r?r.length:0;e--;){var u=r[e],o=u.func;if(null==o||o==n)return u.name}return t}function jr(n,t,e){var u=Nn.indexOf||Yr,u=u===Yr?r:u;return n?u(n,t,e):u}function kr(n){n=Ce(n);for(var t=n.length;t--;){var r,e=n[t];r=n[t][1],r=r===r&&!de(r),e[2]=r}return n}function Or(n,t){var r=null==n?w:n[t];return me(r)?r:w}function Ir(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&eu.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function Rr(n){return n=n.constructor,
+typeof n=="function"&&n instanceof n||(n=Ye),new n}function Er(n,t,r){var e=n.constructor;switch(t){case J:return Mt(n);case D:case M:return new e(+n);case X:case H:case Q:case nn:case tn:case rn:case en:case un:case on:return e instanceof e&&(e=Lu[t]),t=n.buffer,new e(r?Mt(t):t,n.byteOffset,n.length);case V:case G:return new e(n);case Y:var u=new e(n.source,kn.exec(n));u.lastIndex=n.lastIndex}return u}function Cr(n,t,r){return null==n||Wr(t,n)||(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),t=Gr(t)),
+t=null==n?n:n[t],null==t?w:t.apply(n,r)}function Sr(n){return null!=n&&Lr(Vu(n))}function Ur(n,t){return n=typeof n=="number"||Rn.test(n)?+n:-1,t=null==t?$u:t,-1<n&&0==n%1&&n<t}function $r(n,t,r){if(!de(r))return false;var e=typeof t;return("number"==e?Sr(r)&&Ur(t,r.length):"string"==e&&t in r)?(t=r[t],n===n?n===t:t!==t):false}function Wr(n,t){var r=typeof n;return"string"==r&&dn.test(n)||"number"==r?true:Wo(n)?false:!yn.test(n)||null!=t&&n in Dr(t)}function Fr(n){var t=Ar(n),r=Nn[t];return typeof r=="function"&&t in zn.prototype?n===r?true:(t=Ku(r),
+!!t&&n===t[0]):false}function Lr(n){return typeof n=="number"&&-1<n&&0==n%1&&n<=$u}function Nr(n,t){return n===w?t:Fo(n,t,Nr)}function Tr(n,t){n=Dr(n);for(var r=-1,e=t.length,u={};++r<e;){var o=t[r];o in n&&(u[o]=n[o])}return u}function Pr(n,t){var r={};return vt(n,function(n,e,u){t(n,e,u)&&(r[e]=n)}),r}function zr(n){for(var t=Ee(n),r=t.length,e=r&&n.length,u=!!e&&Lr(e)&&(Wo(n)||_e(n)||Ae(n)),o=-1,i=[];++o<r;){var f=t[o];(u&&Ur(f,e)||eu.call(n,f))&&i.push(f)}return i}function Br(n){return null==n?[]:Sr(n)?Nn.support.unindexedChars&&Ae(n)?n.split(""):de(n)?n:Ye(n):Se(n);
+}function Dr(n){if(Nn.support.unindexedChars&&Ae(n)){for(var t=-1,r=n.length,e=Ye(n);++t<r;)e[t]=n.charAt(t);return e}return de(n)?n:Ye(n)}function Mr(n){if(Wo(n))return n;var t=[];return u(n).replace(mn,function(n,r,e,u){t.push(e?u.replace(An,"$1"):r||n)}),t}function qr(n){return n instanceof zn?n.clone():new Pn(n.__wrapped__,n.__chain__,qn(n.__actions__))}function Kr(n,t,r){return n&&n.length?((r?$r(n,t,r):null==t)&&(t=1),St(n,0>t?0:t)):[]}function Vr(n,t,r){var e=n?n.length:0;return e?((r?$r(n,t,r):null==t)&&(t=1),
+t=e-(+t||0),St(n,0,0>t?0:t)):[]}function Zr(n){return n?n[0]:w}function Yr(n,t,e){var u=n?n.length:0;if(!u)return-1;if(typeof e=="number")e=0>e?ju(u+e,0):e;else if(e)return e=zt(n,t),e<u&&(t===t?t===n[e]:n[e]!==n[e])?e:-1;return r(n,t,e||0)}function Gr(n){var t=n?n.length:0;return t?n[t-1]:w}function Jr(n){return Kr(n,1)}function Xr(n,t,e,u){if(!n||!n.length)return[];null!=t&&typeof t!="boolean"&&(u=e,e=$r(n,t,u)?w:t,t=false);var o=br();if((null!=e||o!==it)&&(e=o(e,u,3)),t&&jr()===r){t=e;var i;e=-1,
+u=n.length;for(var o=-1,f=[];++e<u;){var a=n[e],c=t?t(a,e,n):a;e&&i===c||(i=c,f[++o]=a)}n=f}else n=Lt(n,e);return n}function Hr(n){if(!n||!n.length)return[];var t=-1,r=0;n=Zn(n,function(n){return Sr(n)?(r=ju(n.length,r),true):void 0});for(var e=De(r);++t<r;)e[t]=Xn(n,Ot(t));return e}function Qr(n,t,r){return n&&n.length?(n=Hr(n),null==t?n:(t=Dt(t,r,4),Xn(n,function(n){return Qn(n,t,w,true)}))):[]}function ne(n,t){var r=-1,e=n?n.length:0,u={};for(!e||t||Wo(n[0])||(t=[]);++r<e;){var o=n[r];t?u[o]=t[r]:o&&(u[o[0]]=o[1]);
+}return u}function te(n){return n=Nn(n),n.__chain__=true,n}function re(n,t,r){return t.call(r,n)}function ee(n,t,r){var e=Wo(n)?Vn:lt;return r&&$r(n,t,r)&&(t=w),(typeof t!="function"||r!==w)&&(t=br(t,r,3)),e(n,t)}function ue(n,t,r){var e=Wo(n)?Zn:pt;return t=br(t,r,3),e(n,t)}function oe(n,t,r,e){var u=n?Vu(n):0;return Lr(u)||(n=Se(n),u=n.length),r=typeof r!="number"||e&&$r(t,r,e)?0:0>r?ju(u+r,0):r||0,typeof n=="string"||!Wo(n)&&Ae(n)?r<=u&&-1<n.indexOf(t,r):!!u&&-1<jr(n,t,r)}function ie(n,t,r){var e=Wo(n)?Xn:bt;
+return t=br(t,r,3),e(n,t)}function fe(n,t,r){if(r?$r(n,t,r):null==t){n=Br(n);var e=n.length;return 0<e?n[Et(0,e-1)]:w}r=-1,n=Oe(n);var e=n.length,u=e-1;for(t=ku(0>t?0:+t||0,e);++r<t;){var e=Et(r,u),o=n[e];n[e]=n[r],n[r]=o}return n.length=t,n}function ae(n,t,r){var e=Wo(n)?nt:Ut;return r&&$r(n,t,r)&&(t=w),(typeof t!="function"||r!==w)&&(t=br(t,r,3)),e(n,t)}function ce(n,t){var r;if(typeof t!="function"){if(typeof n!="function")throw new Xe(T);var e=n;n=t,t=e}return function(){return 0<--n&&(r=t.apply(this,arguments)),
+1>=n&&(t=w),r}}function le(n,t,r){function e(t,r){r&&cu(r),a=p=h=w,t&&(_=wo(),c=n.apply(s,f),p||a||(f=s=w))}function u(){var n=t-(wo()-l);0>=n||n>t?e(h,a):p=_u(u,n)}function o(){e(g,p)}function i(){if(f=arguments,l=wo(),s=this,h=g&&(p||!y),false===v)var r=y&&!p;else{a||y||(_=l);var e=v-(l-_),i=0>=e||e>v;i?(a&&(a=cu(a)),_=l,c=n.apply(s,f)):a||(a=_u(o,e))}return i&&p?p=cu(p):p||t===v||(p=_u(u,t)),r&&(i=true,c=n.apply(s,f)),!i||p||a||(f=s=w),c}var f,a,c,l,s,p,h,_=0,v=false,g=true;if(typeof n!="function")throw new Xe(T);
+if(t=0>t?0:+t||0,true===r)var y=true,g=false;else de(r)&&(y=!!r.leading,v="maxWait"in r&&ju(+r.maxWait||0,t),g="trailing"in r?!!r.trailing:g);return i.cancel=function(){p&&cu(p),a&&cu(a),_=0,a=p=h=w},i}function se(n,t){if(typeof n!="function"||t&&typeof t!="function")throw new Xe(T);var r=function(){var e=arguments,u=t?t.apply(this,e):e[0],o=r.cache;return o.has(u)?o.get(u):(e=n.apply(this,e),r.cache=o.set(u,e),e)};return r.cache=new se.Cache,r}function pe(n,t){if(typeof n!="function")throw new Xe(T);return t=ju(t===w?n.length-1:+t||0,0),
+function(){for(var r=arguments,e=-1,u=ju(r.length-t,0),o=De(u);++e<u;)o[e]=r[t+e];switch(t){case 0:return n.call(this,o);case 1:return n.call(this,r[0],o);case 2:return n.call(this,r[0],r[1],o)}for(u=De(t+1),e=-1;++e<t;)u[e]=r[e];return u[t]=o,n.apply(this,u)}}function he(n,t){return n>t}function _e(n){return h(n)&&Sr(n)&&eu.call(n,"callee")&&!pu.call(n,"callee")}function ve(n,t,r,e){return e=(r=typeof r=="function"?Dt(r,e,3):w)?r(n,t):w,e===w?wt(n,t,r):!!e}function ge(n){return h(n)&&typeof n.message=="string"&&ou.call(n)==q;
+}function ye(n){return de(n)&&ou.call(n)==K}function de(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}function me(n){return null==n?false:ye(n)?fu.test(ru.call(n)):h(n)&&(Gn(n)?fu:In).test(n)}function we(n){return typeof n=="number"||h(n)&&ou.call(n)==V}function xe(n){var t;if(!h(n)||ou.call(n)!=Z||Gn(n)||_e(n)||!(eu.call(n,"constructor")||(t=n.constructor,typeof t!="function"||t instanceof t)))return false;var r;return Nn.support.ownLast?(vt(n,function(n,t,e){return r=eu.call(e,t),false}),false!==r):(vt(n,function(n,t){
+r=t}),r===w||eu.call(n,r))}function be(n){return de(n)&&ou.call(n)==Y}function Ae(n){return typeof n=="string"||h(n)&&ou.call(n)==G}function je(n){return h(n)&&Lr(n.length)&&!!Fn[ou.call(n)]}function ke(n,t){return n<t}function Oe(n){var t=n?Vu(n):0;return Lr(t)?t?Nn.support.unindexedChars&&Ae(n)?n.split(""):qn(n):[]:Se(n)}function Ie(n){return ot(n,Ee(n))}function Re(n){return dt(n,Ee(n))}function Ee(n){if(null==n)return[];de(n)||(n=Ye(n));for(var t=n.length,r=Nn.support,t=t&&Lr(t)&&(Wo(n)||_e(n)||Ae(n))&&t||0,e=n.constructor,u=-1,e=ye(e)&&e.prototype||nu,o=e===n,i=De(t),f=0<t,a=r.enumErrorProps&&(n===Qe||n instanceof qe),c=r.enumPrototypes&&ye(n);++u<t;)i[u]=u+"";
+for(var l in n)c&&"prototype"==l||a&&("message"==l||"name"==l)||f&&Ur(l,t)||"constructor"==l&&(o||!eu.call(n,l))||i.push(l);if(r.nonEnumShadows&&n!==nu)for(t=n===tu?G:n===Qe?q:ou.call(n),r=Nu[t]||Nu[Z],t==Z&&(e=nu),t=Wn.length;t--;)l=Wn[t],u=r[l],o&&u||(u?!eu.call(n,l):n[l]===e[l])||i.push(l);return i}function Ce(n){n=Dr(n);for(var t=-1,r=Ko(n),e=r.length,u=De(e);++t<e;){var o=r[t];u[t]=[o,n[o]]}return u}function Se(n){return Nt(n,Ko(n))}function Ue(n){return(n=u(n))&&n.replace(En,a).replace(bn,"");
+}function $e(n,t){var r="";if(n=u(n),t=+t,1>t||!n||!bu(t))return r;do t%2&&(r+=n),t=wu(t/2),n+=n;while(t);return r}function We(n,t,r){var e=n;return(n=u(n))?(r?$r(e,t,r):null==t)?n.slice(g(n),y(n)+1):(t+="",n.slice(o(n,t),i(n,t)+1)):n}function Fe(n,t,r){return r&&$r(n,t,r)&&(t=w),n=u(n),n.match(t||Un)||[]}function Le(n,t,r){return r&&$r(n,t,r)&&(t=w),h(n)?Te(n):it(n,t)}function Ne(n){return n}function Te(n){return At(ft(n,true))}function Pe(n,t,r){if(null==r){var e=de(t),u=e?Ko(t):w;((u=u&&u.length?dt(t,u):w)?u.length:e)||(u=false,
+r=t,t=n,n=this)}u||(u=dt(t,Ko(t)));var o=true,e=-1,i=ye(n),f=u.length;false===r?o=false:de(r)&&"chain"in r&&(o=r.chain);for(;++e<f;){r=u[e];var a=t[r];n[r]=a,i&&(n.prototype[r]=function(t){return function(){var r=this.__chain__;if(o||r){var e=n(this.__wrapped__);return(e.__actions__=qn(this.__actions__)).push({func:t,args:arguments,thisArg:n}),e.__chain__=r,e}return t.apply(n,Hn([this.value()],arguments))}}(a))}return n}function ze(){}function Be(n){return Wr(n)?Ot(n):It(n)}_=_?Jn.defaults(Yn.Object(),_,Jn.pick(Yn,$n)):Yn;
+var De=_.Array,Me=_.Date,qe=_.Error,Ke=_.Function,Ve=_.Math,Ze=_.Number,Ye=_.Object,Ge=_.RegExp,Je=_.String,Xe=_.TypeError,He=De.prototype,Qe=qe.prototype,nu=Ye.prototype,tu=Je.prototype,ru=Ke.prototype.toString,eu=nu.hasOwnProperty,uu=0,ou=nu.toString,iu=Yn._,fu=Ge("^"+ru.call(eu).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),au=_.ArrayBuffer,cu=_.clearTimeout,lu=_.parseFloat,su=Ve.pow,pu=nu.propertyIsEnumerable,hu=Or(_,"Set"),_u=_.setTimeout,vu=He.splice,gu=_.Uint8Array,yu=Or(_,"WeakMap"),du=Ve.ceil,mu=Or(Ye,"create"),wu=Ve.floor,xu=Or(De,"isArray"),bu=_.isFinite,Au=Or(Ye,"keys"),ju=Ve.max,ku=Ve.min,Ou=Or(Me,"now"),Iu=_.parseInt,Ru=Ve.random,Eu=Ze.NEGATIVE_INFINITY,Cu=Ze.POSITIVE_INFINITY,Su=4294967294,Uu=2147483647,$u=9007199254740991,Wu=yu&&new yu,Fu={},Lu={};
+Lu[X]=_.Float32Array,Lu[H]=_.Float64Array,Lu[Q]=_.Int8Array,Lu[nn]=_.Int16Array,Lu[tn]=_.Int32Array,Lu[rn]=gu,Lu[en]=_.Uint8ClampedArray,Lu[un]=_.Uint16Array,Lu[on]=_.Uint32Array;var Nu={};Nu[B]=Nu[M]=Nu[V]={constructor:true,toLocaleString:true,toString:true,valueOf:true},Nu[D]=Nu[G]={constructor:true,toString:true,valueOf:true},Nu[q]=Nu[K]=Nu[Y]={constructor:true,toString:true},Nu[Z]={constructor:true},Kn(Wn,function(n){for(var t in Nu)if(eu.call(Nu,t)){var r=Nu[t];r[n]=eu.call(r,n)}});var Tu=Nn.support={};!function(n){
+var t=function(){this.x=n},r={0:n,length:n},e=[];t.prototype={valueOf:n,y:n};for(var u in new t)e.push(u);Tu.enumErrorProps=pu.call(Qe,"message")||pu.call(Qe,"name"),Tu.enumPrototypes=pu.call(t,"prototype"),Tu.nonEnumShadows=!/valueOf/.test(e),Tu.ownLast="x"!=e[0],Tu.spliceObjects=(vu.call(r,0,1),!r[0]),Tu.unindexedChars="xx"!="x"[0]+Ye("x")[0]}(1,0),Nn.templateSettings={escape:_n,evaluate:vn,interpolate:gn,variable:"",imports:{_:Nn}};var Pu=function(){function n(){}return function(t){if(de(t)){n.prototype=t;
+var r=new n;n.prototype=w}return r||{}}}(),zu=Yt(gt),Bu=Yt(yt,true),Du=Gt(),Mu=Gt(true),qu=Wu?function(n,t){return Wu.set(n,t),n}:Ne,Ku=Wu?function(n){return Wu.get(n)}:ze,Vu=Ot("length"),Zu=function(){var n=0,t=0;return function(r,e){var u=wo(),o=W-(u-t);if(t=u,0<o){if(++n>=$)return r}else n=0;return qu(r,e)}}(),Yu=pe(function(n,t){return h(n)&&Sr(n)?ct(n,_t(t,false,true)):[]}),Gu=er(),Ju=er(true),Xu=pe(function(n){for(var t=n.length,e=t,u=De(l),o=jr(),i=o===r,f=[];e--;){var a=n[e]=Sr(a=n[e])?a:[];u[e]=i&&120<=a.length&&mu&&hu?new Dn(e&&a):null;
+}var i=n[0],c=-1,l=i?i.length:0,s=u[0];n:for(;++c<l;)if(a=i[c],0>(s?Mn(s,a):o(f,a,0))){for(e=t;--e;){var p=u[e];if(0>(p?Mn(p,a):o(n[e],a,0)))continue n}s&&s.push(a),f.push(a)}return f}),Hu=pe(function(t,r){r=_t(r);var e=ut(t,r);return Rt(t,r.sort(n)),e}),Qu=yr(),no=yr(true),to=pe(function(n){return Lt(_t(n,false,true))}),ro=pe(function(n,t){return Sr(n)?ct(n,t):[]}),eo=pe(Hr),uo=pe(function(n){var t=n.length,r=2<t?n[t-2]:w,e=1<t?n[t-1]:w;return 2<t&&typeof r=="function"?t-=2:(r=1<t&&typeof e=="function"?(--t,
+e):w,e=w),n.length=t,Qr(n,r,e)}),oo=pe(function(n){return n=_t(n),this.thru(function(t){t=Wo(t)?t:[Dr(t)];for(var r=n,e=-1,u=t.length,o=-1,i=r.length,f=De(u+i);++e<u;)f[e]=t[e];for(;++o<i;)f[e++]=r[o];return f})}),io=pe(function(n,t){return Sr(n)&&(n=Br(n)),ut(n,_t(t))}),fo=Vt(function(n,t,r){eu.call(n,r)?++n[r]:n[r]=1}),ao=rr(zu),co=rr(Bu,true),lo=ir(Kn,zu),so=ir(function(n,t){for(var r=n.length;r--&&false!==t(n[r],r,n););return n},Bu),po=Vt(function(n,t,r){eu.call(n,r)?n[r].push(t):n[r]=[t]}),ho=Vt(function(n,t,r){
+n[r]=t}),_o=pe(function(n,t,r){var e=-1,u=typeof t=="function",o=Wr(t),i=Sr(n)?De(n.length):[];return zu(n,function(n){var f=u?t:o&&null!=n?n[t]:w;i[++e]=f?f.apply(n,r):Cr(n,t,r)}),i}),vo=Vt(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),go=pr(Qn,zu),yo=pr(function(n,t,r,e){var u=n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r},Bu),mo=pe(function(n,t){if(null==n)return[];var r=t[2];return r&&$r(t[0],t[1],r)&&(t.length=1),Wt(n,_t(t),[])}),wo=Ou||function(){return(new Me).getTime();
+},xo=pe(function(n,t,r){var e=b;if(r.length)var u=v(r,xo.placeholder),e=e|I;return dr(n,e,t,r,u)}),bo=pe(function(n,t){t=t.length?_t(t):Re(n);for(var r=-1,e=t.length;++r<e;){var u=t[r];n[u]=dr(n[u],b,n)}return n}),Ao=pe(function(n,t,r){var e=b|A;if(r.length)var u=v(r,Ao.placeholder),e=e|I;return dr(t,e,n,r,u)}),jo=Qt(k),ko=Qt(O),Oo=pe(function(n,t){return at(n,1,t)}),Io=pe(function(n,t,r){return at(n,t,r)}),Ro=or(),Eo=or(true),Co=pe(function(n,t){if(t=_t(t),typeof n!="function"||!Vn(t,e))throw new Xe(T);
+var r=t.length;return pe(function(e){for(var u=ku(e.length,r);u--;)e[u]=t[u](e[u]);return n.apply(this,e)})}),So=sr(I),Uo=sr(R),$o=pe(function(n,t){return dr(n,C,w,w,w,_t(t))}),Wo=xu||function(n){return h(n)&&Lr(n.length)&&ou.call(n)==B},Fo=Zt(kt),Lo=Zt(function(n,t,r){return r?rt(n,t,r):et(n,t)}),No=nr(Lo,function(n,t){return n===w?t:n}),To=nr(Fo,Nr),Po=ur(gt),zo=ur(yt),Bo=fr(Du),Do=fr(Mu),Mo=ar(gt),qo=ar(yt),Ko=Au?function(n){var t=null==n?w:n.constructor;return typeof t=="function"&&t.prototype===n||(typeof n=="function"?Nn.support.enumPrototypes:Sr(n))?zr(n):de(n)?Au(n):[];
+}:zr,Vo=cr(true),Zo=cr(),Yo=pe(function(n,t){if(null==n)return{};if("function"!=typeof t[0])return t=Xn(_t(t),Je),Tr(n,ct(Ee(n),t));var r=Dt(t[0],t[1],3);return Pr(n,function(n,t,e){return!r(n,t,e)})}),Go=pe(function(n,t){return null==n?{}:"function"==typeof t[0]?Pr(n,Dt(t[0],t[1],3)):Tr(n,_t(t))}),Jo=Xt(function(n,t,r){return t=t.toLowerCase(),n+(r?t.charAt(0).toUpperCase()+t.slice(1):t)}),Xo=Xt(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Ho=lr(),Qo=lr(true),ni=Xt(function(n,t,r){return n+(r?"_":"")+t.toLowerCase();
+}),ti=Xt(function(n,t,r){return n+(r?" ":"")+(t.charAt(0).toUpperCase()+t.slice(1))}),ri=pe(function(n,t){try{return n.apply(w,t)}catch(r){return ge(r)?r:new qe(r)}}),ei=pe(function(n,t){return function(r){return Cr(r,n,t)}}),ui=pe(function(n,t){return function(r){return Cr(n,r,t)}}),oi=gr("ceil"),ii=gr("floor"),fi=tr(he,Eu),ai=tr(ke,Cu),ci=gr("round");return Nn.prototype=Tn.prototype,Pn.prototype=Pu(Tn.prototype),Pn.prototype.constructor=Pn,zn.prototype=Pu(Tn.prototype),zn.prototype.constructor=zn,
+Bn.prototype["delete"]=function(n){return this.has(n)&&delete this.__data__[n]},Bn.prototype.get=function(n){return"__proto__"==n?w:this.__data__[n]},Bn.prototype.has=function(n){return"__proto__"!=n&&eu.call(this.__data__,n)},Bn.prototype.set=function(n,t){return"__proto__"!=n&&(this.__data__[n]=t),this},Dn.prototype.push=function(n){var t=this.data;typeof n=="string"||de(n)?t.set.add(n):t.hash[n]=true},se.Cache=Bn,Nn.after=function(n,t){if(typeof t!="function"){if(typeof n!="function")throw new Xe(T);
+var r=n;n=t,t=r}return n=bu(n=+n)?n:0,function(){return 1>--n?t.apply(this,arguments):void 0}},Nn.ary=function(n,t,r){return r&&$r(n,t,r)&&(t=w),t=n&&null==t?n.length:ju(+t||0,0),dr(n,E,w,w,w,w,t)},Nn.assign=Lo,Nn.at=io,Nn.before=ce,Nn.bind=xo,Nn.bindAll=bo,Nn.bindKey=Ao,Nn.callback=Le,Nn.chain=te,Nn.chunk=function(n,t,r){t=(r?$r(n,t,r):null==t)?1:ju(wu(t)||1,1),r=0;for(var e=n?n.length:0,u=-1,o=De(du(e/t));r<e;)o[++u]=St(n,r,r+=t);return o},Nn.compact=function(n){for(var t=-1,r=n?n.length:0,e=-1,u=[];++t<r;){
+var o=n[t];o&&(u[++e]=o)}return u},Nn.constant=function(n){return function(){return n}},Nn.countBy=fo,Nn.create=function(n,t,r){var e=Pu(n);return r&&$r(n,t,r)&&(t=w),t?et(e,t):e},Nn.curry=jo,Nn.curryRight=ko,Nn.debounce=le,Nn.defaults=No,Nn.defaultsDeep=To,Nn.defer=Oo,Nn.delay=Io,Nn.difference=Yu,Nn.drop=Kr,Nn.dropRight=Vr,Nn.dropRightWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),true,true):[]},Nn.dropWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),true):[]},Nn.fill=function(n,t,r,e){
+var u=n?n.length:0;if(!u)return[];for(r&&typeof r!="number"&&$r(n,t,r)&&(r=0,e=u),u=n.length,r=null==r?0:+r||0,0>r&&(r=-r>u?0:u+r),e=e===w||e>u?u:+e||0,0>e&&(e+=u),u=r>e?0:e>>>0,r>>>=0;r<u;)n[r++]=t;return n},Nn.filter=ue,Nn.flatten=function(n,t,r){var e=n?n.length:0;return r&&$r(n,t,r)&&(t=false),e?_t(n,t):[]},Nn.flattenDeep=function(n){return n&&n.length?_t(n,true):[]},Nn.flow=Ro,Nn.flowRight=Eo,Nn.forEach=lo,Nn.forEachRight=so,Nn.forIn=Bo,Nn.forInRight=Do,Nn.forOwn=Mo,Nn.forOwnRight=qo,Nn.functions=Re,
+Nn.groupBy=po,Nn.indexBy=ho,Nn.initial=function(n){return Vr(n,1)},Nn.intersection=Xu,Nn.invert=function(n,t,r){r&&$r(n,t,r)&&(t=w),r=-1;for(var e=Ko(n),u=e.length,o={};++r<u;){var i=e[r],f=n[i];t?eu.call(o,f)?o[f].push(i):o[f]=[i]:o[f]=i}return o},Nn.invoke=_o,Nn.keys=Ko,Nn.keysIn=Ee,Nn.map=ie,Nn.mapKeys=Vo,Nn.mapValues=Zo,Nn.matches=Te,Nn.matchesProperty=function(n,t){return jt(n,ft(t,true))},Nn.memoize=se,Nn.merge=Fo,Nn.method=ei,Nn.methodOf=ui,Nn.mixin=Pe,Nn.modArgs=Co,Nn.negate=function(n){if(typeof n!="function")throw new Xe(T);
+return function(){return!n.apply(this,arguments)}},Nn.omit=Yo,Nn.once=function(n){return ce(2,n)},Nn.pairs=Ce,Nn.partial=So,Nn.partialRight=Uo,Nn.partition=vo,Nn.pick=Go,Nn.pluck=function(n,t){return ie(n,Be(t))},Nn.property=Be,Nn.propertyOf=function(n){return function(t){return mt(n,Mr(t),t+"")}},Nn.pull=function(){var n=arguments,t=n[0];if(!t||!t.length)return t;for(var r=0,e=jr(),u=n.length;++r<u;)for(var o=0,i=n[r];-1<(o=e(t,i,o));)vu.call(t,o,1);return t},Nn.pullAt=Hu,Nn.range=function(n,t,r){
+r&&$r(n,t,r)&&(t=r=w),n=+n||0,r=null==r?1:+r||0,null==t?(t=n,n=0):t=+t||0;var e=-1;t=ju(du((t-n)/(r||1)),0);for(var u=De(t);++e<t;)u[e]=n,n+=r;return u},Nn.rearg=$o,Nn.reject=function(n,t,r){var e=Wo(n)?Zn:pt;return t=br(t,r,3),e(n,function(n,r,e){return!t(n,r,e)})},Nn.remove=function(n,t,r){var e=[];if(!n||!n.length)return e;var u=-1,o=[],i=n.length;for(t=br(t,r,3);++u<i;)r=n[u],t(r,u,n)&&(e.push(r),o.push(u));return Rt(n,o),e},Nn.rest=Jr,Nn.restParam=pe,Nn.set=function(n,t,r){if(null==n)return n;
+var e=t+"";t=null!=n[e]||Wr(t,n)?[e]:Mr(t);for(var e=-1,u=t.length,o=u-1,i=n;null!=i&&++e<u;){var f=t[e];de(i)&&(e==o?i[f]=r:null==i[f]&&(i[f]=Ur(t[e+1])?[]:{})),i=i[f]}return n},Nn.shuffle=function(n){return fe(n,Cu)},Nn.slice=function(n,t,r){var e=n?n.length:0;return e?(r&&typeof r!="number"&&$r(n,t,r)&&(t=0,r=e),St(n,t,r)):[]},Nn.sortBy=function(n,t,r){if(null==n)return[];r&&$r(n,t,r)&&(t=w);var e=-1;return t=br(t,r,3),n=bt(n,function(n,r,u){return{a:t(n,r,u),b:++e,c:n}}),$t(n,f)},Nn.sortByAll=mo,
+Nn.sortByOrder=function(n,t,r,e){return null==n?[]:(e&&$r(t,r,e)&&(r=w),Wo(t)||(t=null==t?[]:[t]),Wo(r)||(r=null==r?[]:[r]),Wt(n,t,r))},Nn.spread=function(n){if(typeof n!="function")throw new Xe(T);return function(t){return n.apply(this,t)}},Nn.take=function(n,t,r){return n&&n.length?((r?$r(n,t,r):null==t)&&(t=1),St(n,0,0>t?0:t)):[]},Nn.takeRight=function(n,t,r){var e=n?n.length:0;return e?((r?$r(n,t,r):null==t)&&(t=1),t=e-(+t||0),St(n,0>t?0:t)):[]},Nn.takeRightWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),false,true):[];
+},Nn.takeWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3)):[]},Nn.tap=function(n,t,r){return t.call(r,n),n},Nn.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new Xe(T);return false===r?e=false:de(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),le(n,t,{leading:e,maxWait:+t,trailing:u})},Nn.thru=re,Nn.times=function(n,t,r){if(n=wu(n),1>n||!bu(n))return[];var e=-1,u=De(ku(n,4294967295));for(t=Dt(t,r,1);++e<n;)4294967295>e?u[e]=t(e):t(e);return u},Nn.toArray=Oe,
+Nn.toPlainObject=Ie,Nn.transform=function(n,t,r,e){var u=Wo(n)||je(n);return t=br(t,e,4),null==r&&(u||de(n)?(e=n.constructor,r=u?Wo(n)?new e:[]:Pu(ye(e)?e.prototype:w)):r={}),(u?Kn:gt)(n,function(n,e,u){return t(r,n,e,u)}),r},Nn.union=to,Nn.uniq=Xr,Nn.unzip=Hr,Nn.unzipWith=Qr,Nn.values=Se,Nn.valuesIn=function(n){return Nt(n,Ee(n))},Nn.where=function(n,t){return ue(n,At(t))},Nn.without=ro,Nn.wrap=function(n,t){return t=null==t?Ne:t,dr(t,I,w,[n],[])},Nn.xor=function(){for(var n=-1,t=arguments.length;++n<t;){
+var r=arguments[n];if(Sr(r))var e=e?Hn(ct(e,r),ct(r,e)):r}return e?Lt(e):[]},Nn.zip=eo,Nn.zipObject=ne,Nn.zipWith=uo,Nn.backflow=Eo,Nn.collect=ie,Nn.compose=Eo,Nn.each=lo,Nn.eachRight=so,Nn.extend=Lo,Nn.iteratee=Le,Nn.methods=Re,Nn.object=ne,Nn.select=ue,Nn.tail=Jr,Nn.unique=Xr,Pe(Nn,Nn),Nn.add=function(n,t){return(+n||0)+(+t||0)},Nn.attempt=ri,Nn.camelCase=Jo,Nn.capitalize=function(n){return(n=u(n))&&n.charAt(0).toUpperCase()+n.slice(1)},Nn.ceil=oi,Nn.clone=function(n,t,r,e){return t&&typeof t!="boolean"&&$r(n,t,r)?t=false:typeof t=="function"&&(e=r,
+r=t,t=false),typeof r=="function"?ft(n,t,Dt(r,e,3)):ft(n,t)},Nn.cloneDeep=function(n,t,r){return typeof t=="function"?ft(n,true,Dt(t,r,3)):ft(n,true)},Nn.deburr=Ue,Nn.endsWith=function(n,t,r){n=u(n),t+="";var e=n.length;return r=r===w?e:ku(0>r?0:+r||0,e),r-=t.length,0<=r&&n.indexOf(t,r)==r},Nn.escape=function(n){return(n=u(n))&&hn.test(n)?n.replace(sn,c):n},Nn.escapeRegExp=function(n){return(n=u(n))&&xn.test(n)?n.replace(wn,l):n||"(?:)"},Nn.every=ee,Nn.find=ao,Nn.findIndex=Gu,Nn.findKey=Po,Nn.findLast=co,
+Nn.findLastIndex=Ju,Nn.findLastKey=zo,Nn.findWhere=function(n,t){return ao(n,At(t))},Nn.first=Zr,Nn.floor=ii,Nn.get=function(n,t,r){return n=null==n?w:mt(n,Mr(t),t+""),n===w?r:n},Nn.gt=he,Nn.gte=function(n,t){return n>=t},Nn.has=function(n,t){if(null==n)return false;var r=eu.call(n,t);if(!r&&!Wr(t)){if(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),null==n)return false;t=Gr(t),r=eu.call(n,t)}return r||Lr(n.length)&&Ur(t,n.length)&&(Wo(n)||_e(n)||Ae(n))},Nn.identity=Ne,Nn.includes=oe,Nn.indexOf=Yr,Nn.inRange=function(n,t,r){
+return t=+t||0,r===w?(r=t,t=0):r=+r||0,n>=ku(t,r)&&n<ju(t,r)},Nn.isArguments=_e,Nn.isArray=Wo,Nn.isBoolean=function(n){return true===n||false===n||h(n)&&ou.call(n)==D},Nn.isDate=function(n){return h(n)&&ou.call(n)==M},Nn.isElement=function(n){return!!n&&1===n.nodeType&&h(n)&&!xe(n)},Nn.isEmpty=function(n){return null==n?true:Sr(n)&&(Wo(n)||Ae(n)||_e(n)||h(n)&&ye(n.splice))?!n.length:!Ko(n).length},Nn.isEqual=ve,Nn.isError=ge,Nn.isFinite=function(n){return typeof n=="number"&&bu(n)},Nn.isFunction=ye,Nn.isMatch=function(n,t,r,e){
+return r=typeof r=="function"?Dt(r,e,3):w,xt(n,kr(t),r)},Nn.isNaN=function(n){return we(n)&&n!=+n},Nn.isNative=me,Nn.isNull=function(n){return null===n},Nn.isNumber=we,Nn.isObject=de,Nn.isPlainObject=xe,Nn.isRegExp=be,Nn.isString=Ae,Nn.isTypedArray=je,Nn.isUndefined=function(n){return n===w},Nn.kebabCase=Xo,Nn.last=Gr,Nn.lastIndexOf=function(n,t,r){var e=n?n.length:0;if(!e)return-1;var u=e;if(typeof r=="number")u=(0>r?ju(e+r,0):ku(r||0,e-1))+1;else if(r)return u=zt(n,t,true)-1,n=n[u],(t===t?t===n:n!==n)?u:-1;
+if(t!==t)return p(n,u,true);for(;u--;)if(n[u]===t)return u;return-1},Nn.lt=ke,Nn.lte=function(n,t){return n<=t},Nn.max=fi,Nn.min=ai,Nn.noConflict=function(){return Yn._=iu,this},Nn.noop=ze,Nn.now=wo,Nn.pad=function(n,t,r){n=u(n),t=+t;var e=n.length;return e<t&&bu(t)?(e=(t-e)/2,t=wu(e),e=du(e),r=_r("",e,r),r.slice(0,t)+n+r):n},Nn.padLeft=Ho,Nn.padRight=Qo,Nn.parseInt=function(n,t,r){return(r?$r(n,t,r):null==t)?t=0:t&&(t=+t),n=We(n),Iu(n,t||(On.test(n)?16:10))},Nn.random=function(n,t,r){r&&$r(n,t,r)&&(t=r=w);
+var e=null==n,u=null==t;return null==r&&(u&&typeof n=="boolean"?(r=n,n=1):typeof t=="boolean"&&(r=t,u=true)),e&&u&&(t=1,u=false),n=+n||0,u?(t=n,n=0):t=+t||0,r||n%1||t%1?(r=Ru(),ku(n+r*(t-n+lu("1e-"+((r+"").length-1))),t)):Et(n,t)},Nn.reduce=go,Nn.reduceRight=yo,Nn.repeat=$e,Nn.result=function(n,t,r){var e=null==n?w:Dr(n)[t];return e===w&&(null==n||Wr(t,n)||(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),e=null==n?w:Dr(n)[Gr(t)]),e=e===w?r:e),ye(e)?e.call(n):e},Nn.round=ci,Nn.runInContext=m,Nn.size=function(n){
+var t=n?Vu(n):0;return Lr(t)?t:Ko(n).length},Nn.snakeCase=ni,Nn.some=ae,Nn.sortedIndex=Qu,Nn.sortedLastIndex=no,Nn.startCase=ti,Nn.startsWith=function(n,t,r){return n=u(n),r=null==r?0:ku(0>r?0:+r||0,n.length),n.lastIndexOf(t,r)==r},Nn.sum=function(n,t,r){if(r&&$r(n,t,r)&&(t=w),t=br(t,r,3),1==t.length){n=Wo(n)?n:Br(n),r=n.length;for(var e=0;r--;)e+=+t(n[r])||0;n=e}else n=Ft(n,t);return n},Nn.template=function(n,t,r){var e=Nn.templateSettings;r&&$r(n,t,r)&&(t=r=w),n=u(n),t=rt(et({},r||t),e,tt),r=rt(et({},t.imports),e.imports,tt);
+var o,i,f=Ko(r),a=Nt(r,f),c=0;r=t.interpolate||Cn;var l="__p+='";r=Ge((t.escape||Cn).source+"|"+r.source+"|"+(r===gn?jn:Cn).source+"|"+(t.evaluate||Cn).source+"|$","g");var p="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";if(n.replace(r,function(t,r,e,u,f,a){return e||(e=u),l+=n.slice(c,a).replace(Sn,s),r&&(o=true,l+="'+__e("+r+")+'"),f&&(i=true,l+="';"+f+";\n__p+='"),e&&(l+="'+((__t=("+e+"))==null?'':__t)+'"),c=a+t.length,t}),l+="';",(t=t.variable)||(l="with(obj){"+l+"}"),l=(i?l.replace(fn,""):l).replace(an,"$1").replace(cn,"$1;"),
+l="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(o?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}",t=ri(function(){return Ke(f,p+"return "+l).apply(w,a)}),t.source=l,ge(t))throw t;return t},Nn.trim=We,Nn.trimLeft=function(n,t,r){var e=n;return(n=u(n))?n.slice((r?$r(e,t,r):null==t)?g(n):o(n,t+"")):n},Nn.trimRight=function(n,t,r){var e=n;return(n=u(n))?(r?$r(e,t,r):null==t)?n.slice(0,y(n)+1):n.slice(0,i(n,t+"")+1):n;
+},Nn.trunc=function(n,t,r){r&&$r(n,t,r)&&(t=w);var e=S;if(r=U,null!=t)if(de(t)){var o="separator"in t?t.separator:o,e="length"in t?+t.length||0:e;r="omission"in t?u(t.omission):r}else e=+t||0;if(n=u(n),e>=n.length)return n;if(e-=r.length,1>e)return r;if(t=n.slice(0,e),null==o)return t+r;if(be(o)){if(n.slice(e).search(o)){var i,f=n.slice(0,e);for(o.global||(o=Ge(o.source,(kn.exec(o)||"")+"g")),o.lastIndex=0;n=o.exec(f);)i=n.index;t=t.slice(0,null==i?e:i)}}else n.indexOf(o,e)!=e&&(o=t.lastIndexOf(o),
+-1<o&&(t=t.slice(0,o)));return t+r},Nn.unescape=function(n){return(n=u(n))&&pn.test(n)?n.replace(ln,d):n},Nn.uniqueId=function(n){var t=++uu;return u(n)+t},Nn.words=Fe,Nn.all=ee,Nn.any=ae,Nn.contains=oe,Nn.eq=ve,Nn.detect=ao,Nn.foldl=go,Nn.foldr=yo,Nn.head=Zr,Nn.include=oe,Nn.inject=go,Pe(Nn,function(){var n={};return gt(Nn,function(t,r){Nn.prototype[r]||(n[r]=t)}),n}(),false),Nn.sample=fe,Nn.prototype.sample=function(n){return this.__chain__||null!=n?this.thru(function(t){return fe(t,n)}):fe(this.value());
+},Nn.VERSION=x,Kn("bind bindKey curry curryRight partial partialRight".split(" "),function(n){Nn[n].placeholder=Nn}),Kn(["drop","take"],function(n,t){zn.prototype[n]=function(r){var e=this.__filtered__;if(e&&!t)return new zn(this);r=null==r?1:ju(wu(r)||0,0);var u=this.clone();return e?u.__takeCount__=ku(u.__takeCount__,r):u.__views__.push({size:r,type:n+(0>u.__dir__?"Right":"")}),u},zn.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),Kn(["filter","map","takeWhile"],function(n,t){
+var r=t+1,e=r!=N;zn.prototype[n]=function(n,t){var u=this.clone();return u.__iteratees__.push({iteratee:br(n,t,1),type:r}),u.__filtered__=u.__filtered__||e,u}}),Kn(["first","last"],function(n,t){var r="take"+(t?"Right":"");zn.prototype[n]=function(){return this[r](1).value()[0]}}),Kn(["initial","rest"],function(n,t){var r="drop"+(t?"":"Right");zn.prototype[n]=function(){return this.__filtered__?new zn(this):this[r](1)}}),Kn(["pluck","where"],function(n,t){var r=t?"filter":"map",e=t?At:Be;zn.prototype[n]=function(n){
+return this[r](e(n))}}),zn.prototype.compact=function(){return this.filter(Ne)},zn.prototype.reject=function(n,t){return n=br(n,t,1),this.filter(function(t){return!n(t)})},zn.prototype.slice=function(n,t){n=null==n?0:+n||0;var r=this;return r.__filtered__&&(0<n||0>t)?new zn(r):(0>n?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==w&&(t=+t||0,r=0>t?r.dropRight(-t):r.take(t-n)),r)},zn.prototype.takeRightWhile=function(n,t){return this.reverse().takeWhile(n,t).reverse()},zn.prototype.toArray=function(){return this.take(Cu);
+},gt(zn.prototype,function(n,t){var r=/^(?:filter|map|reject)|While$/.test(t),e=/^(?:first|last)$/.test(t),u=Nn[e?"take"+("last"==t?"Right":""):t];u&&(Nn.prototype[t]=function(){var t=e?[1]:arguments,o=this.__chain__,i=this.__wrapped__,f=!!this.__actions__.length,a=i instanceof zn,c=t[0],l=a||Wo(i);l&&r&&typeof c=="function"&&1!=c.length&&(a=l=false);var s=function(n){return e&&o?u(n,1)[0]:u.apply(w,Hn([n],t))},c={func:re,args:[s],thisArg:w},f=a&&!f;return e&&!o?f?(i=i.clone(),i.__actions__.push(c),
+n.call(i)):u.call(w,this.value())[0]:!e&&l?(i=f?i:new zn(this),i=n.apply(i,t),i.__actions__.push(c),new Pn(i,o)):this.thru(s)})}),Kn("join pop push replace shift sort splice split unshift".split(" "),function(n){var t=(/^(?:replace|split)$/.test(n)?tu:He)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=!Tu.spliceObjects&&/^(?:pop|shift|splice)$/.test(n),u=/^(?:join|pop|replace|shift)$/.test(n),o=e?function(){var n=t.apply(this,arguments);return 0===this.length&&delete this[0],n}:t;Nn.prototype[n]=function(){
+var n=arguments;return u&&!this.__chain__?o.apply(this.value(),n):this[r](function(t){return o.apply(t,n)})}}),gt(zn.prototype,function(n,t){var r=Nn[t];if(r){var e=r.name+"";(Fu[e]||(Fu[e]=[])).push({name:t,func:r})}}),Fu[hr(w,A).name]=[{name:"wrapper",func:w}],zn.prototype.clone=function(){var n=new zn(this.__wrapped__);return n.__actions__=qn(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=qn(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=qn(this.__views__),
+n},zn.prototype.reverse=function(){if(this.__filtered__){var n=new zn(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},zn.prototype.value=function(){var n,t=this.__wrapped__.value(),r=this.__dir__,e=Wo(t),u=0>r,o=e?t.length:0;n=0;for(var i=o,f=this.__views__,a=-1,c=f.length;++a<c;){var l=f[a],s=l.size;switch(l.type){case"drop":n+=s;break;case"dropRight":i-=s;break;case"take":i=ku(i,n+s);break;case"takeRight":n=ju(n,i-s)}}if(n={start:n,end:i},i=n.start,f=n.end,n=f-i,
+u=u?f:i-1,i=this.__iteratees__,f=i.length,a=0,c=ku(n,this.__takeCount__),!e||o<F||o==n&&c==n)return Pt(t,this.__actions__);e=[];n:for(;n--&&a<c;){for(u+=r,o=-1,l=t[u];++o<f;){var p=i[o],s=p.type,p=p.iteratee(l);if(s==N)l=p;else if(!p){if(s==L)continue n;break n}}e[a++]=l}return e},Nn.prototype.chain=function(){return te(this)},Nn.prototype.commit=function(){return new Pn(this.value(),this.__chain__)},Nn.prototype.concat=oo,Nn.prototype.plant=function(n){for(var t,r=this;r instanceof Tn;){var e=qr(r);
+t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},Nn.prototype.reverse=function(){var n=this.__wrapped__,t=function(n){return n.reverse()};return n instanceof zn?(this.__actions__.length&&(n=new zn(this)),n=n.reverse(),n.__actions__.push({func:re,args:[t],thisArg:w}),new Pn(n,this.__chain__)):this.thru(t)},Nn.prototype.toString=function(){return this.value()+""},Nn.prototype.run=Nn.prototype.toJSON=Nn.prototype.valueOf=Nn.prototype.value=function(){return Pt(this.__wrapped__,this.__actions__);
+},Nn.prototype.collect=Nn.prototype.map,Nn.prototype.head=Nn.prototype.first,Nn.prototype.select=Nn.prototype.filter,Nn.prototype.tail=Nn.prototype.rest,Nn}var w,x="3.10.1",b=1,A=2,j=4,k=8,O=16,I=32,R=64,E=128,C=256,S=30,U="...",$=150,W=16,F=200,L=1,N=2,T="Expected a function",P="__lodash_placeholder__",z="[object Arguments]",B="[object Array]",D="[object Boolean]",M="[object Date]",q="[object Error]",K="[object Function]",V="[object Number]",Z="[object Object]",Y="[object RegExp]",G="[object String]",J="[object ArrayBuffer]",X="[object Float32Array]",H="[object Float64Array]",Q="[object Int8Array]",nn="[object Int16Array]",tn="[object Int32Array]",rn="[object Uint8Array]",en="[object Uint8ClampedArray]",un="[object Uint16Array]",on="[object Uint32Array]",fn=/\b__p\+='';/g,an=/\b(__p\+=)''\+/g,cn=/(__e\(.*?\)|\b__t\))\+'';/g,ln=/&(?:amp|lt|gt|quot|#39|#96);/g,sn=/[&<>"'`]/g,pn=RegExp(ln.source),hn=RegExp(sn.source),_n=/<%-([\s\S]+?)%>/g,vn=/<%([\s\S]+?)%>/g,gn=/<%=([\s\S]+?)%>/g,yn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,dn=/^\w*$/,mn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,wn=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,xn=RegExp(wn.source),bn=/[\u0300-\u036f\ufe20-\ufe23]/g,An=/\\(\\)?/g,jn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,kn=/\w*$/,On=/^0[xX]/,In=/^\[object .+?Constructor\]$/,Rn=/^\d+$/,En=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,Cn=/($^)/,Sn=/['\n\r\u2028\u2029\\]/g,Un=RegExp("[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?=[A-Z\\xc0-\\xd6\\xd8-\\xde][a-z\\xdf-\\xf6\\xf8-\\xff]+)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+|[A-Z\\xc0-\\xd6\\xd8-\\xde]+|[0-9]+","g"),$n="Array ArrayBuffer Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Math Number Object RegExp Set String _ clearTimeout isFinite parseFloat parseInt setTimeout TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap".split(" "),Wn="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Fn={};
+Fn[X]=Fn[H]=Fn[Q]=Fn[nn]=Fn[tn]=Fn[rn]=Fn[en]=Fn[un]=Fn[on]=true,Fn[z]=Fn[B]=Fn[J]=Fn[D]=Fn[M]=Fn[q]=Fn[K]=Fn["[object Map]"]=Fn[V]=Fn[Z]=Fn[Y]=Fn["[object Set]"]=Fn[G]=Fn["[object WeakMap]"]=false;var Ln={};Ln[z]=Ln[B]=Ln[J]=Ln[D]=Ln[M]=Ln[X]=Ln[H]=Ln[Q]=Ln[nn]=Ln[tn]=Ln[V]=Ln[Z]=Ln[Y]=Ln[G]=Ln[rn]=Ln[en]=Ln[un]=Ln[on]=true,Ln[q]=Ln[K]=Ln["[object Map]"]=Ln["[object Set]"]=Ln["[object WeakMap]"]=false;var Nn={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a",
+"\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y",
+"\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss"},Tn={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","`":"&#96;"},Pn={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'","&#96;":"`"},zn={"function":true,object:true},Bn={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"},Dn={"\\":"\\",
+"'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Mn=zn[typeof exports]&&exports&&!exports.nodeType&&exports,qn=zn[typeof module]&&module&&!module.nodeType&&module,Kn=zn[typeof self]&&self&&self.Object&&self,Vn=zn[typeof window]&&window&&window.Object&&window,Zn=qn&&qn.exports===Mn&&Mn,Yn=Mn&&qn&&typeof global=="object"&&global&&global.Object&&global||Vn!==(this&&this.window)&&Vn||Kn||this,Gn=function(){try{Object({toString:0}+"")}catch(n){return function(){return false}}return function(n){
+return typeof n.toString!="function"&&typeof(n+"")=="string"}}(),Jn=m();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(Yn._=Jn, define(function(){return Jn})):Mn&&qn?Zn?(qn.exports=Jn)._=Jn:Mn._=Jn:Yn._=Jn}).call(this);
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/marked.js b/profiles/killbill/src/main/webapp/lib/marked.js
new file mode 100644
index 0000000..c2a678d
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/marked.js
@@ -0,0 +1,1272 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+;(function() {
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+  newline: /^\n+/,
+  code: /^( {4}[^\n]+\n*)+/,
+  fences: noop,
+  hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+  heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+  nptable: noop,
+  lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+  blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+  list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+  html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+  def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+  table: noop,
+  paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+  text: /^[^\n]+/
+};
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = replace(block.item, 'gm')
+  (/bull/g, block.bullet)
+  ();
+
+block.list = replace(block.list)
+  (/bull/g, block.bullet)
+  ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+  ('def', '\\n+(?=' + block.def.source + ')')
+  ();
+
+block.blockquote = replace(block.blockquote)
+  ('def', block.def)
+  ();
+
+block._tag = '(?!(?:'
+  + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+  + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+  + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+
+block.html = replace(block.html)
+  ('comment', /<!--[\s\S]*?-->/)
+  ('closed', /<(tag)[\s\S]+?<\/\1>/)
+  ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
+  (/tag/g, block._tag)
+  ();
+
+block.paragraph = replace(block.paragraph)
+  ('hr', block.hr)
+  ('heading', block.heading)
+  ('lheading', block.lheading)
+  ('blockquote', block.blockquote)
+  ('tag', '<' + block._tag)
+  ('def', block.def)
+  ();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+  fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
+  paragraph: /^/
+});
+
+block.gfm.paragraph = replace(block.paragraph)
+  ('(?!', '(?!'
+    + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+    + block.list.source.replace('\\1', '\\3') + '|')
+  ();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+  nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+  table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+  this.tokens = [];
+  this.tokens.links = {};
+  this.options = options || marked.defaults;
+  this.rules = block.normal;
+
+  if (this.options.gfm) {
+    if (this.options.tables) {
+      this.rules = block.tables;
+    } else {
+      this.rules = block.gfm;
+    }
+  }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+  var lexer = new Lexer(options);
+  return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+  src = src
+    .replace(/\r\n|\r/g, '\n')
+    .replace(/\t/g, '    ')
+    .replace(/\u00a0/g, ' ')
+    .replace(/\u2424/g, '\n');
+
+  return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top, bq) {
+  var src = src.replace(/^ +$/gm, '')
+    , next
+    , loose
+    , cap
+    , bull
+    , b
+    , item
+    , space
+    , i
+    , l;
+
+  while (src) {
+    // newline
+    if (cap = this.rules.newline.exec(src)) {
+      src = src.substring(cap[0].length);
+      if (cap[0].length > 1) {
+        this.tokens.push({
+          type: 'space'
+        });
+      }
+    }
+
+    // code
+    if (cap = this.rules.code.exec(src)) {
+      src = src.substring(cap[0].length);
+      cap = cap[0].replace(/^ {4}/gm, '');
+      this.tokens.push({
+        type: 'code',
+        text: !this.options.pedantic
+          ? cap.replace(/\n+$/, '')
+          : cap
+      });
+      continue;
+    }
+
+    // fences (gfm)
+    if (cap = this.rules.fences.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'code',
+        lang: cap[2],
+        text: cap[3]
+      });
+      continue;
+    }
+
+    // heading
+    if (cap = this.rules.heading.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'heading',
+        depth: cap[1].length,
+        text: cap[2]
+      });
+      continue;
+    }
+
+    // table no leading pipe (gfm)
+    if (top && (cap = this.rules.nptable.exec(src))) {
+      src = src.substring(cap[0].length);
+
+      item = {
+        type: 'table',
+        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+        cells: cap[3].replace(/\n$/, '').split('\n')
+      };
+
+      for (i = 0; i < item.align.length; i++) {
+        if (/^ *-+: *$/.test(item.align[i])) {
+          item.align[i] = 'right';
+        } else if (/^ *:-+: *$/.test(item.align[i])) {
+          item.align[i] = 'center';
+        } else if (/^ *:-+ *$/.test(item.align[i])) {
+          item.align[i] = 'left';
+        } else {
+          item.align[i] = null;
+        }
+      }
+
+      for (i = 0; i < item.cells.length; i++) {
+        item.cells[i] = item.cells[i].split(/ *\| */);
+      }
+
+      this.tokens.push(item);
+
+      continue;
+    }
+
+    // lheading
+    if (cap = this.rules.lheading.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'heading',
+        depth: cap[2] === '=' ? 1 : 2,
+        text: cap[1]
+      });
+      continue;
+    }
+
+    // hr
+    if (cap = this.rules.hr.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'hr'
+      });
+      continue;
+    }
+
+    // blockquote
+    if (cap = this.rules.blockquote.exec(src)) {
+      src = src.substring(cap[0].length);
+
+      this.tokens.push({
+        type: 'blockquote_start'
+      });
+
+      cap = cap[0].replace(/^ *> ?/gm, '');
+
+      // Pass `top` to keep the current
+      // "toplevel" state. This is exactly
+      // how markdown.pl works.
+      this.token(cap, top, true);
+
+      this.tokens.push({
+        type: 'blockquote_end'
+      });
+
+      continue;
+    }
+
+    // list
+    if (cap = this.rules.list.exec(src)) {
+      src = src.substring(cap[0].length);
+      bull = cap[2];
+
+      this.tokens.push({
+        type: 'list_start',
+        ordered: bull.length > 1
+      });
+
+      // Get each top-level item.
+      cap = cap[0].match(this.rules.item);
+
+      next = false;
+      l = cap.length;
+      i = 0;
+
+      for (; i < l; i++) {
+        item = cap[i];
+
+        // Remove the list item's bullet
+        // so it is seen as the next token.
+        space = item.length;
+        item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+        // Outdent whatever the
+        // list item contains. Hacky.
+        if (~item.indexOf('\n ')) {
+          space -= item.length;
+          item = !this.options.pedantic
+            ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+            : item.replace(/^ {1,4}/gm, '');
+        }
+
+        // Determine whether the next list item belongs here.
+        // Backpedal if it does not belong in this list.
+        if (this.options.smartLists && i !== l - 1) {
+          b = block.bullet.exec(cap[i + 1])[0];
+          if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+            src = cap.slice(i + 1).join('\n') + src;
+            i = l - 1;
+          }
+        }
+
+        // Determine whether item is loose or not.
+        // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+        // for discount behavior.
+        loose = next || /\n\n(?!\s*$)/.test(item);
+        if (i !== l - 1) {
+          next = item.charAt(item.length - 1) === '\n';
+          if (!loose) loose = next;
+        }
+
+        this.tokens.push({
+          type: loose
+            ? 'loose_item_start'
+            : 'list_item_start'
+        });
+
+        // Recurse.
+        this.token(item, false, bq);
+
+        this.tokens.push({
+          type: 'list_item_end'
+        });
+      }
+
+      this.tokens.push({
+        type: 'list_end'
+      });
+
+      continue;
+    }
+
+    // html
+    if (cap = this.rules.html.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: this.options.sanitize
+          ? 'paragraph'
+          : 'html',
+        pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
+        text: cap[0]
+      });
+      continue;
+    }
+
+    // def
+    if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+      src = src.substring(cap[0].length);
+      this.tokens.links[cap[1].toLowerCase()] = {
+        href: cap[2],
+        title: cap[3]
+      };
+      continue;
+    }
+
+    // table (gfm)
+    if (top && (cap = this.rules.table.exec(src))) {
+      src = src.substring(cap[0].length);
+
+      item = {
+        type: 'table',
+        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+        cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+      };
+
+      for (i = 0; i < item.align.length; i++) {
+        if (/^ *-+: *$/.test(item.align[i])) {
+          item.align[i] = 'right';
+        } else if (/^ *:-+: *$/.test(item.align[i])) {
+          item.align[i] = 'center';
+        } else if (/^ *:-+ *$/.test(item.align[i])) {
+          item.align[i] = 'left';
+        } else {
+          item.align[i] = null;
+        }
+      }
+
+      for (i = 0; i < item.cells.length; i++) {
+        item.cells[i] = item.cells[i]
+          .replace(/^ *\| *| *\| *$/g, '')
+          .split(/ *\| */);
+      }
+
+      this.tokens.push(item);
+
+      continue;
+    }
+
+    // top-level paragraph
+    if (top && (cap = this.rules.paragraph.exec(src))) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'paragraph',
+        text: cap[1].charAt(cap[1].length - 1) === '\n'
+          ? cap[1].slice(0, -1)
+          : cap[1]
+      });
+      continue;
+    }
+
+    // text
+    if (cap = this.rules.text.exec(src)) {
+      // Top-level should never reach here.
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'text',
+        text: cap[0]
+      });
+      continue;
+    }
+
+    if (src) {
+      throw new
+        Error('Infinite loop on byte: ' + src.charCodeAt(0));
+    }
+  }
+
+  return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+  escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+  url: noop,
+  tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+  link: /^!?\[(inside)\]\(href\)/,
+  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+  strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+  em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+  code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+  br: /^ {2,}\n(?!\s*$)/,
+  del: noop,
+  text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
+};
+
+inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
+inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+inline.link = replace(inline.link)
+  ('inside', inline._inside)
+  ('href', inline._href)
+  ();
+
+inline.reflink = replace(inline.reflink)
+  ('inside', inline._inside)
+  ();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+  em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+  escape: replace(inline.escape)('])', '~|])')(),
+  url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+  del: /^~~(?=\S)([\s\S]*?\S)~~/,
+  text: replace(inline.text)
+    (']|', '~]|')
+    ('|', '|https?://|')
+    ()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+  br: replace(inline.br)('{2,}', '*')(),
+  text: replace(inline.gfm.text)('{2,}', '*')()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+  this.options = options || marked.defaults;
+  this.links = links;
+  this.rules = inline.normal;
+  this.renderer = this.options.renderer || new Renderer;
+  this.renderer.options = this.options;
+
+  if (!this.links) {
+    throw new
+      Error('Tokens array requires a `links` property.');
+  }
+
+  if (this.options.gfm) {
+    if (this.options.breaks) {
+      this.rules = inline.breaks;
+    } else {
+      this.rules = inline.gfm;
+    }
+  } else if (this.options.pedantic) {
+    this.rules = inline.pedantic;
+  }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+  var inline = new InlineLexer(links, options);
+  return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+  var out = ''
+    , link
+    , text
+    , href
+    , cap;
+
+  while (src) {
+    // escape
+    if (cap = this.rules.escape.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += cap[1];
+      continue;
+    }
+
+    // autolink
+    if (cap = this.rules.autolink.exec(src)) {
+      src = src.substring(cap[0].length);
+      if (cap[2] === '@') {
+        text = cap[1].charAt(6) === ':'
+          ? this.mangle(cap[1].substring(7))
+          : this.mangle(cap[1]);
+        href = this.mangle('mailto:') + text;
+      } else {
+        text = escape(cap[1]);
+        href = text;
+      }
+      out += this.renderer.link(href, null, text);
+      continue;
+    }
+
+    // url (gfm)
+    if (!this.inLink && (cap = this.rules.url.exec(src))) {
+      src = src.substring(cap[0].length);
+      text = escape(cap[1]);
+      href = text;
+      out += this.renderer.link(href, null, text);
+      continue;
+    }
+
+    // tag
+    if (cap = this.rules.tag.exec(src)) {
+      if (!this.inLink && /^<a /i.test(cap[0])) {
+        this.inLink = true;
+      } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
+        this.inLink = false;
+      }
+      src = src.substring(cap[0].length);
+      out += this.options.sanitize
+        ? escape(cap[0])
+        : cap[0];
+      continue;
+    }
+
+    // link
+    if (cap = this.rules.link.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.inLink = true;
+      out += this.outputLink(cap, {
+        href: cap[2],
+        title: cap[3]
+      });
+      this.inLink = false;
+      continue;
+    }
+
+    // reflink, nolink
+    if ((cap = this.rules.reflink.exec(src))
+        || (cap = this.rules.nolink.exec(src))) {
+      src = src.substring(cap[0].length);
+      link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+      link = this.links[link.toLowerCase()];
+      if (!link || !link.href) {
+        out += cap[0].charAt(0);
+        src = cap[0].substring(1) + src;
+        continue;
+      }
+      this.inLink = true;
+      out += this.outputLink(cap, link);
+      this.inLink = false;
+      continue;
+    }
+
+    // strong
+    if (cap = this.rules.strong.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.strong(this.output(cap[2] || cap[1]));
+      continue;
+    }
+
+    // em
+    if (cap = this.rules.em.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.em(this.output(cap[2] || cap[1]));
+      continue;
+    }
+
+    // code
+    if (cap = this.rules.code.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.codespan(escape(cap[2], true));
+      continue;
+    }
+
+    // br
+    if (cap = this.rules.br.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.br();
+      continue;
+    }
+
+    // del (gfm)
+    if (cap = this.rules.del.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.del(this.output(cap[1]));
+      continue;
+    }
+
+    // text
+    if (cap = this.rules.text.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += escape(this.smartypants(cap[0]));
+      continue;
+    }
+
+    if (src) {
+      throw new
+        Error('Infinite loop on byte: ' + src.charCodeAt(0));
+    }
+  }
+
+  return out;
+};
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+  var href = escape(link.href)
+    , title = link.title ? escape(link.title) : null;
+
+  return cap[0].charAt(0) !== '!'
+    ? this.renderer.link(href, title, this.output(cap[1]))
+    : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+  if (!this.options.smartypants) return text;
+  return text
+    // em-dashes
+    .replace(/--/g, '\u2014')
+    // opening singles
+    .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+    // closing singles & apostrophes
+    .replace(/'/g, '\u2019')
+    // opening doubles
+    .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+    // closing doubles
+    .replace(/"/g, '\u201d')
+    // ellipses
+    .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+  var out = ''
+    , l = text.length
+    , i = 0
+    , ch;
+
+  for (; i < l; i++) {
+    ch = text.charCodeAt(i);
+    if (Math.random() > 0.5) {
+      ch = 'x' + ch.toString(16);
+    }
+    out += '&#' + ch + ';';
+  }
+
+  return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+  this.options = options || {};
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+  if (this.options.highlight) {
+    var out = this.options.highlight(code, lang);
+    if (out != null && out !== code) {
+      escaped = true;
+      code = out;
+    }
+  }
+
+  if (!lang) {
+    return '<pre><code>'
+      + (escaped ? code : escape(code, true))
+      + '\n</code></pre>';
+  }
+
+  return '<pre><code class="'
+    + this.options.langPrefix
+    + escape(lang, true)
+    + '">'
+    + (escaped ? code : escape(code, true))
+    + '\n</code></pre>\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+  return '<blockquote>\n' + quote + '</blockquote>\n';
+};
+
+Renderer.prototype.html = function(html) {
+  return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+  return '<h'
+    + level
+    + ' id="'
+    + this.options.headerPrefix
+    + raw.toLowerCase().replace(/[^\w]+/g, '-')
+    + '">'
+    + text
+    + '</h'
+    + level
+    + '>\n';
+};
+
+Renderer.prototype.hr = function() {
+  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
+};
+
+Renderer.prototype.list = function(body, ordered) {
+  var type = ordered ? 'ol' : 'ul';
+  return '<' + type + '>\n' + body + '</' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+  return '<li>' + text + '</li>\n';
+};
+
+Renderer.prototype.paragraph = function(text) {
+  return '<p>' + text + '</p>\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+  return '<table>\n'
+    + '<thead>\n'
+    + header
+    + '</thead>\n'
+    + '<tbody>\n'
+    + body
+    + '</tbody>\n'
+    + '</table>\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+  return '<tr>\n' + content + '</tr>\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+  var type = flags.header ? 'th' : 'td';
+  var tag = flags.align
+    ? '<' + type + ' style="text-align:' + flags.align + '">'
+    : '<' + type + '>';
+  return tag + content + '</' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+  return '<strong>' + text + '</strong>';
+};
+
+Renderer.prototype.em = function(text) {
+  return '<em>' + text + '</em>';
+};
+
+Renderer.prototype.codespan = function(text) {
+  return '<code>' + text + '</code>';
+};
+
+Renderer.prototype.br = function() {
+  return this.options.xhtml ? '<br/>' : '<br>';
+};
+
+Renderer.prototype.del = function(text) {
+  return '<del>' + text + '</del>';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+  if (this.options.sanitize) {
+    try {
+      var prot = decodeURIComponent(unescape(href))
+        .replace(/[^\w:]/g, '')
+        .toLowerCase();
+    } catch (e) {
+      return '';
+    }
+    if (prot.indexOf('javascript:') === 0) {
+      return '';
+    }
+  }
+  var out = '<a href="' + href + '"';
+  if (title) {
+    out += ' title="' + title + '"';
+  }
+  out += '>' + text + '</a>';
+  return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+  var out = '<img src="' + href + '" alt="' + text + '"';
+  if (title) {
+    out += ' title="' + title + '"';
+  }
+  out += this.options.xhtml ? '/>' : '>';
+  return out;
+};
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+  this.tokens = [];
+  this.token = null;
+  this.options = options || marked.defaults;
+  this.options.renderer = this.options.renderer || new Renderer;
+  this.renderer = this.options.renderer;
+  this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options, renderer) {
+  var parser = new Parser(options, renderer);
+  return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+  this.inline = new InlineLexer(src.links, this.options, this.renderer);
+  this.tokens = src.reverse();
+
+  var out = '';
+  while (this.next()) {
+    out += this.tok();
+  }
+
+  return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+  return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+  return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+  var body = this.token.text;
+
+  while (this.peek().type === 'text') {
+    body += '\n' + this.next().text;
+  }
+
+  return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+  switch (this.token.type) {
+    case 'space': {
+      return '';
+    }
+    case 'hr': {
+      return this.renderer.hr();
+    }
+    case 'heading': {
+      return this.renderer.heading(
+        this.inline.output(this.token.text),
+        this.token.depth,
+        this.token.text);
+    }
+    case 'code': {
+      return this.renderer.code(this.token.text,
+        this.token.lang,
+        this.token.escaped);
+    }
+    case 'table': {
+      var header = ''
+        , body = ''
+        , i
+        , row
+        , cell
+        , flags
+        , j;
+
+      // header
+      cell = '';
+      for (i = 0; i < this.token.header.length; i++) {
+        flags = { header: true, align: this.token.align[i] };
+        cell += this.renderer.tablecell(
+          this.inline.output(this.token.header[i]),
+          { header: true, align: this.token.align[i] }
+        );
+      }
+      header += this.renderer.tablerow(cell);
+
+      for (i = 0; i < this.token.cells.length; i++) {
+        row = this.token.cells[i];
+
+        cell = '';
+        for (j = 0; j < row.length; j++) {
+          cell += this.renderer.tablecell(
+            this.inline.output(row[j]),
+            { header: false, align: this.token.align[j] }
+          );
+        }
+
+        body += this.renderer.tablerow(cell);
+      }
+      return this.renderer.table(header, body);
+    }
+    case 'blockquote_start': {
+      var body = '';
+
+      while (this.next().type !== 'blockquote_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.blockquote(body);
+    }
+    case 'list_start': {
+      var body = ''
+        , ordered = this.token.ordered;
+
+      while (this.next().type !== 'list_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.list(body, ordered);
+    }
+    case 'list_item_start': {
+      var body = '';
+
+      while (this.next().type !== 'list_item_end') {
+        body += this.token.type === 'text'
+          ? this.parseText()
+          : this.tok();
+      }
+
+      return this.renderer.listitem(body);
+    }
+    case 'loose_item_start': {
+      var body = '';
+
+      while (this.next().type !== 'list_item_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.listitem(body);
+    }
+    case 'html': {
+      var html = !this.token.pre && !this.options.pedantic
+        ? this.inline.output(this.token.text)
+        : this.token.text;
+      return this.renderer.html(html);
+    }
+    case 'paragraph': {
+      return this.renderer.paragraph(this.inline.output(this.token.text));
+    }
+    case 'text': {
+      return this.renderer.paragraph(this.parseText());
+    }
+  }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+  return html
+    .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#39;');
+}
+
+function unescape(html) {
+  return html.replace(/&([#\w]+);/g, function(_, n) {
+    n = n.toLowerCase();
+    if (n === 'colon') return ':';
+    if (n.charAt(0) === '#') {
+      return n.charAt(1) === 'x'
+        ? String.fromCharCode(parseInt(n.substring(2), 16))
+        : String.fromCharCode(+n.substring(1));
+    }
+    return '';
+  });
+}
+
+function replace(regex, opt) {
+  regex = regex.source;
+  opt = opt || '';
+  return function self(name, val) {
+    if (!name) return new RegExp(regex, opt);
+    val = val.source || val;
+    val = val.replace(/(^|[^\[])\^/g, '$1');
+    regex = regex.replace(name, val);
+    return self;
+  };
+}
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+  var i = 1
+    , target
+    , key;
+
+  for (; i < arguments.length; i++) {
+    target = arguments[i];
+    for (key in target) {
+      if (Object.prototype.hasOwnProperty.call(target, key)) {
+        obj[key] = target[key];
+      }
+    }
+  }
+
+  return obj;
+}
+
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+  if (callback || typeof opt === 'function') {
+    if (!callback) {
+      callback = opt;
+      opt = null;
+    }
+
+    opt = merge({}, marked.defaults, opt || {});
+
+    var highlight = opt.highlight
+      , tokens
+      , pending
+      , i = 0;
+
+    try {
+      tokens = Lexer.lex(src, opt)
+    } catch (e) {
+      return callback(e);
+    }
+
+    pending = tokens.length;
+
+    var done = function(err) {
+      if (err) {
+        opt.highlight = highlight;
+        return callback(err);
+      }
+
+      var out;
+
+      try {
+        out = Parser.parse(tokens, opt);
+      } catch (e) {
+        err = e;
+      }
+
+      opt.highlight = highlight;
+
+      return err
+        ? callback(err)
+        : callback(null, out);
+    };
+
+    if (!highlight || highlight.length < 3) {
+      return done();
+    }
+
+    delete opt.highlight;
+
+    if (!pending) return done();
+
+    for (; i < tokens.length; i++) {
+      (function(token) {
+        if (token.type !== 'code') {
+          return --pending || done();
+        }
+        return highlight(token.text, token.lang, function(err, code) {
+          if (err) return done(err);
+          if (code == null || code === token.text) {
+            return --pending || done();
+          }
+          token.text = code;
+          token.escaped = true;
+          --pending || done();
+        });
+      })(tokens[i]);
+    }
+
+    return;
+  }
+  try {
+    if (opt) opt = merge({}, marked.defaults, opt);
+    return Parser.parse(Lexer.lex(src, opt), opt);
+  } catch (e) {
+    e.message += '\nPlease report this to https://github.com/chjj/marked.';
+    if ((opt || marked.defaults).silent) {
+      return '<p>An error occured:</p><pre>'
+        + escape(e.message + '', true)
+        + '</pre>';
+    }
+    throw e;
+  }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+  merge(marked.defaults, opt);
+  return marked;
+};
+
+marked.defaults = {
+  gfm: true,
+  tables: true,
+  breaks: false,
+  pedantic: false,
+  sanitize: false,
+  smartLists: false,
+  silent: false,
+  highlight: null,
+  langPrefix: 'lang-',
+  smartypants: false,
+  headerPrefix: '',
+  renderer: new Renderer,
+  xhtml: false
+};
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+  module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+  define(function() { return marked; });
+} else {
+  this.marked = marked;
+}
+
+}).call(function() {
+  return this || (typeof window !== 'undefined' ? window : global);
+}());
\ No newline at end of file
diff --git a/profiles/killbill/src/main/webapp/lib/object-assign-pollyfill.js b/profiles/killbill/src/main/webapp/lib/object-assign-pollyfill.js
new file mode 100644
index 0000000..5179920
--- /dev/null
+++ b/profiles/killbill/src/main/webapp/lib/object-assign-pollyfill.js
@@ -0,0 +1,23 @@
+if (typeof Object.assign != 'function') {
+  (function () {
+    Object.assign = function (target) {
+      'use strict';
+      if (target === undefined || target === null) {
+        throw new TypeError('Cannot convert undefined or null to object');
+      }
+
+      var output = Object(target);
+      for (var index = 1; index < arguments.length; index++) {
+        var source = arguments[index];
+        if (source !== undefined && source !== null) {
+          for (var nextKey in source) {
+            if (Object.prototype.hasOwnProperty.call(source, nextKey)) {
+              output[nextKey] = source[nextKey];
+            }
+          }
+        }
+      }
+      return output;
+    };
+  })();
+}
diff --git a/profiles/killbill/src/main/webapp/lib/swagger-oauth.js b/profiles/killbill/src/main/webapp/lib/swagger-oauth.js
index 8bb17fb..a35bda3 100644
--- a/profiles/killbill/src/main/webapp/lib/swagger-oauth.js
+++ b/profiles/killbill/src/main/webapp/lib/swagger-oauth.js
@@ -3,14 +3,37 @@ var popupMask;
 var popupDialog;
 var clientId;
 var realm;
+var redirect_uri;
+var clientSecret;
+var scopeSeparator;
+var additionalQueryStringParams;
 
 function handleLogin() {
   var scopes = [];
 
-  if(window.swaggerUi.api.authSchemes 
-    && window.swaggerUi.api.authSchemes.oauth2
-    && window.swaggerUi.api.authSchemes.oauth2.scopes) {
-    scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
+  var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
+  if(auths) {
+    var key;
+    var defs = auths;
+    for(key in defs) {
+      var auth = defs[key];
+      if(auth.type === 'oauth2' && auth.scopes) {
+        var scope;
+        if(Array.isArray(auth.scopes)) {
+          // 1.2 support
+          var i;
+          for(i = 0; i < auth.scopes.length; i++) {
+            scopes.push(auth.scopes[i]);
+          }
+        }
+        else {
+          // 2.0 support
+          for(scope in auth.scopes) {
+            scopes.push({scope: scope, description: auth.scopes[scope], OAuthSchemeKey: key});
+          }
+        }
+      }
+    }
   }
 
   if(window.swaggerUi.api
@@ -18,36 +41,37 @@ function handleLogin() {
     appName = window.swaggerUi.api.info.title;
   }
 
-  if(popupDialog.length > 0)
-    popupDialog = popupDialog.last();
-  else {
-    popupDialog = $(
-      [
-        '<div class="api-popup-dialog">',
-        '<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
-        '<div class="api-popup-content">',
-          '<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
-            '<a href="#">Learn how to use</a>',
-          '</p>',
-          '<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
-          '<ul class="api-popup-scopes">',
-          '</ul>',
-          '<p class="error-msg"></p>',
-          '<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
-        '</div>',
-        '</div>'].join(''));
-    $(document.body).append(popupDialog);
-
-    popup = popupDialog.find('ul.api-popup-scopes').empty();
-    for (i = 0; i < scopes.length; i ++) {
-      scope = scopes[i];
-      str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
-      if (scope.description) {
-        str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
-      }
-      str += '</label></li>';
-      popup.append(str);
+  $('.api-popup-dialog').remove(); 
+  popupDialog = $(
+    [
+      '<div class="api-popup-dialog">',
+      '<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
+      '<div class="api-popup-content">',
+        '<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
+          '<a href="#">Learn how to use</a>',
+        '</p>',
+        '<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
+        '<ul class="api-popup-scopes">',
+        '</ul>',
+        '<p class="error-msg"></p>',
+        '<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
+      '</div>',
+      '</div>'].join(''));
+  $(document.body).append(popupDialog);
+
+  //TODO: only display applicable scopes (will need to pass them into handleLogin)
+  popup = popupDialog.find('ul.api-popup-scopes').empty();
+  for (i = 0; i < scopes.length; i ++) {
+    scope = scopes[i];
+    str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"' +'" oauthtype="' + scope.OAuthSchemeKey +'"/>' + '<label for="scope_' + i + '">' + scope.scope ;
+    if (scope.description) {
+      if ($.map(auths, function(n, i) { return i; }).length > 1) //if we have more than one scheme, display schemes
+	    str += '<br/><span class="api-scope-desc">' + scope.description + ' ('+ scope.OAuthSchemeKey+')' +'</span>';
+	  else
+	    str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
     }
+    str += '</label></li>';
+    popup.append(str);
   }
 
   var $win = $(window),
@@ -67,7 +91,11 @@ function handleLogin() {
   popupDialog.find('button.api-popup-cancel').click(function() {
     popupMask.hide();
     popupDialog.hide();
+    popupDialog.empty();
+    popupDialog = [];
   });
+
+  $('button.api-popup-authbtn').unbind();
   popupDialog.find('button.api-popup-authbtn').click(function() {
     popupMask.hide();
     popupDialog.hide();
@@ -75,34 +103,74 @@ function handleLogin() {
     var authSchemes = window.swaggerUi.api.authSchemes;
     var host = window.location;
     var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
-    var redirectUrl = host.protocol + '//' + host.host + pathname + "/o2c.html";
+    var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
+    var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
     var url = null;
+    var scopes = []
+    var o = popup.find('input:checked'); 
+    var OAuthSchemeKeys = [];
+    var state;
+    for(k =0; k < o.length; k++) {
+      var scope = $(o[k]).attr('scope');
+      if (scopes.indexOf(scope) === -1)
+        scopes.push(scope);
+      var OAuthSchemeKey = $(o[k]).attr('oauthtype');      
+      if (OAuthSchemeKeys.indexOf(OAuthSchemeKey) === -1)
+          OAuthSchemeKeys.push(OAuthSchemeKey);
+    }
+    
+    //TODO: merge not replace if scheme is different from any existing 
+    //(needs to be aware of schemes to do so correctly)
+    window.enabledScopes=scopes;    
+    
+    for (var key in authSchemes) { 
+      if (authSchemes.hasOwnProperty(key) && OAuthSchemeKeys.indexOf(key) != -1) { //only look at keys that match this scope.
+        var flow = authSchemes[key].flow;
 
-    for (var key in authSchemes) {
-      if (authSchemes.hasOwnProperty(key)) {
-        var o = authSchemes[key].grantTypes;
-        for(var t in o) {
-          if(o.hasOwnProperty(t) && t === 'implicit') {
-            var dets = o[t];
-            url = dets.loginEndpoint.url + "?response_type=token";
-            window.swaggerUi.tokenName = dets.tokenName;
+        if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
+          var dets = authSchemes[key];
+          url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
+          window.swaggerUi.tokenName = dets.tokenName || 'access_token';
+          window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
+          state = key;
+        }
+        else if(authSchemes[key].type === 'oauth2' && flow && (flow === 'application')) {
+            var dets = authSchemes[key];
+            window.swaggerUi.tokenName = dets.tokenName || 'access_token';
+            clientCredentialsFlow(scopes, dets.tokenUrl, key);
+            return;
+        }        
+        else if(authSchemes[key].grantTypes) {
+          // 1.2 support
+          var o = authSchemes[key].grantTypes;
+          for(var t in o) {
+            if(o.hasOwnProperty(t) && t === 'implicit') {
+              var dets = o[t];
+              var ep = dets.loginEndpoint.url;
+              url = dets.loginEndpoint.url + '?response_type=token';
+              window.swaggerUi.tokenName = dets.tokenName;
+            }
+            else if (o.hasOwnProperty(t) && t === 'accessCode') {
+              var dets = o[t];
+              var ep = dets.tokenRequestEndpoint.url;
+              url = dets.tokenRequestEndpoint.url + '?response_type=code';
+              window.swaggerUi.tokenName = dets.tokenName;
+            }
           }
         }
       }
     }
-    var scopes = []
-    var o = $('.api-popup-scopes').find('input:checked');
 
-    for(k =0; k < o.length; k++) {
-      scopes.push($(o[k]).attr("scope"));
-    }
-
-    window.enabledScopes=scopes;
+    redirect_uri = redirectUrl;
 
     url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
     url += '&realm=' + encodeURIComponent(realm);
     url += '&client_id=' + encodeURIComponent(clientId);
-    url += '&scope=' + encodeURIComponent(scopes);
+    url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
+    url += '&state=' + encodeURIComponent(state);
+    for (var key in additionalQueryStringParams) {
+        url += '&' + key + '=' + encodeURIComponent(additionalQueryStringParams[key]);
+    }
 
     window.open(url);
   });
@@ -114,8 +182,8 @@ function handleLogin() {
 
 
 function handleLogout() {
-  for(key in window.authorizations.authz){
-    window.authorizations.remove(key)
+  for(key in window.swaggerUi.api.clientAuthorizations.authz){
+    window.swaggerUi.api.clientAuthorizations.remove(key)
   }
   window.enabledScopes = null;
   $('.api-ic.ic-on').addClass('ic-off');
@@ -130,18 +198,22 @@ function initOAuth(opts) {
   var o = (opts||{});
   var errors = [];
 
-  appName = (o.appName||errors.push("missing appName"));
+  appName = (o.appName||errors.push('missing appName'));
   popupMask = (o.popupMask||$('#api-common-mask'));
   popupDialog = (o.popupDialog||$('.api-popup-dialog'));
-  clientId = (o.clientId||errors.push("missing client id"));
-  realm = (o.realm||errors.push("missing realm"));
+  clientId = (o.clientId||errors.push('missing client id'));
+  clientSecret = (o.clientSecret||null);
+  realm = (o.realm||errors.push('missing realm'));
+  scopeSeparator = (o.scopeSeparator||' ');
+  additionalQueryStringParams = (o.additionalQueryStringParams||{});
 
   if(errors.length > 0){
-    log("auth unable initialize oauth: " + errors);
+    log('auth unable initialize oauth: ' + errors);
     return;
   }
 
   $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
+  $('.api-ic').unbind();
   $('.api-ic').click(function(s) {
     if($(s.target).hasClass('ic-off'))
       handleLogin();
@@ -152,7 +224,68 @@ function initOAuth(opts) {
   });
 }
 
-function onOAuthComplete(token) {
+function clientCredentialsFlow(scopes, tokenUrl, OAuthSchemeKey) {
+    var params = {
+      'client_id': clientId,
+      'client_secret': clientSecret,
+      'scope': scopes.join(' '),
+      'grant_type': 'client_credentials'
+    }
+    $.ajax(
+    {
+      url : tokenUrl,
+      type: "POST",
+      data: params,
+      success:function(data, textStatus, jqXHR)
+      {
+        onOAuthComplete(data,OAuthSchemeKey);
+      },
+      error: function(jqXHR, textStatus, errorThrown)
+      {
+        onOAuthComplete("");
+      }
+    });
+    
+  }
+
+window.processOAuthCode = function processOAuthCode(data) {
+  var OAuthSchemeKey = data.state;
+
+  // redirect_uri is required in auth code flow 
+  // see https://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1.3
+  var host = window.location;
+  var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
+  var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
+  var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
+
+  var params = {
+    'client_id': clientId,
+    'code': data.code,
+    'grant_type': 'authorization_code',
+    'redirect_uri': redirectUrl
+  };
+
+  if (clientSecret) {
+    params.client_secret = clientSecret;
+  }
+
+  $.ajax(
+  {
+    url : window.swaggerUi.tokenUrl,
+    type: "POST",
+    data: params,
+    success:function(data, textStatus, jqXHR)
+    {
+      onOAuthComplete(data, OAuthSchemeKey);
+    },
+    error: function(jqXHR, textStatus, errorThrown)
+    {
+      onOAuthComplete("");
+    }
+  });
+};
+
+window.onOAuthComplete = function onOAuthComplete(token,OAuthSchemeKey) {
   if(token) {
     if(token.error) {
       var checkbox = $('input[type=checkbox],.secured')
@@ -162,11 +295,14 @@ function onOAuthComplete(token) {
       alert(token.error);
     }
     else {
-      var b = token[window.swaggerUi.tokenName];
+      var b = token[window.swaggerUi.tokenName];      
+      if (!OAuthSchemeKey){
+          OAuthSchemeKey = token.state;
+      }
       if(b){
         // if all roles are satisfied
         var o = null;
-        $.each($('.auth #api_information_panel'), function(k, v) {
+        $.each($('.auth .api-ic .api_information_panel'), function(k, v) { 
           var children = v;
           if(children && children.childNodes) {
             var requiredScopes = [];
@@ -183,7 +319,7 @@ function onOAuthComplete(token) {
               }
             }
             if(diff.length > 0){
-              o = v.parentNode;
+              o = v.parentNode.parentNode;
               $(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
               $(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
 
@@ -192,20 +328,20 @@ function onOAuthComplete(token) {
               $(o).find('.api-ic').removeClass('ic-error');
             }
             else {
-              o = v.parentNode;
+              o = v.parentNode.parentNode;
               $(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
               $(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
 
               // all scopes are satisfied
               $(o).find('.api-ic').addClass('ic-info');
               $(o).find('.api-ic').removeClass('ic-warning');
-              $(o).find('.api-ic').removeClass('ic-error');          
+              $(o).find('.api-ic').removeClass('ic-error');
             }
           }
         });
-
-        window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
+        window.swaggerUi.api.clientAuthorizations.add(window.OAuthSchemeKey, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
+        window.swaggerUi.load();
       }
     }
   }
-}
\ No newline at end of file
+};
diff --git a/profiles/killbill/src/main/webapp/lib/swagger-ui.min.js b/profiles/killbill/src/main/webapp/lib/swagger-ui.min.js
index 0d60523..d5ed02d 100644
--- a/profiles/killbill/src/main/webapp/lib/swagger-ui.min.js
+++ b/profiles/killbill/src/main/webapp/lib/swagger-ui.min.js
@@ -1 +1,10 @@
-$(function(){$.fn.vAlign=function(){return this.each(function(c){var a=$(this).height();var d=$(this).parent().height();var b=(d-a)/2;$(this).css("margin-top",b)})};$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(b){var d=$(this).closest("form").innerWidth();var c=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10);var a=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",d-c-a)})};$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent();$("ul.downplayed li div.content p").vAlign();$("form.sandbox").submit(function(){var a=true;$(this).find("input.required").each(function(){$(this).removeClass("error");if($(this).val()==""){$(this).addClass("error");$(this).wiggle();a=false}});return a})});function clippyCopiedCallback(b){$("#api_key_copied").fadeIn().delay(1000).fadeOut()}log=function(){log.history=log.history||[];log.history.push(arguments);if(this.console){console.log(Array.prototype.slice.call(arguments)[0])}};if(Function.prototype.bind&&console&&typeof console.log=="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(a){console[a]=this.bind(console[a],console)},Function.prototype.call)}var Docs={shebang:function(){var b=$.param.fragment().split("/");b.shift();switch(b.length){case 1:var d="resource_"+b[0];Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});break;case 2:Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});var c=b.join("_");var a=c+"_content";Docs.expandOperation($("#"+a));$("#"+c).slideto({highlight:false});break}},toggleEndpointListForResource:function(b){var a=$("li#resource_"+Docs.escapeResourceName(b)+" ul.endpoints");if(a.is(":visible")){Docs.collapseEndpointListForResource(b)}else{Docs.expandEndpointListForResource(b)}},expandEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);if(b==""){$(".resource ul.endpoints").slideDown();return}$("li#resource_"+b).addClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideDown()},collapseEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);$("li#resource_"+b).removeClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideUp()},expandOperationsForResource:function(a){Docs.expandEndpointListForResource(a);if(a==""){$(".resource ul.endpoints li.operation div.content").slideDown();return}$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(a){Docs.expandEndpointListForResource(a);$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(a){return a.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(a){a.slideDown()},collapseOperation:function(a){a.slideUp()}};(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.produces,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n	<option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n	";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="contentType"></label>\n<select name="contentType">\n';c=f["if"].call(l,l.produces,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.main=b(function(h,n,g,m,l){this.compilerInfo=[4,">= 1.0.0"];g=this.merge(g,h.helpers);l=l||{};var j="",c,s,i="function",k=this.escapeExpression,q=this;function f(x,w){var t="",v,u;t+='\n  <div class="info_title">'+k(((v=((v=x.info),v==null||v===false?v:v.title)),typeof v===i?v.apply(x):v))+'</div>\n  <div class="info_description">';u=((v=((v=x.info),v==null||v===false?v:v.description)),typeof v===i?v.apply(x):v);if(u||u===0){t+=u}t+="</div>\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.termsOfServiceUrl),{hash:{},inverse:q.noop,fn:q.program(2,d,w),data:w});if(u||u===0){t+=u}t+="\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.contact),{hash:{},inverse:q.noop,fn:q.program(4,r,w),data:w});if(u||u===0){t+=u}t+="\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.license),{hash:{},inverse:q.noop,fn:q.program(6,p,w),data:w});if(u||u===0){t+=u}t+="\n  ";return t}function d(w,v){var t="",u;t+='<div class="info_tos"><a href="'+k(((u=((u=w.info),u==null||u===false?u:u.termsOfServiceUrl)),typeof u===i?u.apply(w):u))+'">Terms of service</a></div>';return t}function r(w,v){var t="",u;t+="<div class='info_contact'><a href=\"mailto:"+k(((u=((u=((u=w.info),u==null||u===false?u:u.contact)),u==null||u===false?u:u.name)),typeof u===i?u.apply(w):u))+'">Contact the developer</a></div>';return t}function p(w,v){var t="",u;t+="<div class='info_license'><a href='"+k(((u=((u=((u=w.info),u==null||u===false?u:u.license)),u==null||u===false?u:u.url)),typeof u===i?u.apply(w):u))+"'>"+k(((u=((u=((u=w.info),u==null||u===false?u:u.license)),u==null||u===false?u:u.name)),typeof u===i?u.apply(w):u))+"</a></div>";return t}function o(w,v){var t="",u;t+='\n    , <span style="font-variant: small-caps">api version</span>: '+k(((u=((u=w.info),u==null||u===false?u:u.version)),typeof u===i?u.apply(w):u))+"\n    ";return t}function e(w,v){var t="",u;t+='\n    <span style="float:right"><a href="http://online.swagger.io/validator/debug?url=';if(u=g.url){u=u.call(w,{hash:{},data:v})}else{u=w.url;u=typeof u===i?u.apply(w):u}t+=k(u)+'"><img id="validator" src="http://online.swagger.io/validator?url=';if(u=g.url){u=u.call(w,{hash:{},data:v})}else{u=w.url;u=typeof u===i?u.apply(w):u}t+=k(u)+'"></a>\n    </span>\n    ';return t}j+="<div class='info' id='api_info'>\n  ";c=g["if"].call(n,n.info,{hash:{},inverse:q.noop,fn:q.program(1,f,l),data:l});if(c||c===0){j+=c}j+="\n</div>\n<div class='container' id='resources_container'>\n  <ul id='resources'>\n  </ul>\n\n  <div class=\"footer\">\n    <br>\n    <br>\n    <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: ";if(c=g.basePath){c=c.call(n,{hash:{},data:l})}else{c=n.basePath;c=typeof c===i?c.apply(n):c}j+=k(c)+"\n    ";s=g["if"].call(n,((c=n.info),c==null||c===false?c:c.version),{hash:{},inverse:q.noop,fn:q.program(8,o,l),data:l});if(s||s===0){j+=s}j+="]\n    ";s=g["if"].call(n,n.validatorUrl,{hash:{},inverse:q.noop,fn:q.program(10,e,l),data:l});if(s||s===0){j+=s}j+="\n    </h4>\n    </div>\n</div>\n";return j})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.operation=b(function(g,s,q,m,y){this.compilerInfo=[4,">= 1.0.0"];q=this.merge(q,g.helpers);y=y||{};var r="",i,f,e="function",d=this.escapeExpression,p=this,c=q.blockHelperMissing;function o(C,B){var z="",A;z+="\n        <h4>Implementation Notes</h4>\n        <p>";if(A=q.description){A=A.call(C,{hash:{},data:B})}else{A=C.description;A=typeof A===e?A.apply(C):A}if(A||A===0){z+=A}z+="</p>\n        ";return z}function n(A,z){return'\n        <div class="auth">\n        <span class="api-ic ic-error"></span>'}function l(C,B){var z="",A;z+='\n          <div id="api_information_panel" style="top: 526px; left: 776px; display: none;">\n          ';A=q.each.call(C,C,{hash:{},inverse:p.noop,fn:p.program(6,k,B),data:B});if(A||A===0){z+=A}z+="\n          </div>\n        ";return z}function k(D,C){var z="",B,A;z+="\n            <div title='";A=((B=D.description),typeof B===e?B.apply(D):B);if(A||A===0){z+=A}z+="'>"+d(((B=D.scope),typeof B===e?B.apply(D):B))+"</div>\n          ";return z}function h(A,z){return"</div>"}function x(A,z){return'\n        <div class=\'access\'>\n          <span class="api-ic ic-off" title="click to authenticate"></span>\n        </div>\n        '}function w(A,z){return'\n          <h4>Response Class</h4>\n          <p><span class="model-signature" /></p>\n          <br/>\n          <div class="response-content-type" />\n        '}function v(A,z){return'\n          <h4>Parameters</h4>\n          <table class=\'fullwidth\'>\n          <thead>\n            <tr>\n            <th style="width: 100px; max-width: 100px">Parameter</th>\n            <th style="width: 310px; max-width: 310px">Value</th>\n            <th style="width: 200px; max-width: 200px">Description</th>\n            <th style="width: 100px; max-width: 100px">Parameter Type</th>\n            <th style="width: 220px; max-width: 230px">Data Type</th>\n            </tr>\n          </thead>\n          <tbody class="operation-params">\n\n          </tbody>\n          </table>\n          '}function u(A,z){return"\n          <div style='margin:0;padding:0;display:inline'></div>\n          <h4>Response Messages</h4>\n          <table class='fullwidth'>\n            <thead>\n            <tr>\n              <th>HTTP Status Code</th>\n              <th>Reason</th>\n              <th>Response Model</th>\n            </tr>\n            </thead>\n            <tbody class=\"operation-status\">\n            \n            </tbody>\n          </table>\n          "}function t(A,z){return"\n          "}function j(A,z){return"\n          <div class='sandbox_header'>\n            <input class='submit' name='commit' type='button' value='Try it out!' />\n            <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n            <span class='response_throbber' style='display:none'></span>\n          </div>\n          "}r+="\n  <ul class='operations' >\n    <li class='";if(i=q.method){i=i.call(s,{hash:{},data:y})}else{i=s.method;i=typeof i===e?i.apply(s):i}r+=d(i)+" operation' id='";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"_";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+"'>\n      <div class='heading'>\n        <h3>\n          <span class='http_method'>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.method){i=i.call(s,{hash:{},data:y})}else{i=s.method;i=typeof i===e?i.apply(s):i}r+=d(i)+"</a>\n          </span>\n          <span class='path'>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.path){i=i.call(s,{hash:{},data:y})}else{i=s.path;i=typeof i===e?i.apply(s):i}r+=d(i)+"</a>\n          </span>\n        </h3>\n        <ul class='options'>\n          <li>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.summary){i=i.call(s,{hash:{},data:y})}else{i=s.summary;i=typeof i===e?i.apply(s):i}if(i||i===0){r+=i}r+="</a>\n          </li>\n        </ul>\n      </div>\n      <div class='content' id='";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"_";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+"_content' style='display:none'>\n        ";i=q["if"].call(s,s.description,{hash:{},inverse:p.noop,fn:p.program(1,o,y),data:y});if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(3,n,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";i=q.each.call(s,s.oauth,{hash:{},inverse:p.noop,fn:p.program(5,l,y),data:y});if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(8,h,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(10,x,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";i=q["if"].call(s,s.type,{hash:{},inverse:p.noop,fn:p.program(12,w,y),data:y});if(i||i===0){r+=i}r+="\n        <form accept-charset='UTF-8' class='sandbox'>\n          <div style='margin:0;padding:0;display:inline'></div>\n          ";i=q["if"].call(s,s.parameters,{hash:{},inverse:p.noop,fn:p.program(14,v,y),data:y});if(i||i===0){r+=i}r+="\n          ";i=q["if"].call(s,s.responseMessages,{hash:{},inverse:p.noop,fn:p.program(16,u,y),data:y});if(i||i===0){r+=i}r+="\n          ";i=q["if"].call(s,s.isReadOnly,{hash:{},inverse:p.program(20,j,y),fn:p.program(18,t,y),data:y});if(i||i===0){r+=i}r+="\n        </form>\n        <div class='response' style='display:none'>\n          <h4>Request URL</h4>\n          <div class='block request_url'></div>\n          <h4>Response Body</h4>\n          <div class='block response_body'></div>\n          <h4>Response Code</h4>\n          <div class='block response_code'></div>\n          <h4>Response Headers</h4>\n          <div class='block response_headers'></div>\n        </div>\n      </div>\n    </li>\n  </ul>\n";return r})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param=b(function(f,q,o,j,t){this.compilerInfo=[4,">= 1.0.0"];o=this.merge(o,f.helpers);t=t||{};var p="",g,d="function",c=this.escapeExpression,n=this;function m(y,x){var v="",w;v+="\n		";w=o["if"].call(y,y.isFile,{hash:{},inverse:n.program(4,k,x),fn:n.program(2,l,x),data:x});if(w||w===0){v+=w}v+="\n	";return v}function l(y,x){var v="",w;v+='\n			<input type="file" name=\'';if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+'\'/>\n			<div class="parameter-content-type" />\n		';return v}function k(y,x){var v="",w;v+="\n			";w=o["if"].call(y,y["default"],{hash:{},inverse:n.program(7,h,x),fn:n.program(5,i,x),data:x});if(w||w===0){v+=w}v+="\n		";return v}function i(y,x){var v="",w;v+="\n				<textarea class='body-textarea' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"'>";if(w=o["default"]){w=w.call(y,{hash:{},data:x})}else{w=y["default"];w=typeof w===d?w.apply(y):w}v+=c(w)+"</textarea>\n			";return v}function h(y,x){var v="",w;v+="\n				<textarea class='body-textarea' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+'\'></textarea>\n				<br />\n				<div class="parameter-content-type" />\n			';return v}function e(y,x){var v="",w;v+="\n		";w=o["if"].call(y,y.isFile,{hash:{},inverse:n.program(10,u,x),fn:n.program(2,l,x),data:x});if(w||w===0){v+=w}v+="\n	";return v}function u(y,x){var v="",w;v+="\n			";w=o["if"].call(y,y["default"],{hash:{},inverse:n.program(13,r,x),fn:n.program(11,s,x),data:x});if(w||w===0){v+=w}v+="\n		";return v}function s(y,x){var v="",w;v+="\n				<input class='parameter' minlength='0' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"' placeholder='' type='text' value='";if(w=o["default"]){w=w.call(y,{hash:{},data:x})}else{w=y["default"];w=typeof w===d?w.apply(y):w}v+=c(w)+"'/>\n			";return v}function r(y,x){var v="",w;v+="\n				<input class='parameter' minlength='0' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"' placeholder='' type='text' value=''/>\n			";return v}p+="<td class='code'>";if(g=o.name){g=g.call(q,{hash:{},data:t})}else{g=q.name;g=typeof g===d?g.apply(q):g}p+=c(g)+"</td>\n<td>\n\n	";g=o["if"].call(q,q.isBody,{hash:{},inverse:n.program(9,e,t),fn:n.program(1,m,t),data:t});if(g||g===0){p+=g}p+="\n\n</td>\n<td>";if(g=o.description){g=g.call(q,{hash:{},data:t})}else{g=q.description;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+="</td>\n<td>";if(g=o.paramType){g=g.call(q,{hash:{},data:t})}else{g=q.paramType;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+='</td>\n<td>\n	<span class="model-signature"></span>\n</td>\n';return p})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_list=b(function(h,t,r,m,y){this.compilerInfo=[4,">= 1.0.0"];r=this.merge(r,h.helpers);y=y||{};var s="",j,g,e,p=this,q=r.helperMissing,d="function",c=this.escapeExpression;function o(A,z){return" multiple='multiple'"}function n(A,z){return"\n    "}function l(C,B){var z="",A;z+="\n      ";A=r["if"].call(C,C.defaultValue,{hash:{},inverse:p.program(8,i,B),fn:p.program(6,k,B),data:B});if(A||A===0){z+=A}z+="\n    ";return z}function k(A,z){return"\n      "}function i(E,D){var z="",C,B,A;z+="\n        ";A={hash:{},inverse:p.program(11,x,D),fn:p.program(9,f,D),data:D};B=((C=r.isArray||E.isArray),C?C.call(E,E,A):q.call(E,"isArray",E,A));if(B||B===0){z+=B}z+="\n      ";return z}function f(A,z){return"\n        "}function x(A,z){return"\n          <option selected=\"\" value=''></option>\n        "}function w(C,B){var z="",A;z+="\n      ";A=r["if"].call(C,C.isDefault,{hash:{},inverse:p.program(16,u,B),fn:p.program(14,v,B),data:B});if(A||A===0){z+=A}z+="\n    ";return z}function v(C,B){var z="",A;z+='\n        <option selected="" value=\'';if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"'>";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+" (default)</option>\n      ";return z}function u(C,B){var z="",A;z+="\n        <option value='";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"'>";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"</option>\n      ";return z}s+="<td class='code'>";if(j=r.name){j=j.call(t,{hash:{},data:y})}else{j=t.name;j=typeof j===d?j.apply(t):j}s+=c(j)+"</td>\n<td>\n  <select ";e={hash:{},inverse:p.noop,fn:p.program(1,o,y),data:y};g=((j=r.isArray||t.isArray),j?j.call(t,t,e):q.call(t,"isArray",t,e));if(g||g===0){s+=g}s+=" class='parameter' name='";if(g=r.name){g=g.call(t,{hash:{},data:y})}else{g=t.name;g=typeof g===d?g.apply(t):g}s+=c(g)+"'>\n    ";g=r["if"].call(t,t.required,{hash:{},inverse:p.program(5,l,y),fn:p.program(3,n,y),data:y});if(g||g===0){s+=g}s+="\n    ";g=r.each.call(t,((j=t.allowableValues),j==null||j===false?j:j.descriptiveValues),{hash:{},inverse:p.noop,fn:p.program(13,w,y),data:y});if(g||g===0){s+=g}s+="\n  </select>\n</td>\n<td>";if(g=r.description){g=g.call(t,{hash:{},data:y})}else{g=t.description;g=typeof g===d?g.apply(t):g}if(g||g===0){s+=g}s+="</td>\n<td>";if(g=r.paramType){g=g.call(t,{hash:{},data:y})}else{g=t.paramType;g=typeof g===d?g.apply(t):g}if(g||g===0){s+=g}s+='</td>\n<td><span class="model-signature"></span></td>';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r;q+="\n        <textarea class='body-textarea' readonly='readonly' name='";if(r=f.name){r=r.call(t,{hash:{},data:s})}else{r=t.name;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"</textarea>\n    ";return q}function c(t,s){var q="",r;q+="\n        ";r=f["if"].call(t,t.defaultValue,{hash:{},inverse:o.program(6,n,s),fn:o.program(4,p,s),data:s});if(r||r===0){q+=r}q+="\n    ";return q}function p(t,s){var q="",r;q+="\n            ";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"\n        ";return q}function n(r,q){return"\n            (empty)\n        "}i+="<td class='code'>";if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</td>\n<td>\n    ";d=f["if"].call(m,m.isBody,{hash:{},inverse:o.program(3,c,k),fn:o.program(1,e,k),data:k});if(d||d===0){i+=d}i+="\n</td>\n<td>";if(d=f.description){d=d.call(m,{hash:{},data:k})}else{d=m.description;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="</td>\n<td>";if(d=f.paramType){d=d.call(m,{hash:{},data:k})}else{d=m.paramType;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+='</td>\n<td><span class="model-signature"></span></td>\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly_required=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r;q+="\n        <textarea class='body-textarea'  readonly='readonly' placeholder='(required)' name='";if(r=f.name){r=r.call(t,{hash:{},data:s})}else{r=t.name;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"</textarea>\n    ";return q}function c(t,s){var q="",r;q+="\n        ";r=f["if"].call(t,t.defaultValue,{hash:{},inverse:o.program(6,n,s),fn:o.program(4,p,s),data:s});if(r||r===0){q+=r}q+="\n    ";return q}function p(t,s){var q="",r;q+="\n            ";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"\n        ";return q}function n(r,q){return"\n            (empty)\n        "}i+="<td class='code required'>";if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</td>\n<td>\n    ";d=f["if"].call(m,m.isBody,{hash:{},inverse:o.program(3,c,k),fn:o.program(1,e,k),data:k});if(d||d===0){i+=d}i+="\n</td>\n<td>";if(d=f.description){d=d.call(m,{hash:{},data:k})}else{d=m.description;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="</td>\n<td>";if(d=f.paramType){d=d.call(m,{hash:{},data:k})}else{d=m.paramType;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+='</td>\n<td><span class="model-signature"></span></td>\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_required=b(function(f,q,o,j,u){this.compilerInfo=[4,">= 1.0.0"];o=this.merge(o,f.helpers);u=u||{};var p="",g,d="function",c=this.escapeExpression,n=this;function m(z,y){var w="",x;w+="\n		";x=o["if"].call(z,z.isFile,{hash:{},inverse:n.program(4,k,y),fn:n.program(2,l,y),data:y});if(x||x===0){w+=x}w+="\n	";return w}function l(z,y){var w="",x;w+='\n			<input type="file" name=\'';if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n		";return w}function k(z,y){var w="",x;w+="\n			";x=o["if"].call(z,z.defaultValue,{hash:{},inverse:n.program(7,h,y),fn:n.program(5,i,y),data:y});if(x||x===0){w+=x}w+="\n		";return w}function i(z,y){var w="",x;w+="\n				<textarea class='body-textarea' placeholder='(required)' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'>";if(x=o.defaultValue){x=x.call(z,{hash:{},data:y})}else{x=z.defaultValue;x=typeof x===d?x.apply(z):x}w+=c(x)+"</textarea>\n			";return w}function h(z,y){var w="",x;w+="\n				<textarea class='body-textarea' placeholder='(required)' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+'\'></textarea>\n				<br />\n				<div class="parameter-content-type" />\n			';return w}function e(z,y){var w="",x;w+="\n		";x=o["if"].call(z,z.isFile,{hash:{},inverse:n.program(12,t,y),fn:n.program(10,v,y),data:y});if(x||x===0){w+=x}w+="\n	";return w}function v(z,y){var w="",x;w+="\n			<input class='parameter' class='required' type='file' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n		";return w}function t(z,y){var w="",x;w+="\n			";x=o["if"].call(z,z.defaultValue,{hash:{},inverse:n.program(15,r,y),fn:n.program(13,s,y),data:y});if(x||x===0){w+=x}w+="\n		";return w}function s(z,y){var w="",x;w+="\n				<input class='parameter required' minlength='1' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"' placeholder='(required)' type='text' value='";if(x=o.defaultValue){x=x.call(z,{hash:{},data:y})}else{x=z.defaultValue;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n			";return w}function r(z,y){var w="",x;w+="\n				<input class='parameter required' minlength='1' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"' placeholder='(required)' type='text' value=''/>\n			";return w}p+="<td class='code required'>";if(g=o.name){g=g.call(q,{hash:{},data:u})}else{g=q.name;g=typeof g===d?g.apply(q):g}p+=c(g)+"</td>\n<td>\n	";g=o["if"].call(q,q.isBody,{hash:{},inverse:n.program(9,e,u),fn:n.program(1,m,u),data:u});if(g||g===0){p+=g}p+="\n</td>\n<td>\n	<strong>";if(g=o.description){g=g.call(q,{hash:{},data:u})}else{g=q.description;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+="</strong>\n</td>\n<td>";if(g=o.paramType){g=g.call(q,{hash:{},data:u})}else{g=q.paramType;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+='</td>\n<td><span class="model-signature"></span></td>\n';return p})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.parameter_content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.consumes,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n  <option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n  ";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="parameterContentType"></label>\n<select name="parameterContentType">\n';c=f["if"].call(l,l.consumes,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.resource=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,p,h="function",j=this.escapeExpression,o=this,n=f.blockHelperMissing;function e(r,q){return" : "}function c(t,s){var q="",r;q+="<li>\n      <a href='";if(r=f.url){r=r.call(t,{hash:{},data:s})}else{r=t.url;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>Raw</a>\n    </li>";return q}i+="<div class='heading'>\n  <h2>\n    <a href='#!/";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'\' class="toggleEndpointList" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">';if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</a> ";p={hash:{},inverse:o.noop,fn:o.program(1,e,k),data:k};if(d=f.summary){d=d.call(m,p)}else{d=m.summary;d=typeof d===h?d.apply(m):d}if(!f.summary){d=n.call(m,d,p)}if(d||d===0){i+=d}if(d=f.summary){d=d.call(m,{hash:{},data:k})}else{d=m.summary;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="\n  </h2>\n  <ul class='options'>\n    <li>\n      <a href='#!/";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+"' id='endpointListTogger_";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'\' class="toggleEndpointList" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">Show/Hide</a>\n    </li>\n    <li>\n      <a href=\'#\' class="collapseResource" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href=\'#\' class="expandResource" data-id=';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+">\n        Expand Operations\n      </a>\n    </li>\n    ";p={hash:{},inverse:o.noop,fn:o.program(3,c,k),data:k};if(d=f.url){d=d.call(m,p)}else{d=m.url;d=typeof d===h?d.apply(m):d}if(!f.url){d=n.call(m,d,p)}if(d||d===0){i+=d}i+="\n  </ul>\n</div>\n<ul class='endpoints' id='";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+"_endpoint_list' style='display:none'>\n\n</ul>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.response_content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.produces,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n  <option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n  ";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="responseContentType"></label>\n<select name="responseContentType">\n';c=f["if"].call(l,l.produces,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.signature=b(function(e,k,d,j,i){this.compilerInfo=[4,">= 1.0.0"];d=this.merge(d,e.helpers);i=i||{};var g="",c,f="function",h=this.escapeExpression;g+='<div>\n<ul class="signature-nav">\n  <li><a class="description-link" href="#">Model</a></li>\n  <li><a class="snippet-link" href="#">Model Schema</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n  <div class="description">\n    ';if(c=d.signature){c=c.call(k,{hash:{},data:i})}else{c=k.signature;c=typeof c===f?c.apply(k):c}if(c||c===0){g+=c}g+='\n  </div>\n\n  <div class="snippet">\n    <pre><code>';if(c=d.sampleJSON){c=c.call(k,{hash:{},data:i})}else{c=k.sampleJSON;c=typeof c===f?c.apply(k):c}g+=h(c)+'</code></pre>\n    <small class="notice"></small>\n  </div>\n</div>\n\n';return g})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.status_code=b(function(e,k,d,j,i){this.compilerInfo=[4,">= 1.0.0"];d=this.merge(d,e.helpers);i=i||{};var g="",c,f="function",h=this.escapeExpression;g+="<td width='15%' class='code'>";if(c=d.code){c=c.call(k,{hash:{},data:i})}else{c=k.code;c=typeof c===f?c.apply(k):c}g+=h(c)+"</td>\n<td>";if(c=d.message){c=c.call(k,{hash:{},data:i})}else{c=k.message;c=typeof c===f?c.apply(k):c}if(c||c===0){g+=c}g+="</td>\n<td width='50%'><span class=\"model-signature\" /></td>";return g})})();(function(){var j,r,u,o,l,k,n,m,i,p,s,q,h,c,g,f,e,d,b,a,x,w,t={}.hasOwnProperty,v=function(B,z){for(var y in z){if(t.call(z,y)){B[y]=z[y]}}function A(){this.constructor=B}A.prototype=z.prototype;B.prototype=new A();B.__super__=z.prototype;return B};s=(function(z){v(y,z);function y(){q=y.__super__.constructor.apply(this,arguments);return q}y.prototype.dom_id="swagger_ui";y.prototype.options=null;y.prototype.api=null;y.prototype.headerView=null;y.prototype.mainView=null;y.prototype.initialize=function(A){var B=this;if(A==null){A={}}if(A.dom_id!=null){this.dom_id=A.dom_id;delete A.dom_id}if($("#"+this.dom_id)==null){$("body").append('<div id="'+this.dom_id+'"></div>')}this.options=A;this.options.success=function(){return B.render()};this.options.progress=function(C){return B.showMessage(C)};this.options.failure=function(C){if(B.api&&B.api.isValid===false){log("not a valid 2.0 spec, loading legacy client");B.api=new SwaggerApi(B.options);return B.api.build()}else{return B.onLoadFailure(C)}};this.headerView=new r({el:$("#header")});return this.headerView.on("update-swagger-ui",function(C){return B.updateSwaggerUi(C)})};y.prototype.updateSwaggerUi=function(A){this.options.url=A.url;return this.load()};y.prototype.load=function(){var B,A;if((A=this.mainView)!=null){A.clear()}B=this.options.url;if(B.indexOf("http")!==0){B=this.buildUrl(window.location.href.toString(),B)}this.options.url=B;this.headerView.update(B);this.api=new SwaggerClient(this.options);return this.api.build()};y.prototype.render=function(){var A=this;this.showMessage("Finished Loading Resource Information. Rendering Swagger UI...");this.mainView=new u({model:this.api,el:$("#"+this.dom_id),swaggerOptions:this.options}).render();this.showMessage();switch(this.options.docExpansion){case"full":Docs.expandOperationsForResource("");break;case"list":Docs.collapseOperationsForResource("")}if(this.options.onComplete){this.options.onComplete(this.api,this)}return setTimeout(function(){return Docs.shebang()},400)};y.prototype.buildUrl=function(C,A){var B,D;log("base is "+C);if(A.indexOf("/")===0){D=C.split("/");C=D[0]+"//"+D[2];return C+A}else{B=C.length;if(C.indexOf("?")>-1){B=Math.min(B,C.indexOf("?"))}if(C.indexOf("#")>-1){B=Math.min(B,C.indexOf("#"))}C=C.substring(0,B);if(C.indexOf("/",C.length-1)!==-1){return C+A}return C+"/"+A}};y.prototype.showMessage=function(A){if(A==null){A=""}$("#message-bar").removeClass("message-fail");$("#message-bar").addClass("message-success");return $("#message-bar").html(A)};y.prototype.onLoadFailure=function(A){var B;if(A==null){A=""}$("#message-bar").removeClass("message-success");$("#message-bar").addClass("message-fail");B=$("#message-bar").html(A);if(this.options.onFailure!=null){this.options.onFailure(A)}return B};return y})(Backbone.Router);window.SwaggerUi=s;r=(function(z){v(y,z);function y(){h=y.__super__.constructor.apply(this,arguments);return h}y.prototype.events={"click #show-pet-store-icon":"showPetStore","click #show-wordnik-dev-icon":"showWordnikDev","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"};y.prototype.initialize=function(){};y.prototype.showPetStore=function(A){return this.trigger("update-swagger-ui",{url:"http://petstore.swagger.wordnik.com/api/api-docs"})};y.prototype.showWordnikDev=function(A){return this.trigger("update-swagger-ui",{url:"http://api.wordnik.com/v4/resources.json"})};y.prototype.showCustomOnKeyup=function(A){if(A.keyCode===13){return this.showCustom()}};y.prototype.showCustom=function(A){if(A!=null){A.preventDefault()}return this.trigger("update-swagger-ui",{url:$("#input_baseUrl").val(),apiKey:$("#input_apiKey").val()})};y.prototype.update=function(B,C,A){if(A==null){A=false}$("#input_baseUrl").val(B);if(A){return this.trigger("update-swagger-ui",{url:B})}};return y})(Backbone.View);u=(function(y){var z;v(A,y);function A(){g=A.__super__.constructor.apply(this,arguments);return g}z={alpha:function(C,B){return C.path.localeCompare(B.path)},method:function(C,B){return C.method.localeCompare(B.method)}};A.prototype.initialize=function(B){var C,I,J,E,D,G,H,F;if(B==null){B={}}if(B.swaggerOptions.sorter){E=B.swaggerOptions.sorter;J=z[E];if(this.model.apisArray){F=this.model.apisArray;for(G=0,H=F.length;G<H;G++){I=F[G];I.operationsArray.sort(J)}if(E==="alpha"){this.model.apisArray.sort(J)}}}if(this.model.info&&this.model.info.license&&typeof this.model.info.license==="string"){C=this.model.info.license;D=this.model.info.licenseUrl;this.model.info.license={};this.model.info.license.name=C;this.model.info.license.url=D}if(!this.model.info){this.model.info={}}if(!this.model.info.version){this.model.info.version=this.model.apiVersion}if(this.model.url.indexOf("http://localhost")===-1&&this.model.swaggerVersion===2){return this.model.validatorUrl=this.model.url}};A.prototype.render=function(){var C,H,E,F,D,B,G;$(this.el).html(Handlebars.templates.main(this.model));F={};C=0;G=this.model.apisArray;for(D=0,B=G.length;D<B;D++){E=G[D];H=E.name;while(typeof F[H]!=="undefined"){H=H+"_"+C;C+=1}E.id=H;F[H]=E;this.addResource(E)}return this};A.prototype.addResource=function(C){var B;C.id=C.id.replace(/\s/g,"_");B=new n({model:C,tagName:"li",id:"resource_"+C.id,className:"resource",swaggerOptions:this.options.swaggerOptions});return $("#resources").append(B.render().el)};A.prototype.clear=function(){return $(this.el).html("")};return A})(Backbone.View);n=(function(z){v(y,z);function y(){f=y.__super__.constructor.apply(this,arguments);return f}y.prototype.initialize=function(){if(""===this.model.description){return this.model.description=null}};y.prototype.render=function(){var B,G,D,C,E,A,F;$(this.el).html(Handlebars.templates.resource(this.model));D={};if(this.model.description){this.model.summary=this.model.description}F=this.model.operationsArray;for(E=0,A=F.length;E<A;E++){C=F[E];B=0;G=C.nickname;while(typeof D[G]!=="undefined"){G=G+"_"+B;B+=1}D[G]=C;C.nickname=G;C.parentId=this.model.id;this.addOperation(C)}$(".toggleEndpointList",this.el).click(this.callDocs.bind(this,"toggleEndpointListForResource"));$(".collapseResource",this.el).click(this.callDocs.bind(this,"collapseOperationsForResource"));$(".expandResource",this.el).click(this.callDocs.bind(this,"expandOperationsForResource"));return this};y.prototype.addOperation=function(A){var B;A.number=this.number;B=new o({model:A,tagName:"li",className:"endpoint",swaggerOptions:this.options.swaggerOptions});$(".endpoints",$(this.el)).append(B.render().el);return this.number++};y.prototype.callDocs=function(B,A){A.preventDefault();return Docs[B](A.currentTarget.getAttribute("data-id"))};return y})(Backbone.View);o=(function(z){v(y,z);function y(){e=y.__super__.constructor.apply(this,arguments);return e}y.prototype.invocationUrl=null;y.prototype.events={"submit .sandbox":"submitOperation","click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent","mouseenter .api-ic":"mouseEnter","mouseout .api-ic":"mouseExit"};y.prototype.initialize=function(){};y.prototype.mouseEnter=function(F){var D,E,I,B,A,J,G,C,K,H;D=$(F.currentTarget.parentNode).find("#api_information_panel");K=F.pageX;H=F.pageY;J=$(window).scrollLeft();G=$(window).scrollTop();B=J+$(window).width();A=G+$(window).height();C=D.width();E=D.height();if(K+C>B){K=B-C}if(K<J){K=J}if(H+E>A){H=A-E}if(H<G){H=G}I={};I.top=H;I.left=K;D.css(I);return $(F.currentTarget.parentNode).find("#api_information_panel").show()};y.prototype.mouseExit=function(A){return $(A.currentTarget.parentNode).find("#api_information_panel").hide()};y.prototype.render=function(){var D,U,V,T,R,K,J,Q,L,P,W,O,M,I,N,S,H,G,F,C,Y,ab,Z,X,E,B,A,ac,aa;V=true;if(!V){this.model.isReadOnly=true}this.model.description=this.model.description||this.model.notes;if(this.model.description){this.model.description=this.model.description.replace(/(?:\r\n|\r|\n)/g,"<br />")}this.model.oauth=null;if(this.model.authorizations){E=this.model.authorizations;for(T in E){N=E[T];if(T==="oauth2"){if(this.model.oauth===null){this.model.oauth={}}if(this.model.oauth.scopes===void 0){this.model.oauth.scopes=[]}for(H=0,Y=N.length;H<Y;H++){R=N[H];this.model.oauth.scopes.push(R)}}}}if(typeof this.model.responses!=="undefined"){this.model.responseMessages=[];B=this.model.responses;for(D in B){S=B[D];P=null;W=this.model.responses[D].schema;if(W&&W["$ref"]){P=W["$ref"];if(P.indexOf("#/definitions/")===0){P=P.substring("#/definitions/".length)}}this.model.responseMessages.push({code:D,message:S.description,responseModel:P})}}if(typeof this.model.responseMessages==="undefined"){this.model.responseMessages=[]}$(this.el).html(Handlebars.templates.operation(this.model));if(this.model.responseClassSignature&&this.model.responseClassSignature!=="string"){O={sampleJSON:this.model.responseSampleJSON,isParam:false,signature:this.model.responseClassSignature};L=new i({model:O,tagName:"div"});$(".model-signature",$(this.el)).append(L.render().el)}else{this.model.responseClassSignature="string";$(".model-signature",$(this.el)).html(this.model.type)}U={isParam:false};U.consumes=this.model.consumes;U.produces=this.model.produces;A=this.model.parameters;for(G=0,ab=A.length;G<ab;G++){K=A[G];I=K.type||K.dataType;if(typeof I==="undefined"){P=K.schema;if(P&&P["$ref"]){J=P["$ref"];if(J.indexOf("#/definitions/")===0){I=J.substring("#/definitions/".length)}else{I=J}}}if(I&&I.toLowerCase()==="file"){if(!U.consumes){U.consumes="multipart/form-data"}}K.type=I}Q=new m({model:U});$(".response-content-type",$(this.el)).append(Q.render().el);ac=this.model.parameters;for(F=0,Z=ac.length;F<Z;F++){K=ac[F];this.addParameter(K,U.consumes)}aa=this.model.responseMessages;for(C=0,X=aa.length;C<X;C++){M=aa[C];this.addStatusCode(M)}return this};y.prototype.addParameter=function(C,A){var B;C.consumes=A;B=new k({model:C,tagName:"tr",readOnly:this.model.isReadOnly});return $(".operation-params",$(this.el)).append(B.render().el)};y.prototype.addStatusCode=function(B){var A;A=new p({model:B,tagName:"tr"});return $(".operation-status",$(this.el)).append(A.render().el)};y.prototype.submitOperation=function(O){var Q,G,N,D,I,A,J,M,L,K,P,F,C,H,E,B;if(O!=null){O.preventDefault()}G=$(".sandbox",$(this.el));Q=true;G.find("input.required").each(function(){var R=this;$(this).removeClass("error");if(jQuery.trim($(this).val())===""){$(this).addClass("error");$(this).wiggle({callback:function(){return $(R).focus()}});return Q=false}});if(Q){D={};A={parent:this};N=false;H=G.find("input");for(M=0,P=H.length;M<P;M++){I=H[M];if((I.value!=null)&&jQuery.trim(I.value).length>0){D[I.name]=I.value}if(I.type==="file"){N=true}}E=G.find("textarea");for(L=0,F=E.length;L<F;L++){I=E[L];if((I.value!=null)&&jQuery.trim(I.value).length>0){D.body=I.value}}B=G.find("select");for(K=0,C=B.length;K<C;K++){I=B[K];J=this.getSelectedValue(I);if((J!=null)&&jQuery.trim(J).length>0){D[I.name]=J}}A.responseContentType=$("div select[name=responseContentType]",$(this.el)).val();A.requestContentType=$("div select[name=parameterContentType]",$(this.el)).val();$(".response_throbber",$(this.el)).show();if(N){return this.handleFileUpload(D,G)}else{return this.model["do"](D,A,this.showCompleteStatus,this.showErrorStatus,this)}}};y.prototype.success=function(A,B){return B.showCompleteStatus(A)};y.prototype.handleFileUpload=function(R,I){var M,H,C,N,L,K,P,J,G,F,D,Q,U,T,S,E,B,A,V,O=this;E=I.serializeArray();for(J=0,Q=E.length;J<Q;J++){N=E[J];if((N.value!=null)&&jQuery.trim(N.value).length>0){R[N.name]=N.value}}M=new FormData();P=0;B=this.model.parameters;for(G=0,U=B.length;G<U;G++){K=B[G];if(K.paramType==="form"){if(K.type.toLowerCase()!=="file"&&R[K.name]!==void 0){M.append(K.name,R[K.name])}}}C={};A=this.model.parameters;for(F=0,T=A.length;F<T;F++){K=A[F];if(K.paramType==="header"){C[K.name]=R[K.name]}}V=I.find('input[type~="file"]');for(D=0,S=V.length;D<S;D++){H=V[D];if(typeof H.files[0]!=="undefined"){M.append($(H).attr("name"),H.files[0]);P+=1}}this.invocationUrl=this.model.supportHeaderParams()?(C=this.model.getHeaderParams(R),this.model.urlify(R,false)):this.model.urlify(R,true);$(".request_url",$(this.el)).html("<pre></pre>");$(".request_url pre",$(this.el)).text(this.invocationUrl);L={type:this.model.method,url:this.invocationUrl,headers:C,data:M,dataType:"json",contentType:false,processData:false,error:function(X,Y,W){return O.showErrorStatus(O.wrap(X),O)},success:function(W){return O.showResponse(W,O)},complete:function(W){return O.showCompleteStatus(O.wrap(W),O)}};if(window.authorizations){window.authorizations.apply(L)}if(P===0){L.data.append("fake","true")}jQuery.ajax(L);return false};y.prototype.wrap=function(E){var C,F,H,B,G,D,A;H={};F=E.getAllResponseHeaders().split("\r");for(D=0,A=F.length;D<A;D++){B=F[D];C=B.split(":");if(C[0]!==void 0&&C[1]!==void 0){H[C[0].trim()]=C[1].trim()}}G={};G.content={};G.content.data=E.responseText;G.headers=H;G.request={};G.request.url=this.invocationUrl;G.status=E.status;return G};y.prototype.getSelectedValue=function(A){var D,C,F,B,E;if(!A.multiple){return A.value}else{C=[];E=A.options;for(F=0,B=E.length;F<B;F++){D=E[F];if(D.selected){C.push(D.value)}}if(C.length>0){return C.join(",")}else{return null}}};y.prototype.hideResponse=function(A){if(A!=null){A.preventDefault()}$(".response",$(this.el)).slideUp();return $(".response_hider",$(this.el)).fadeOut()};y.prototype.showResponse=function(A){var B;B=JSON.stringify(A,null,"\t").replace(/\n/g,"<br>");return $(".response_body",$(this.el)).html(escape(B))};y.prototype.showErrorStatus=function(B,A){return A.showStatus(B)};y.prototype.showCompleteStatus=function(B,A){return A.showStatus(B)};y.prototype.formatXml=function(H){var D,G,B,I,N,J,C,A,L,M,F,E,K;A=/(>)(<)(\/*)/g;M=/[ ]*(.*)[ ]+\n/g;D=/(<.+>)(.+\n)/g;H=H.replace(A,"$1\n$2$3").replace(M,"$1\n").replace(D,"$1\n$2");C=0;G="";N=H.split("\n");B=0;I="other";L={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0};F=function(T){var P,O,R,V,S,Q,U;Q={single:Boolean(T.match(/<.+\/>/)),closing:Boolean(T.match(/<\/.+>/)),opening:Boolean(T.match(/<[^!?].*>/))};S=((function(){var W;W=[];for(R in Q){U=Q[R];if(U){W.push(R)}}return W})())[0];S=S===void 0?"other":S;P=I+"->"+S;I=S;V="";B+=L[P];V=((function(){var X,Y,W;W=[];for(O=X=0,Y=B;0<=Y?X<Y:X>Y;O=0<=Y?++X:--X){W.push("  ")}return W})()).join("");if(P==="opening->closing"){return G=G.substr(0,G.length-1)+T+"\n"}else{return G+=V+T+"\n"}};for(E=0,K=N.length;E<K;E++){J=N[E];F(J)}return G};y.prototype.showStatus=function(F){var C,J,L,I,D,M,A,E,H,G,B;if(F.content===void 0){J=F.data;B=F.url}else{J=F.content.data;B=F.request.url}D=F.headers;L=D&&D["Content-Type"]?D["Content-Type"].split(";")[0].trim():null;if(!J){C=$("<code />").text("no content");E=$('<pre class="json" />').append(C)}else{if(L==="application/json"||/\+json$/.test(L)){M=null;try{M=JSON.stringify(JSON.parse(J),null,"  ")}catch(K){I=K;M="can't parse JSON.  Raw result:\n\n"+J}C=$("<code />").text(M);E=$('<pre class="json" />').append(C)}else{if(L==="application/xml"||/\+xml$/.test(L)){C=$("<code />").text(this.formatXml(J));E=$('<pre class="xml" />').append(C)}else{if(L==="text/html"){C=$("<code />").html(_.escape(J));E=$('<pre class="xml" />').append(C)}else{if(/^image\//.test(L)){E=$("<img>").attr("src",B)}else{C=$("<code />").text(J);E=$('<pre class="json" />').append(C)}}}}}H=E;$(".request_url",$(this.el)).html("<pre></pre>");$(".request_url pre",$(this.el)).text(B);$(".response_code",$(this.el)).html("<pre>"+F.status+"</pre>");$(".response_body",$(this.el)).html(H);$(".response_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(F.headers,null,"  ")).replace(/\n/g,"<br>")+"</pre>");$(".response",$(this.el)).slideDown();$(".response_hider",$(this.el)).show();$(".response_throbber",$(this.el)).hide();G=$(".response_body",$(this.el))[0];A=this.options.swaggerOptions;if(A.highlightSizeThreshold&&F.data.length>A.highlightSizeThreshold){return G}else{return hljs.highlightBlock(G)}};y.prototype.toggleOperationContent=function(){var A;A=$("#"+Docs.escapeResourceName(this.model.parentId)+"_"+this.model.nickname+"_content");if(A.is(":visible")){return Docs.collapseOperation(A)}else{return Docs.expandOperation(A)}};return y})(Backbone.View);p=(function(z){v(y,z);function y(){d=y.__super__.constructor.apply(this,arguments);return d}y.prototype.initialize=function(){};y.prototype.render=function(){var B,A,C;C=this.template();$(this.el).html(C(this.model));if(swaggerUi.api.models.hasOwnProperty(this.model.responseModel)){B={sampleJSON:JSON.stringify(swaggerUi.api.models[this.model.responseModel].createJSONSample(),null,2),isParam:false,signature:swaggerUi.api.models[this.model.responseModel].getMockSignature()};A=new i({model:B,tagName:"div"});$(".model-signature",this.$el).append(A.render().el)}else{$(".model-signature",this.$el).html("")}return this};y.prototype.template=function(){return Handlebars.templates.status_code};return y})(Backbone.View);k=(function(z){v(y,z);function y(){b=y.__super__.constructor.apply(this,arguments);return b}y.prototype.initialize=function(){return Handlebars.registerHelper("isArray",function(B,A){if(B.type.toLowerCase()==="array"||B.allowMultiple){return A.fn(this)}else{return A.inverse(this)}})};y.prototype.render=function(){var A,B,E,C,F,D,I,J,H,G;G=this.model.type||this.model.dataType;if(typeof G==="undefined"){D=this.model.schema;if(D&&D["$ref"]){C=D["$ref"];if(C.indexOf("#/definitions/")===0){G=C.substring("#/definitions/".length)}else{G=C}}}this.model.type=G;this.model.paramType=this.model["in"]||this.model.paramType;if(this.model.paramType==="body"){this.model.isBody=true}if(G&&G.toLowerCase()==="file"){this.model.isFile=true}this.model["default"]=this.model["default"]||this.model.defaultValue;H=this.template();$(this.el).html(H(this.model));I={sampleJSON:this.model.sampleJSON,isParam:true,signature:this.model.signature};if(this.model.sampleJSON){J=new i({model:I,tagName:"div"});$(".model-signature",$(this.el)).append(J.render().el)}else{$(".model-signature",$(this.el)).html(this.model.signature)}B=false;if(this.model.isBody){B=true}A={isParam:B};A.consumes=this.model.consumes;if(B){E=new l({model:A});$(".parameter-content-type",$(this.el)).append(E.render().el)}else{F=new m({model:A});$(".response-content-type",$(this.el)).append(F.render().el)}return this};y.prototype.template=function(){if(this.model.isList){return Handlebars.templates.param_list}else{if(this.options.readOnly){if(this.model.required){return Handlebars.templates.param_readonly_required}else{return Handlebars.templates.param_readonly}}else{if(this.model.required){return Handlebars.templates.param_required}else{return Handlebars.templates.param}}}};return y})(Backbone.View);i=(function(z){v(y,z);function y(){a=y.__super__.constructor.apply(this,arguments);return a}y.prototype.events={"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet":"snippetToTextArea"};y.prototype.initialize=function(){};y.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));this.switchToSnippet();this.isParam=this.model.isParam;if(this.isParam){$(".notice",$(this.el)).text("Click to set as parameter value")}return this};y.prototype.template=function(){return Handlebars.templates.signature};y.prototype.switchToDescription=function(A){if(A!=null){A.preventDefault()}$(".snippet",$(this.el)).hide();$(".description",$(this.el)).show();$(".description-link",$(this.el)).addClass("selected");return $(".snippet-link",$(this.el)).removeClass("selected")};y.prototype.switchToSnippet=function(A){if(A!=null){A.preventDefault()}$(".description",$(this.el)).hide();$(".snippet",$(this.el)).show();$(".snippet-link",$(this.el)).addClass("selected");return $(".description-link",$(this.el)).removeClass("selected")};y.prototype.snippetToTextArea=function(A){var B;if(this.isParam){if(A!=null){A.preventDefault()}B=$("textarea",$(this.el.parentNode.parentNode.parentNode));if($.trim(B.val())===""){return B.val(this.model.sampleJSON)}}};return y})(Backbone.View);j=(function(y){v(z,y);function z(){x=z.__super__.constructor.apply(this,arguments);return x}z.prototype.initialize=function(){};z.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=contentType]",$(this.el)).text("Response Content Type");return this};z.prototype.template=function(){return Handlebars.templates.content_type};return z})(Backbone.View);m=(function(y){v(z,y);function z(){w=z.__super__.constructor.apply(this,arguments);return w}z.prototype.initialize=function(){};z.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=responseContentType]",$(this.el)).text("Response Content Type");return this};z.prototype.template=function(){return Handlebars.templates.response_content_type};return z})(Backbone.View);l=(function(z){v(y,z);function y(){c=y.__super__.constructor.apply(this,arguments);return c}y.prototype.initialize=function(){};y.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=parameterContentType]",$(this.el)).text("Parameter content type:");return this};y.prototype.template=function(){return Handlebars.templates.parameter_content_type};return y})(Backbone.View)}).call(this);
\ No newline at end of file
+(function(){function e(){e.history=e.history||[],e.history.push(arguments),this.console&&console.log(Array.prototype.slice.call(arguments)[0])}this.Handlebars=this.Handlebars||{},this.Handlebars.templates=this.Handlebars.templates||{},this.Handlebars.templates.apikey_auth=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <span class="key_auth__value">'+s((r=null!=(r=t.value||(null!=e?e.value:e))?r:o,typeof r===a?r.call(e,{name:"value",hash:{},data:i}):r))+"</span>\n"},3:function(e,t,n,i){return'                <input placeholder="api_key" class="auth_input input_apiKey_entry" name="apiKey" type="text"/>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<div class="key_input_container">\n    <h3 class="auth__title">Api key authorization</h3>\n    <div class="auth__description">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</div>\n    <div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">name:</span>\n            <span class="key_auth__value">'+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+'</span>\n        </div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">in:</span>\n            <span class="key_auth__value">'+l((a=null!=(a=t["in"]||(null!=e?e["in"]:e))?a:s,typeof a===o?a.call(e,{name:"in",hash:{},data:i}):a))+'</span>\n        </div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">value:</span>\n';return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+"        </div>\n    </div>\n</div>\n"},useData:!0}),this.Handlebars.templates.auth_button_operation=Handlebars.template({1:function(e,t,n,i){return"        authorize__btn_operation_login\n"},3:function(e,t,n,i){return"        authorize__btn_operation_logout\n"},5:function(e,t,n,i){var r,a='        <ul class="authorize-scopes">\n';return r=t.each.call(e,null!=e?e.scopes:e,{name:"each",hash:{},fn:this.program(6,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"        </ul>\n"},6:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <li class="authorize__scope" title="'+s((r=null!=(r=t.description||(null!=e?e.description:e))?r:o,typeof r===a?r.call(e,{name:"description",hash:{},data:i}):r))+'">'+s((r=null!=(r=t.scope||(null!=e?e.scope:e))?r:o,typeof r===a?r.call(e,{name:"scope",hash:{},data:i}):r))+"</li>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a='<div class="authorize__btn authorize__btn_operation\n';return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(a+=r),a+='">\n',r=t["if"].call(e,null!=e?e.scopes:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"</div>\n"},useData:!0}),this.Handlebars.templates.auth_button=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){return"<a class='authorize__btn' href=\"#\">Authorize</a>\n"},useData:!0}),this.Handlebars.templates.auth_view=Handlebars.template({1:function(e,t,n,i){return'            <button type="button" class="auth__button auth_submit__button" data-sw-translate>Authorize</button>\n'},3:function(e,t,n,i){return'            <button type="button" class="auth__button auth_logout__button" data-sw-translate>Logout</button>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a='<div class="auth_container">\n\n    <div class="auth_inner"></div>\n    <div class="auth_submit">\n';return r=t.unless.call(e,null!=e?e.isLogout:e,{name:"unless",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(a+=r),r=t["if"].call(e,null!=e?e.isAuthorized:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"    </div>\n\n</div>\n"},useData:!0}),this.Handlebars.templates.basic_auth=Handlebars.template({1:function(e,t,n,i){return" - authorized"},3:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <span class="basic_auth__value">'+s((r=null!=(r=t.username||(null!=e?e.username:e))?r:o,typeof r===a?r.call(e,{name:"username",hash:{},data:i}):r))+"</span>\n"},5:function(e,t,n,i){return'                <input required placeholder="username" class="basic_auth__username auth_input" name="username" type="text"/>\n'},7:function(e,t,n,i){return'            <div class="auth_label">\n                <span class="basic_auth__label" data-sw-translate>password:</span>\n                <input required placeholder="password" class="basic_auth__password auth_input" name="password" type="password"/></label>\n            </div>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<div class='basic_auth_container'>\n    <h3 class=\"auth__title\">Basic authentication";return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='</h3>\n    <form class="basic_input_container">\n        <div class="auth__description">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</div>\n        <div class="auth_label">\n            <span class="basic_auth__label" data-sw-translate>username:</span>\n',r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.program(5,i),data:i}),null!=r&&(u+=r),u+="        </div>\n",r=t.unless.call(e,null!=e?e.isLogout:e,{name:"unless",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </form>\n</div>\n"},useData:!0}),this.Handlebars.templates.content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'	<option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label data-sw-translate for="'+l((a=null!=(a=t.contentTypeId||(null!=e?e.contentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"contentTypeId",hash:{},data:i}):a))+'">Response Content Type</label>\n<select name="contentType" id="'+l((a=null!=(a=t.contentTypeId||(null!=e?e.contentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"contentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),$(function(){$.fn.vAlign=function(){return this.each(function(){var e=$(this).height(),t=$(this).parent().height(),n=(t-e)/2;$(this).css("margin-top",n)})},$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(){var e=$(this).closest("form").innerWidth(),t=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10),n=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",e-t-n)})},$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent(),$("ul.downplayed li div.content p").vAlign(),$("form.sandbox").submit(function(){var e=!0;return $(this).find("input.required").each(function(){$(this).removeClass("error"),""===$(this).val()&&($(this).addClass("error"),$(this).wiggle(),e=!1)}),e})}),Function.prototype.bind&&console&&"object"==typeof console.log&&["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(e){console[e]=this.bind(console[e],console)},Function.prototype.call),window.Docs={shebang:function(){var e=$.param.fragment().split("/");switch(e.shift(),e.length){case 1:if(e[0].length>0){var t="resource_"+e[0];Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1})}break;case 2:Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1});var n=e.join("_"),i=n+"_content";Docs.expandOperation($("#"+i)),$("#"+n).slideto({highlight:!1})}},toggleEndpointListForResource:function(e){var t=$("li#resource_"+Docs.escapeResourceName(e)+" ul.endpoints");t.is(":visible")?($.bbq.pushState("#/",2),Docs.collapseEndpointListForResource(e)):($.bbq.pushState("#/"+e,2),Docs.expandEndpointListForResource(e))},expandEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideDown();$("li#resource_"+e).addClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideDown()},collapseEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideUp();$("li#resource_"+e).removeClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideUp()},expandOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideDown():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideUp():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(e){return e.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(e){e.slideDown()},collapseOperation:function(e){e.slideUp()}},Handlebars.registerHelper("sanitize",function(e){return e=e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,""),new Handlebars.SafeString(e)}),Handlebars.registerHelper("renderTextParam",function(e){var t,n="text",i="",r=e.type||e.schema.type||"",a="array"===r.toLowerCase()||e.allowMultiple,o=a&&Array.isArray(e["default"])?e["default"].join("\n"):e["default"],s=Object.keys(e).filter(function(e){return null!==e.match(/^X-data-/i)}).reduce(function(t,n){return t+=" "+n.substring(2,n.length)+"='"+e[n]+"'"},"");if("undefined"==typeof o&&(o=""),e.format&&"password"===e.format&&(n="password"),e.valueId&&(i=" id='"+e.valueId+"'"),("string"==typeof o||o instanceof String)&&(o=o.replace(/'/g,"&apos;")),a)t="<textarea class='body-textarea"+(e.required?" required":"")+"' name='"+e.name+"'"+i+s,t+=" placeholder='Provide multiple values in new lines"+(e.required?" (at least one required).":".")+"'>",t+=o+"</textarea>";else{var l="parameter";e.required&&(l+=" required"),t="<input class='"+l+"' minlength='"+(e.required?1:0)+"'",t+=" name='"+e.name+"' placeholder='"+(e.required?"(required)":"")+"'"+i+s,t+=" type='"+n+"' value='"+o+"'/>"}return new Handlebars.SafeString(t)}),Handlebars.registerHelper("ifCond",function(e,t,n,i){switch(t){case"==":return e==n?i.fn(this):i.inverse(this);case"===":return e===n?i.fn(this):i.inverse(this);case"<":return n>e?i.fn(this):i.inverse(this);case"<=":return n>=e?i.fn(this):i.inverse(this);case">":return e>n?i.fn(this):i.inverse(this);case">=":return e>=n?i.fn(this):i.inverse(this);case"&&":return e&&n?i.fn(this):i.inverse(this);case"||":return e||n?i.fn(this):i.inverse(this);default:return i.inverse(this)}}),this.Handlebars.templates.main=Handlebars.template({1:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression,s='  <div class="info_title">'+o(a(null!=(r=null!=e?e.info:e)?r.title:r,e))+'</div>\n  <div class="info_description markdown">';return r=a(null!=(r=null!=e?e.info:e)?r.description:r,e),null!=r&&(s+=r),s+="</div>\n",r=t["if"].call(e,null!=e?e.externalDocs:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="  ",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.termsOfServiceUrl:r,{name:"if",hash:{},fn:this.program(4,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.name:r,{name:"if",hash:{},fn:this.program(6,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,{name:"if",hash:{},fn:this.program(8,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.email:r,{name:"if",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.license:r,{name:"if",hash:{},fn:this.program(12,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+"\n"},2:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"  <p>"+o(a(null!=(r=null!=e?e.externalDocs:e)?r.description:r,e))+'</p>\n  <a href="'+o(a(null!=(r=null!=e?e.externalDocs:e)?r.url:r,e))+'" target="_blank">'+o(a(null!=(r=null!=e?e.externalDocs:e)?r.url:r,e))+"</a>\n"},4:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'<div class="info_tos"><a target="_blank" href="'+o(a(null!=(r=null!=e?e.info:e)?r.termsOfServiceUrl:r,e))+'" data-sw-translate>Terms of service</a></div>'},6:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div><div class='info_name' style=\"display: inline\" data-sw-translate>Created by </div> "+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.name:r,e))+"</div>"},8:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div class='info_url' data-sw-translate>See more at <a href=\""+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,e))+'">'+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,e))+"</a></div>"},10:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'<div class=\'info_email\'><a target="_parent" href="mailto:'+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.email:r,e))+"?subject="+o(a(null!=(r=null!=e?e.info:e)?r.title:r,e))+'" data-sw-translate>Contact the developer</a></div>'},12:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div class='info_license'><a target=\"_blank\" href='"+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.license:r)?r.url:r,e))+"'>"+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.license:r)?r.name:r,e))+"</a></div>"},14:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'  , <span style="font-variant: small-caps" data-sw-translate>api version</span>: '+o(a(null!=(r=null!=e?e.info:e)?r.version:r,e))+"\n    "},16:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'    <span style="float:right"><a target="_blank" href="'+s((r=null!=(r=t.validatorUrl||(null!=e?e.validatorUrl:e))?r:o,typeof r===a?r.call(e,{name:"validatorUrl",hash:{},data:i}):r))+"/debug?url="+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+'"><img id="validator" src="'+s((r=null!=(r=t.validatorUrl||(null!=e?e.validatorUrl:e))?r:o,typeof r===a?r.call(e,{name:"validatorUrl",hash:{},data:i}):r))+"?url="+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+'"></a>\n    </span>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<div class='info' id='api_info'>\n";return r=t["if"].call(e,null!=e?e.info:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="</div>\n<div class='container' id='resources_container'>\n  <div class='authorize-wrapper'></div>\n\n  <ul id='resources'></ul>\n\n  <div class=\"footer\">\n    <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: "+l((a=null!=(a=t.basePath||(null!=e?e.basePath:e))?a:s,typeof a===o?a.call(e,{name:"basePath",hash:{},data:i}):a))+"\n",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.version:r,{name:"if",hash:{},fn:this.program(14,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="]\n",r=t["if"].call(e,null!=e?e.validatorUrl:e,{name:"if",hash:{},fn:this.program(16,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </h4>\n    </div>\n</div>\n"},useData:!0}),this.Handlebars.templates.oauth2=Handlebars.template({1:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='            <li>\n                <input class="oauth-scope" type="checkbox" data-scope="'+l((a=null!=(a=t.scope||(null!=e?e.scope:e))?a:s,typeof a===o?a.call(e,{name:"scope",hash:{},data:i}):a))+'" oauthtype="'+l((a=null!=(a=t.OAuthSchemeKey||(null!=e?e.OAuthSchemeKey:e))?a:s,typeof a===o?a.call(e,{name:"OAuthSchemeKey",hash:{},data:i}):a))+'"/>\n                <label>'+l((a=null!=(a=t.scope||(null!=e?e.scope:e))?a:s,typeof a===o?a.call(e,{name:"scope",hash:{},data:i}):a))+'</label><br/>\n                <span class="api-scope-desc">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+"\n";return r=t["if"].call(e,null!=e?e.OAuthSchemeKey:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"                </span>\n            </li>\n"},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"                        ("+s((r=null!=(r=t.OAuthSchemeKey||(null!=e?e.OAuthSchemeKey:e))?r:o,typeof r===a?r.call(e,{name:"OAuthSchemeKey",hash:{},data:i}):r))+")\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<div>\n    <h3 class="auth__title">Select OAuth2.0 Scopes</h3>\n    <p>'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</p>\n    <p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.\n        <a href="#">Learn how to use</a>\n    </p>\n    <p><strong> '+l((a=null!=(a=t.appName||(null!=e?e.appName:e))?a:s,typeof a===o?a.call(e,{name:"appName",hash:{},data:i}):a))+" </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>\n    <p>Authorization URL: "+l((a=null!=(a=t.authorizationUrl||(null!=e?e.authorizationUrl:e))?a:s,typeof a===o?a.call(e,{name:"authorizationUrl",hash:{},data:i}):a))+"</p>\n    <p>flow: "+l((a=null!=(a=t.flow||(null!=e?e.flow:e))?a:s,typeof a===o?a.call(e,{name:"flow",hash:{},data:i}):a))+'</p>\n    <ul class="api-popup-scopes">\n';return r=t.each.call(e,null!=e?e.scopes:e,{name:"each",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </ul>\n</div>"},useData:!0}),this.Handlebars.templates.operation=Handlebars.template({1:function(e,t,n,i){return"deprecated"},3:function(e,t,n,i){return"            <h4><span data-sw-translate>Warning: Deprecated</span></h4>\n"},5:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='        <h4><span data-sw-translate>Implementation Notes</span></h4>\n        <div class="markdown">';return a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(l+=r),l+"</div>\n"},7:function(e,t,n,i){return"            <div class='authorize-wrapper authorize-wrapper_operation'></div>\n"},9:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='          <div class="response-class">\n            <h4><span data-sw-translate>Response Class</span> (<span data-sw-translate>Status</span> '+l((a=null!=(a=t.successCode||(null!=e?e.successCode:e))?a:s,typeof a===o?a.call(e,{name:"successCode",hash:{},data:i}):a))+")</h4>\n              ";return r=t["if"].call(e,null!=e?e.successDescription:e,{name:"if",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+'\n            <p><span class="model-signature" /></p>\n            <br/>\n            <div class="response-content-type" />\n            </div>\n'},10:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='<div class="markdown">';return a=null!=(a=t.successDescription||(null!=e?e.successDescription:e))?a:s,r=typeof a===o?a.call(e,{name:"successDescription",hash:{},data:i}):a,null!=r&&(l+=r),l+"</div>"},12:function(e,t,n,i){var r,a='          <h4 data-sw-translate>Headers</h4>\n          <table class="headers">\n            <thead>\n              <tr>\n                <th style="width: 100px; max-width: 100px" data-sw-translate>Header</th>\n                <th style="width: 310px; max-width: 310px" data-sw-translate>Description</th>\n                <th style="width: 200px; max-width: 200px" data-sw-translate>Type</th>\n                <th style="width: 320px; max-width: 320px" data-sw-translate>Other</th>\n              </tr>\n            </thead>\n            <tbody>\n';return r=t.each.call(e,null!=e?e.headers:e,{name:"each",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"            </tbody>\n          </table>\n"},13:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return"              <tr>\n                <td>"+a(r(i&&i.key,e))+"</td>\n                <td>"+a(r(null!=e?e.description:e,e))+"</td>\n                <td>"+a(r(null!=e?e.type:e,e))+"</td>\n                <td>"+a(r(null!=e?e.other:e,e))+"</td>\n              </tr>\n"},15:function(e,t,n,i){return'          <h4 data-sw-translate>Parameters</h4>\n          <table class=\'fullwidth parameters\'>\n          <thead>\n            <tr>\n            <th style="width: 100px; max-width: 100px" data-sw-translate>Parameter</th>\n            <th style="width: 310px; max-width: 310px" data-sw-translate>Value</th>\n            <th style="width: 200px; max-width: 200px" data-sw-translate>Description</th>\n            <th style="width: 100px; max-width: 100px" data-sw-translate>Parameter Type</th>\n            <th style="width: 220px; max-width: 230px" data-sw-translate>Data Type</th>\n            </tr>\n          </thead>\n          <tbody class="operation-params">\n\n          </tbody>\n          </table>\n'},17:function(e,t,n,i){return"          <div style='margin:0;padding:0;display:inline'></div>\n          <h4 data-sw-translate>Response Messages</h4>\n          <table class='fullwidth response-messages'>\n            <thead>\n            <tr>\n              <th data-sw-translate>HTTP Status Code</th>\n              <th data-sw-translate>Reason</th>\n              <th data-sw-translate>Response Model</th>\n              <th data-sw-translate>Headers</th>\n            </tr>\n            </thead>\n            <tbody class=\"operation-status\">\n            </tbody>\n          </table>\n"},19:function(e,t,n,i){return""},21:function(e,t,n,i){return"          <div class='sandbox_header'>\n            <input class='submit' type='submit' value='Try it out!' data-sw-translate/>\n            <a href='#' class='response_hider' style='display:none' data-sw-translate>Hide Response</a>\n            <span class='response_throbber' style='display:none'></span>\n          </div>\n"},23:function(e,t,n,i){return"          <h4 data-sw-translate>Request Headers</h4>\n          <div class='block request_headers'></div>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="  <ul class='operations' >\n    <li class='"+l((a=null!=(a=t.method||(null!=e?e.method:e))?a:s,typeof a===o?a.call(e,{name:"method",hash:{},data:i}):a))+" operation' id='"+l((a=null!=(a=t.parentId||(null!=e?e.parentId:e))?a:s,typeof a===o?a.call(e,{name:"parentId",hash:{},data:i}):a))+"_"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"'>\n      <div class='heading'>\n        <h3>\n          <span class='http_method'>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+'\' class="toggleOperation">'+l((a=null!=(a=t.method||(null!=e?e.method:e))?a:s,typeof a===o?a.call(e,{name:"method",hash:{},data:i}):a))+"</a>\n          </span>\n          <span class='path'>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"' class=\"toggleOperation ";return r=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='">'+l((a=null!=(a=t.path||(null!=e?e.path:e))?a:s,typeof a===o?a.call(e,{name:"path",hash:{},data:i}):a))+"</a>\n          </span>\n        </h3>\n        <ul class='options'>\n          <li>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+'\' class="toggleOperation">',a=null!=(a=t.summary||(null!=e?e.summary:e))?a:s,r=typeof a===o?a.call(e,{name:"summary",hash:{},data:i}):a,null!=r&&(u+=r),u+="</a>\n          </li>\n        </ul>\n      </div>\n      <div class='content' id='"+l((a=null!=(a=t.parentId||(null!=e?e.parentId:e))?a:s,typeof a===o?a.call(e,{name:"parentId",hash:{},data:i}):a))+"_"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"_content' style='display:none'>\n",r=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.description:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.security:e,{name:"if",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.type:e,{name:"if",hash:{},fn:this.program(9,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n",r=t["if"].call(e,null!=e?e.headers:e,{name:"if",hash:{},fn:this.program(12,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n        <form accept-charset='UTF-8' class='sandbox'>\n          <div style='margin:0;padding:0;display:inline'></div>\n",r=t["if"].call(e,null!=e?e.parameters:e,{name:"if",hash:{},fn:this.program(15,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.responseMessages:e,{name:"if",hash:{},fn:this.program(17,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.isReadOnly:e,{name:"if",hash:{},fn:this.program(19,i),inverse:this.program(21,i),data:i}),null!=r&&(u+=r),u+="        </form>\n        <div class='response' style='display:none'>\n          <h4 class='curl'>Curl</h4>\n          <div class='block curl'></div>\n          <h4 data-sw-translate>Request URL</h4>\n          <div class='block request_url'></div>\n",r=t["if"].call(e,null!=e?e.showRequestHeaders:e,{name:"if",hash:{},fn:this.program(23,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"          <h4 data-sw-translate>Response Body</h4>\n          <div class='block response_body'></div>\n          <h4 data-sw-translate>Response Code</h4>\n          <div class='block response_code'></div>\n          <h4 data-sw-translate>Response Headers</h4>\n          <div class='block response_headers'></div>\n        </div>\n      </div>\n    </li>\n  </ul>\n"},useData:!0}),this.Handlebars.templates.param_list=Handlebars.template({1:function(e,t,n,i){return" required"},3:function(e,t,n,i){return' multiple="multiple"'},5:function(e,t,n,i){return" required "},7:function(e,t,n,i){var r,a="      <option ";return r=t.unless.call(e,null!=e?e.hasDefault:e,{name:"unless",hash:{},fn:this.program(8,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+" value=''></option>\n"},8:function(e,t,n,i){return'  selected="" '},10:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="\n      <option ";return r=t["if"].call(e,null!=e?e.isDefault:e,{name:"if",hash:{},fn:this.program(11,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="  value='"+l((a=null!=(a=t.value||(null!=e?e.value:e))?a:s,typeof a===o?a.call(e,{name:"value",hash:{},data:i}):a))+"'> "+l((a=null!=(a=t.value||(null!=e?e.value:e))?a:s,typeof a===o?a.call(e,{name:"value",hash:{},data:i}):a))+" ",r=t["if"].call(e,null!=e?e.isDefault:e,{name:"if",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+" </option>\n\n"},11:function(e,t,n,i){return' selected=""  '},13:function(e,t,n,i){return" (default) "},15:function(e,t,n,i){return"<strong>"},17:function(e,t,n,i){return"</strong>"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code";return r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n  <select ",r=(t.isArray||e&&e.isArray||s).call(e,e,{name:"isArray",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+=' class="parameter ',r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='" name="'+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+'" id="'+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+'">\n\n',r=t.unless.call(e,null!=e?e.required:e,{name:"unless",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n",r=t.each.call(e,null!=(r=null!=e?e.allowableValues:e)?r.descriptiveValues:r,{name:"each",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='\n  </select>\n</td>\n<td class="markdown">',r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(15,i),inverse:this.noop,data:i}),null!=r&&(u+=r),a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(17,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_readonly_required=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"        <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"</textarea>\n"},3:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,i),inverse:this.program(6,i),data:i}),null!=r&&(a+=r),a},4:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"            "+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,
+typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"\n"},6:function(e,t,n,i){return"            (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_readonly=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"        <textarea class='body-textarea' readonly='readonly' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <div class="parameter-content-type" />\n'},3:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,i),inverse:this.program(6,i),data:i}),null!=r&&(a+=r),a},4:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"            "+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"\n"},6:function(e,t,n,i){return"            (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_required=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(4,i),data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'			<input type="file" name=\''+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'/>\n"},4:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.program(7,i),data:i}),null!=r&&(a+=r),a},5:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<div class=\"editor_holder\"></div>\n				<textarea class='body-textarea required' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id=\""+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'">'+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <br />\n        <div class="parameter-content-type" />\n'},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<textarea class='body-textarea required' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'></textarea>\n				<div class="editor_holder"></div>\n				<br />\n				<div class="parameter-content-type" />\n'},9:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(10,i),inverse:this.program(12,i),data:i}),null!=r&&(a+=r),a},10:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"			<input class='parameter' class='required' type='file' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'/>\n"},12:function(e,t,n,i){var r,a=t.helperMissing,o="";return r=(t.renderTextParam||e&&e.renderTextParam||a).call(e,e,{name:"renderTextParam",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(o+=r),o},13:function(e,t,n,i){return""},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(9,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td>\n	<strong><span class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</span></strong>\n</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(4,i),data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'			<input type="file" name=\''+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'/>\n			<div class="parameter-content-type" />\n'},4:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.program(7,i),data:i}),null!=r&&(a+=r),a},5:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<div class=\"editor_holder\"></div>\n				<textarea class='body-textarea' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <br />\n        <div class="parameter-content-type" />\n'},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<textarea class='body-textarea' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'></textarea>\n				<div class="editor_holder"></div>\n				<br />\n				<div class="parameter-content-type" />\n'},9:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(10,i),data:i}),null!=r&&(a+=r),a},10:function(e,t,n,i){var r,a=t.helperMissing,o="";return r=(t.renderTextParam||e&&e.renderTextParam||a).call(e,e,{name:"renderTextParam",hash:{},fn:this.program(11,i),inverse:this.noop,data:i}),null!=r&&(o+=r),o},11:function(e,t,n,i){return""},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(9,i),data:i}),null!=r&&(u+=r),u+='\n</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td>\n	<span class="model-signature"></span>\n</td>\n'},useData:!0}),this.Handlebars.templates.parameter_content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.consumes:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'  <option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label for="'+l((a=null!=(a=t.parameterContentTypeId||(null!=e?e.parameterContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"parameterContentTypeId",hash:{},data:i}):a))+'" data-sw-translate>Parameter content type:</label>\n<select name="parameterContentType" id="'+l((a=null!=(a=t.parameterContentTypeId||(null!=e?e.parameterContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"parameterContentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.consumes:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),this.Handlebars.templates.popup=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'<div class="api-popup-dialog-wrapper">\n    <div class="api-popup-title">'+s((r=null!=(r=t.title||(null!=e?e.title:e))?r:o,typeof r===a?r.call(e,{name:"title",hash:{},data:i}):r))+'</div>\n    <div class="api-popup-content"></div>\n    <p class="error-msg"></p>\n    <div class="api-popup-actions">\n        <button class="api-popup-cancel api-button gray" type="button">Cancel</button>\n    </div>\n</div>\n<div class="api-popup-dialog-shadow"></div>'},useData:!0}),this.Handlebars.templates.resource=Handlebars.template({1:function(e,t,n,i){return" : "},3:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"    <li>\n      <a href='"+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+"' data-sw-translate>Raw</a>\n    </li>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o,s="function",l=t.helperMissing,u=this.escapeExpression,c=t.blockHelperMissing,p="<div class='heading'>\n  <h2>\n    <a href='#!/"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'\' class="toggleEndpointList" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'">'+u((a=null!=(a=t.name||(null!=e?e.name:e))?a:l,typeof a===s?a.call(e,{name:"name",hash:{},data:i}):a))+"</a> ";return a=null!=(a=t.summary||(null!=e?e.summary:e))?a:l,o={name:"summary",hash:{},fn:this.program(1,i),inverse:this.noop,data:i},r=typeof a===s?a.call(e,o):a,t.summary||(r=c.call(e,r,o)),null!=r&&(p+=r),a=null!=(a=t.summary||(null!=e?e.summary:e))?a:l,r=typeof a===s?a.call(e,{name:"summary",hash:{},data:i}):a,null!=r&&(p+=r),p+="\n  </h2>\n  <ul class='options'>\n    <li>\n      <a href='#!/"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+"' id='endpointListTogger_"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'\' class="toggleEndpointList" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>Show/Hide</a>\n    </li>\n    <li>\n      <a href=\'#\' class="collapseResource" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href=\'#\' class="expandResource" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>\n        Expand Operations\n      </a>\n    </li>\n',r=t["if"].call(e,null!=e?e.url:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(p+=r),p+"  </ul>\n</div>\n<ul class='endpoints' id='"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+"_endpoint_list' style='display:none'>\n\n</ul>\n"},useData:!0}),this.Handlebars.templates.response_content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'  <option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label data-sw-translate for="'+l((a=null!=(a=t.responseContentTypeId||(null!=e?e.responseContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"responseContentTypeId",hash:{},data:i}):a))+'">Response Content Type</label>\n<select name="responseContentType" id="'+l((a=null!=(a=t.responseContentTypeId||(null!=e?e.responseContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"responseContentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),this.Handlebars.templates.signature=Handlebars.template({1:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='\n<div>\n<ul class="signature-nav">\n  <li><a class="description-link" href="#" data-sw-translate>Model</a></li>\n  <li><a class="snippet-link" href="#" data-sw-translate>Example Value</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n  <div class="description">\n    ';return a=null!=(a=t.signature||(null!=e?e.signature:e))?a:s,r=typeof a===o?a.call(e,{name:"signature",hash:{},data:i}):a,null!=r&&(l+=r),l+='\n  </div>\n\n  <div class="snippet">\n',r=t["if"].call(e,null!=e?e.sampleJSON:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(l+=r),r=t["if"].call(e,null!=e?e.sampleXML:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(l+=r),l+"  </div>\n</div>\n"},2:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='      <div class="snippet_json">\n        <pre><code>'+l((a=null!=(a=t.sampleJSON||(null!=e?e.sampleJSON:e))?a:s,typeof a===o?a.call(e,{name:"sampleJSON",hash:{},data:i}):a))+"</code></pre>\n        ";return r=t["if"].call(e,null!=e?e.isParam:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"\n      </div>\n"},3:function(e,t,n,i){return'<small class="notice" data-sw-translate></small>'},5:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='    <div class="snippet_xml">\n      <pre><code>'+l((a=null!=(a=t.sampleXML||(null!=e?e.sampleXML:e))?a:s,typeof a===o?a.call(e,{name:"sampleXML",hash:{},data:i}):a))+"</code></pre>\n      ";return r=t["if"].call(e,null!=e?e.isParam:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"\n    </div>\n"},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"    "+s((r=null!=(r=t.signature||(null!=e?e.signature:e))?r:o,typeof r===a?r.call(e,{name:"signature",hash:{},data:i}):r))+"\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a=t.helperMissing;return r=(t.ifCond||e&&e.ifCond||a).call(e,null!=e?e.sampleJSON:e,"||",null!=e?e.sampleXML:e,{name:"ifCond",hash:{},fn:this.program(1,i),inverse:this.program(7,i),data:i}),null!=r?r:""},useData:!0}),this.Handlebars.templates.status_code=Handlebars.template({1:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return"      <tr>\n        <td>"+a(r(i&&i.key,e))+"</td>\n        <td>"+a(r(null!=e?e.description:e,e))+"</td>\n        <td>"+a(r(null!=e?e.type:e,e))+"</td>\n      </tr>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td width='15%' class='code'>"+l((a=null!=(a=t.code||(null!=e?e.code:e))?a:s,typeof a===o?a.call(e,{name:"code",hash:{},data:i}):a))+'</td>\n<td class="markdown">';return a=null!=(a=t.message||(null!=e?e.message:e))?a:s,r=typeof a===o?a.call(e,{name:"message",hash:{},data:i}):a,null!=r&&(u+=r),u+='</td>\n<td width=\'50%\'><span class="model-signature" /></td>\n<td class="headers">\n  <table>\n    <tbody>\n',r=t.each.call(e,null!=e?e.headers:e,{name:"each",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </tbody>\n  </table>\n</td>"},useData:!0}),function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.SwaggerClient=e()}}(function(){var t;return function n(e,t,i){function r(o,s){if(!t[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(a)return a(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var c=t[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return r(n?n:t)},c,c.exports,n,e,t,i)}return t[o].exports}for(var a="function"==typeof require&&require,o=0;o<i.length;o++)r(i[o]);return r}({1:[function(e,t,n){"use strict";var i=e("./lib/auth"),r=e("./lib/helpers"),a=e("./lib/client"),o=function(e,t){return r.log('This is deprecated, use "new SwaggerClient" instead.'),new a(e,t)};Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){for(var n=t||0,i=this.length;i>n;n++)if(this[n]===e)return n;return-1}),String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),String.prototype.endsWith||(String.prototype.endsWith=function(e){return-1!==this.indexOf(e,this.length-e.length)}),t.exports=a,a.ApiKeyAuthorization=i.ApiKeyAuthorization,a.PasswordAuthorization=i.PasswordAuthorization,a.CookieAuthorization=i.CookieAuthorization,a.SwaggerApi=o,a.SwaggerClient=o,a.SchemaMarkup=e("./lib/schema-markup")},{"./lib/auth":2,"./lib/client":3,"./lib/helpers":4,"./lib/schema-markup":7}],2:[function(e,t,n){"use strict";var i=e("./helpers"),r=e("btoa"),a=e("cookiejar").CookieJar,o={each:e("lodash-compat/collection/each"),includes:e("lodash-compat/collection/includes"),isObject:e("lodash-compat/lang/isObject"),isArray:e("lodash-compat/lang/isArray")},s=t.exports.SwaggerAuthorizations=function(e){this.authz=e||{}};s.prototype.add=function(e,t){if(o.isObject(e))for(var n in e)this.authz[n]=e[n];else"string"==typeof e&&(this.authz[e]=t);return t},s.prototype.remove=function(e){return delete this.authz[e]},s.prototype.apply=function(e,t){var n=!0,i=!t,r=[],a=e.clientAuthorizations||this.authz;return o.each(t,function(e,t){"string"==typeof t&&r.push(t),o.each(e,function(e,t){r.push(t)})}),o.each(a,function(t,a){if(i||o.includes(r,a)){var s=t.apply(e);n=n&&!!s}}),n};var l=t.exports.ApiKeyAuthorization=function(e,t,n){this.name=e,this.value=t,this.type=n};l.prototype.apply=function(e){if("query"===this.type){var t;if(e.url.indexOf("?")>0){t=e.url.substring(e.url.indexOf("?")+1);var n=t.split("&");if(n&&n.length>0)for(var i=0;i<n.length;i++){var r=n[i].split("=");if(r&&r.length>0&&r[0]===this.name)return!1}}return e.url.indexOf("?")>0?e.url=e.url+"&"+this.name+"="+this.value:e.url=e.url+"?"+this.name+"="+this.value,!0}return"header"===this.type?("undefined"==typeof e.headers[this.name]&&(e.headers[this.name]=this.value),!0):void 0};var u=t.exports.CookieAuthorization=function(e){this.cookie=e};u.prototype.apply=function(e){return e.cookieJar=e.cookieJar||new a,e.cookieJar.setCookie(this.cookie),!0};var c=t.exports.PasswordAuthorization=function(e,t){3===arguments.length&&(i.log("PasswordAuthorization: the 'name' argument has been removed, pass only username and password"),e=arguments[1],t=arguments[2]),this.username=e,this.password=t};c.prototype.apply=function(e){return"undefined"==typeof e.headers.Authorization&&(e.headers.Authorization="Basic "+r(this.username+":"+this.password)),!0}},{"./helpers":4,btoa:13,cookiejar:18,"lodash-compat/collection/each":52,"lodash-compat/collection/includes":55,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isObject":144}],3:[function(e,t,n){"use strict";var i={bind:e("lodash-compat/function/bind"),cloneDeep:e("lodash-compat/lang/cloneDeep"),find:e("lodash-compat/collection/find"),forEach:e("lodash-compat/collection/forEach"),indexOf:e("lodash-compat/array/indexOf"),isArray:e("lodash-compat/lang/isArray"),isObject:e("lodash-compat/lang/isObject"),isFunction:e("lodash-compat/lang/isFunction"),isPlainObject:e("lodash-compat/lang/isPlainObject"),isUndefined:e("lodash-compat/lang/isUndefined")},r=e("./auth"),a=e("./helpers"),o=e("./types/model"),s=e("./types/operation"),l=e("./types/operationGroup"),u=e("./resolver"),c=e("./http"),p=e("./spec-converter"),h=e("q"),f=["apis","authorizationScheme","authorizations","basePath","build","buildFrom1_1Spec","buildFrom1_2Spec","buildFromSpec","clientAuthorizations","convertInfo","debug","defaultErrorCallback","defaultSuccessCallback","enableCookies","fail","failure","finish","help","host","idFromOp","info","initialize","isBuilt","isValid","modelPropertyMacro","models","modelsArray","options","parameterMacro","parseUri","progress","resourceCount","sampleModels","selfReflect","setConsolidatedModels","spec","supportedSubmitMethods","swaggerRequestHeaders","tagFromLabel","title","url","useJQuery","jqueryAjaxCache"],d=["apis","asCurl","description","externalDocs","help","label","name","operation","operations","operationsArray","path","tag"],m=["delete","get","head","options","patch","post","put"],y=t.exports=function(e,t){return this.authorizations=null,this.authorizationScheme=null,this.basePath=null,this.debug=!1,this.enableCookies=!1,this.info=null,this.isBuilt=!1,this.isValid=!1,this.modelsArray=[],this.resourceCount=0,this.url=null,this.useJQuery=!1,this.jqueryAjaxCache=!1,this.swaggerObject={},this.deferredClient=void 0,this.clientAuthorizations=new r.SwaggerAuthorizations,"undefined"!=typeof e?this.initialize(e,t):this};y.prototype.initialize=function(e,t){return this.models={},this.sampleModels={},"string"==typeof e?this.url=e:i.isObject(e)&&(t=e,this.url=t.url),this.url&&-1===this.url.indexOf("http")&&"undefined"!=typeof window&&window&&window.location&&(this.url=window.location.origin+this.url),t=t||{},this.clientAuthorizations.add(t.authorizations),this.swaggerRequestHeaders=t.swaggerRequestHeaders||"application/json;charset=utf-8,*/*",this.defaultSuccessCallback=t.defaultSuccessCallback||null,this.defaultErrorCallback=t.defaultErrorCallback||null,this.modelPropertyMacro=t.modelPropertyMacro||null,this.parameterMacro=t.parameterMacro||null,this.usePromise=t.usePromise||null,this.usePromise&&(this.deferredClient=h.defer()),"function"==typeof t.success&&(this.success=t.success),t.useJQuery&&(this.useJQuery=t.useJQuery),t.jqueryAjaxCache&&(this.jqueryAjaxCache=t.jqueryAjaxCache),t.enableCookies&&(this.enableCookies=t.enableCookies),this.options=t||{},this.supportedSubmitMethods=t.supportedSubmitMethods||[],this.failure=t.failure||function(e){throw e},this.progress=t.progress||function(){},this.spec=i.cloneDeep(t.spec),t.scheme&&(this.scheme=t.scheme),this.usePromise||"function"==typeof t.success?(this.ready=!0,this.build()):void 0},y.prototype.build=function(e){if(this.isBuilt)return this;var t=this;this.progress("fetching resource list: "+this.url+"; Please wait.");var n={useJQuery:this.useJQuery,jqueryAjaxCache:this.jqueryAjaxCache,url:this.url,method:"get",headers:{accept:this.swaggerRequestHeaders},on:{error:function(e){return"http"!==t.url.substring(0,4)?t.fail("Please specify the protocol for "+t.url):0===e.status?t.fail("Can't read from server.  It may not have the appropriate access-control-origin settings."):404===e.status?t.fail("Can't read swagger JSON from "+t.url):t.fail(e.status+" : "+e.statusText+" "+t.url)},response:function(e){var n=e.obj;if(!n)return t.fail("failed to parse JSON/YAML response");if(t.swaggerVersion=n.swaggerVersion,t.swaggerObject=n,n.swagger&&2===parseInt(n.swagger))t.swaggerVersion=n.swagger,(new u).resolve(n,t.url,t.buildFromSpec,t),t.isValid=!0;else{var i=new p;t.oldSwaggerObject=t.swaggerObject,i.setDocumentationLocation(t.url),i.convert(n,t.clientAuthorizations,t.options,function(e){t.swaggerObject=e,(new u).resolve(e,t.url,t.buildFromSpec,t),t.isValid=!0})}}}};if(this.spec)t.swaggerObject=this.spec,setTimeout(function(){(new u).resolve(t.spec,t.url,t.buildFromSpec,t)},10);else{if(this.clientAuthorizations.apply(n),e)return n;(new c).execute(n,this.options)}return this.usePromise?this.deferredClient.promise:this},y.prototype.buildFromSpec=function(e){if(this.isBuilt)return this;this.apis={},this.apisArray=[],this.basePath=e.basePath||"",this.consumes=e.consumes,this.host=e.host||"",this.info=e.info||{},this.produces=e.produces,this.schemes=e.schemes||[],this.securityDefinitions=e.securityDefinitions,this.security=e.security,this.title=e.title||"",e.externalDocs&&(this.externalDocs=e.externalDocs),this.authSchemes=e.securityDefinitions;var t,n={};if(Array.isArray(e.tags))for(n={},t=0;t<e.tags.length;t++){var r=e.tags[t];n[r.name]=r}var u;"string"==typeof this.url?(u=this.parseUri(this.url),"undefined"==typeof this.scheme&&"undefined"==typeof this.schemes||0===this.schemes.length?this.scheme=u.scheme||"http":"undefined"==typeof this.scheme&&(this.scheme=this.schemes[0]||u.scheme),"undefined"!=typeof this.host&&""!==this.host||(this.host=u.host,u.port&&(this.host=this.host+":"+u.port))):"undefined"==typeof this.schemes||0===this.schemes.length?this.scheme="http":"undefined"==typeof this.scheme&&(this.scheme=this.schemes[0]),this.definitions=e.definitions;var c;for(c in this.definitions){var p=new o(c,this.definitions[c],this.models,this.modelPropertyMacro);p&&(this.models[c]=p)}var h=this;h.apis.help=i.bind(h.help,h),i.forEach(e.paths,function(e,t){i.isPlainObject(e)&&i.forEach(m,function(r){var o=e[r];if(!i.isUndefined(o)){if(!i.isPlainObject(o))return void a.log("The '"+r+"' operation for '"+t+"' path is not an Operation Object");var u=o.tags;!i.isUndefined(u)&&i.isArray(u)&&0!==u.length||(u=o.tags=["default"]);var c=h.idFromOp(t,r,o),p=new s(h,o.scheme,c,r,t,o,h.definitions,h.models,h.clientAuthorizations);i.forEach(u,function(e){var t=i.indexOf(f,e)>-1?"_"+e:e,r=i.indexOf(d,e)>-1?"_"+e:e,o=h[t];if(t!==e&&a.log("The '"+e+"' tag conflicts with a SwaggerClient function/property name.  Use 'client."+t+"' or 'client.apis."+e+"' instead of 'client."+e+"'."),r!==e&&a.log("The '"+e+"' tag conflicts with a SwaggerClient operation function/property name.  Use 'client.apis."+r+"' instead of 'client.apis."+e+"'."),i.indexOf(d,c)>-1&&(a.log("The '"+c+"' operationId conflicts with a SwaggerClient operation function/property name.  Use 'client.apis."+r+"._"+c+"' instead of 'client.apis."+r+"."+c+"'."),c="_"+c,p.nickname=c),i.isUndefined(o)){o=h[t]=h.apis[r]={},o.operations={},o.label=r,o.apis={};var s=n[e];i.isUndefined(s)||(o.description=s.description,o.externalDocs=s.externalDocs),h[t].help=i.bind(h.help,o),h.apisArray.push(new l(e,o.description,o.externalDocs,p))}c=h.makeUniqueOperationId(c,h.apis[r]),i.isFunction(o.help)||(o.help=i.bind(h.help,o)),h.apis[r][c]=o[c]=i.bind(p.execute,p),h.apis[r][c].help=o[c].help=i.bind(p.help,p),h.apis[r][c].asCurl=o[c].asCurl=i.bind(p.asCurl,p),o.apis[c]=o.operations[c]=p;var u=i.find(h.apisArray,function(t){return t.tag===e});u&&u.operationsArray.push(p)})}})});var y=[];return i.forEach(Object.keys(n),function(e){var t;for(t in h.apisArray){var n=h.apisArray[t];n&&e===n.name&&(y.push(n),h.apisArray[t]=null)}}),i.forEach(h.apisArray,function(e){e&&y.push(e)}),h.apisArray=y,i.forEach(e.definitions,function(e,t){e.id=t.toLowerCase(),e.name=t,h.modelsArray.push(e)}),this.isBuilt=!0,this.usePromise?(this.isValid=!0,this.isBuilt=!0,this.deferredClient.resolve(this),this.deferredClient.promise):(this.success&&this.success(),this)},y.prototype.makeUniqueOperationId=function(e,t){for(var n=0,r=e;;){var a=!1;if(i.forEach(t.operations,function(e){e.nickname===r&&(a=!0)}),!a)return r;r=e+"_"+n,n++}return e},y.prototype.parseUri=function(e){var t=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,n=t.exec(e);return{scheme:n[4]?n[4].replace(":",""):void 0,host:n[11],port:n[12],path:n[15]}},y.prototype.help=function(e){var t="";return this instanceof y?i.forEach(this.apis,function(e,n){i.isPlainObject(e)&&(t+="operations for the '"+n+"' tag\n",i.forEach(e.operations,function(e,n){t+="  * "+n+": "+e.summary+"\n"}))}):(this instanceof l||i.isPlainObject(this))&&(t+="operations for the '"+this.label+"' tag\n",i.forEach(this.apis,function(e,n){t+="  * "+n+": "+e.summary+"\n"})),e?t:(a.log(t),t)},y.prototype.tagFromLabel=function(e){return e},y.prototype.idFromOp=function(e,t,n){n&&n.operationId||(n=n||{},n.operationId=t+"_"+e);var i=n.operationId.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|.\/?,\\'""-]/g,"_")||e.substring(1)+"_"+t;return i=i.replace(/((_){2,})/g,"_"),i=i.replace(/^(_)*/g,""),i=i.replace(/([_])*$/g,"")},y.prototype.setHost=function(e){this.host=e,this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.host=e})})},y.prototype.setBasePath=function(e){this.basePath=e,this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.basePath=e})})},y.prototype.setSchemes=function(e){this.schemes=e,e&&e.length>0&&this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.scheme=e[0]})})},y.prototype.fail=function(e){return this.usePromise?(this.deferredClient.reject(e),this.deferredClient.promise):void(this.failure?this.failure(e):this.failure(e))}},{"./auth":2,"./helpers":4,"./http":5,"./resolver":6,"./spec-converter":8,
+"./types/model":9,"./types/operation":10,"./types/operationGroup":11,"lodash-compat/array/indexOf":49,"lodash-compat/collection/find":53,"lodash-compat/collection/forEach":54,"lodash-compat/function/bind":58,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isFunction":142,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isUndefined":148,q:157}],4:[function(e,t,n){(function(n){"use strict";var i={isPlainObject:e("lodash-compat/lang/isPlainObject"),indexOf:e("lodash-compat/array/indexOf")};t.exports.__bind=function(e,t){return function(){return e.apply(t,arguments)}};var r=t.exports.log=function(){console&&"test"!==n.env.NODE_ENV&&console.log(Array.prototype.slice.call(arguments)[0])};t.exports.fail=function(e){r(e)};var a=(t.exports.optionHtml=function(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"},t.exports.resolveSchema=function(e){return i.isPlainObject(e.schema)&&(e=a(e.schema)),e});t.exports.simpleRef=function(e){return"undefined"==typeof e?null:0===e.indexOf("#/definitions/")?e.substring("#/definitions/".length):e}}).call(this,e("_process"))},{_process:12,"lodash-compat/array/indexOf":49,"lodash-compat/lang/isPlainObject":145}],5:[function(t,n,i){"use strict";var r=t("./helpers"),a=t("superagent"),o=t("js-yaml"),s={isObject:t("lodash-compat/lang/isObject")},l=function(){this.type="JQueryHttpClient"},u=function(){this.type="SuperagentHttpClient"},c=n.exports=function(){};c.prototype.execute=function(t,n){var i;i=n&&n.client?n.client:new u(n),i.opts=n||{};var r=!1;if("undefined"!=typeof window&&"undefined"!=typeof window.jQuery&&(r=!0),"options"===t.method.toLowerCase()&&"SuperagentHttpClient"===i.type&&(e("forcing jQuery as OPTIONS are not supported by SuperAgent"),t.useJQuery=!0),this.isInternetExplorer()&&(t.useJQuery===!1||!r))throw new Error("Unsupported configuration! JQuery is required but not available");(t&&t.useJQuery===!0||this.isInternetExplorer()&&r)&&(i=new l(n));var a=t.on.response,o=t.on.error,c=function(e){return n&&n.requestInterceptor&&(e=n.requestInterceptor.apply(e)),e},p=function(e){return n&&n.responseInterceptor&&(e=n.responseInterceptor.apply(e)),a(e)},h=function(e){n&&n.responseInterceptor&&(e=n.responseInterceptor.apply(e)),o(e)};return t.on.error=function(e){h(e)},t.on.response=function(e){p(e)},s.isObject(t)&&s.isObject(t.body)&&t.body.type&&"formData"===t.body.type&&n.useJQuery&&(t.contentType=!1,t.processData=!1,delete t.headers["Content-Type"]),t=c(t)||t,t.beforeSend?t.beforeSend(function(e){i.execute(e||t)}):i.execute(t),t.deferred?t.deferred.promise:t},c.prototype.isInternetExplorer=function(){var e=!1;if("undefined"!=typeof navigator&&navigator.userAgent){var t=navigator.userAgent.toLowerCase();if(-1!==t.indexOf("msie")){var n=parseInt(t.split("msie")[1]);8>=n&&(e=!0)}}return e},l.prototype.execute=function(e){var t=this.jQuery||"undefined"!=typeof window&&window.jQuery,n=e.on,i=e;if("undefined"==typeof t||t===!1)throw new Error("Unsupported configuration! JQuery is required but not available");return e.type=e.method,e.cache=e.jqueryAjaxCache,e.data=e.body,delete e.jqueryAjaxCache,delete e.useJQuery,delete e.body,e.complete=function(e){for(var t={},a=e.getAllResponseHeaders().split("\n"),s=0;s<a.length;s++){var l=a[s].trim();if(0!==l.length){var u=l.indexOf(":");if(-1!==u){var c=l.substring(0,u).trim(),p=l.substring(u+1).trim();t[c]=p}else t[l]=null}}var h={url:i.url,method:i.method,status:e.status,statusText:e.statusText,data:e.responseText,headers:t};try{var f=e.responseJSON||o.safeLoad(e.responseText);h.obj="string"==typeof f?{}:f}catch(d){r.log("unable to parse JSON/YAML content")}if(h.obj=h.obj||null,e.status>=200&&e.status<300)n.response(h);else{if(!(0===e.status||e.status>=400&&e.status<599))return n.response(h);n.error(h)}},t.support.cors=!0,t.ajax(e)},u.prototype.execute=function(e){var t=e.method.toLowerCase();"delete"===t&&(t="del");var n=e.headers||{},i=a[t](e.url);if(e.enableCookies&&i.withCredentials(),e.body)if(s.isObject(e.body)){var l=e.headers["Content-Type"]||"";if(0===l.indexOf("multipart/form-data"))if(delete n["Content-Type"],"[object FormData]"==={}.toString.apply(e.body))for(var u=e.body.keys();;){var c=u.next();if(c.done)break;var p=c.value,h=e.body.get(p);console.log({}.toString.apply(h)),"[object File]"==={}.toString.apply(h)?i.attach(p,h):i.field(p,h)}else{var f;for(var f in e.body){var h=e.body[f];i.field(f,h)}}else s.isObject(e.body)&&(e.body=JSON.stringify(e.body),i.send(e.body))}else i.send(e.body);var d;for(d in n)i.set(d,n[d]);"function"==typeof i.buffer&&i.buffer(),i.end(function(t,n){n=n||{status:0,headers:{error:"no response from server"}};var i,a={url:e.url,method:e.method,headers:n.headers};if(!t&&n.error&&(t=n.error),t&&e.on&&e.on.error){if(a.errObj=t,a.status=n?n.status:500,a.statusText=n?n.text:t.message,n.headers&&n.headers["content-type"]&&n.headers["content-type"].indexOf("application/json")>=0)try{a.obj=JSON.parse(a.statusText)}catch(s){a.obj=null}i=e.on.error}else if(n&&e.on&&e.on.response){var l;if(n.body&&Object.keys(n.body).length>0)l=n.body;else try{l=o.safeLoad(n.text),l="string"==typeof l?null:l}catch(s){r.log("cannot parse JSON/YAML content")}a.obj="object"==typeof l?l:null,a.status=n.status,a.statusText=n.text,i=e.on.response}a.data=a.statusText,i&&i(a)})}},{"./helpers":4,"js-yaml":19,"lodash-compat/lang/isObject":144,superagent:158}],6:[function(e,t,n){"use strict";var i=e("./http"),r={isObject:e("lodash-compat/lang/isObject"),cloneDeep:e("lodash-compat/lang/cloneDeep"),isArray:e("lodash-compat/lang/isArray")},a=t.exports=function(){this.failedUrls=[]};a.prototype.processAllOf=function(e,t,n,i,r,a){var o,s,l;n["x-resolved-from"]=["#/definitions/"+t];var u=n.allOf;for(u.sort(function(e,t){return e.$ref&&t.$ref?0:e.$ref?-1:1}),o=0;o<u.length;o++)l=u[o],s="/definitions/"+t+"/allOf",this.resolveInline(e,a,l,i,r,s)},a.prototype.resolve=function(e,t,n,a){this.spec=e;var o,s,l=t,u=n,c=a,p={};"function"==typeof t&&(l=null,u=t,c=n);var h=l;this.scope=c||this,this.iteration=this.iteration||0,this.scope.options&&this.scope.options.requestInterceptor&&(p.requestInterceptor=this.scope.options.requestInterceptor),this.scope.options&&this.scope.options.responseInterceptor&&(p.responseInterceptor=this.scope.options.responseInterceptor);var f,d,m,y,g=0,v={},b={},w=[];e.definitions=e.definitions||{};for(f in e.definitions){var x=e.definitions[f];if(x.$ref)this.resolveInline(l,e,x,w,b,x);else{for(y in x.properties)m=x.properties[y],r.isArray(m.allOf)?this.processAllOf(l,f,m,w,b,e):this.resolveTo(l,m,w,"/definitions");x.allOf&&this.processAllOf(l,f,x,w,b,e)}}e.parameters=e.parameters||{};for(f in e.parameters){var A=e.parameters[f];if("body"===A["in"]&&A.schema)if(r.isArray(A.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:A.schema.allOf},delete A.schema.allOf,A.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else this.resolveTo(l,A.schema,w,o);A.$ref&&this.resolveInline(l,e,A,w,b,A.$ref)}for(f in e.paths){var S,k,C;d=e.paths[f];for(S in d)if("$ref"===S)o="/paths"+f,this.resolveInline(l,e,d,w,b,o);else{k=d[S];var E=d.parameters||[],I=k.parameters||[];for(s in E){var A=E[s];I.unshift(A)}"parameters"!==S&&r.isObject(k)&&(k.parameters=k.parameters||I);for(s in I){var A=I[s];if(o="/paths"+f+"/"+S+"/parameters","body"===A["in"]&&A.schema)if(r.isArray(A.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:A.schema.allOf},delete A.schema.allOf,A.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else this.resolveTo(l,A.schema,w,o);A.$ref&&this.resolveInline(l,e,A,w,b,A.$ref)}for(C in k.responses){var T=k.responses[C];if(o="/paths"+f+"/"+S+"/responses/"+C,r.isObject(T)&&(T.$ref&&this.resolveInline(l,e,T,w,b,o),T.schema)){var $=T;if(r.isArray($.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:$.schema.allOf},delete $.schema.allOf,delete $.schema.type,$.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else"array"===$.schema.type?$.schema.items&&$.schema.items.$ref&&this.resolveInline(l,e,$.schema.items,w,b,o):this.resolveTo(l,T.schema,w,o)}}}d.parameters=[]}var M,U=0,P=[],L=w;for(s=0;s<L.length;s++){var D=L[s];if(l===D.root){if("ref"===D.resolveAs){var R,N=((D.root||"")+"/"+D.key).split("/"),F=[],B="";if(D.key.indexOf("../")>=0){for(var q=0;q<N.length;q++)".."===N[q]?F=F.slice(0,F.length-1):F.push(N[q]);for(R=0;R<F.length;R++)R>0&&(B+="/"),B+=F[R];D.root=B,P.push(D)}else if(M=D.key.split("#"),2===M.length){0!==M[0].indexOf("http://")&&0!==M[0].indexOf("https://")||(D.root=M[0]),o=M[1].split("/");var V,z=e;for(R=0;R<o.length;R++){var H=o[R];if(""!==H){if(z=z[H],"undefined"==typeof z){V=null;break}V=z}}null===V&&P.push(D)}}else if("inline"===D.resolveAs){if(D.key&&-1===D.key.indexOf("#")&&"/"!==D.key.charAt(0)){for(M=D.root.split("/"),o="",s=0;s<M.length-1;s++)o+=M[s]+"/";o+=D.key,D.root=o,D.location=""}P.push(D)}}else P.push(D)}U=P.length;for(var J=0;J<P.length;J++)!function(e,t,n){if(e.root&&e.root!==l)if(-1===n.failedUrls.indexOf(e.root)){var r={useJQuery:!1,url:e.root,method:"get",headers:{accept:n.scope.swaggerRequestHeaders||"application/json"},on:{error:function(i){g+=1,console.log("failed url: "+r.url),n.failedUrls.push(r.url),b[e.key]={root:e.root,location:e.location},g===U&&n.finish(t,h,w,v,b,u)},response:function(i){var r=i.obj;n.resolveItem(r,e.root,w,v,b,e),g+=1,g===U&&n.finish(t,h,w,v,b,u)}}};c&&c.clientAuthorizations&&c.clientAuthorizations.apply(r),(new i).execute(r,p)}else g+=1,b[e.key]={root:e.root,location:e.location},g===U&&n.finish(t,h,w,v,b,u);else n.resolveItem(t,h,w,v,b,e),g+=1,g===U&&n.finish(t,l,w,v,b,u,!0)}(P[J],e,this);0===Object.keys(P).length&&this.finish(e,h,w,v,b,u)},a.prototype.resolveItem=function(e,t,n,i,r,a){var o=a.location,s=e,l=o.split("/");if(""!==o)for(var u=0;u<l.length;u++){var c=l[u];if(-1!==c.indexOf("~1")&&(c=l[u].replace(/~0/g,"~").replace(/~1/g,"/"),"/"!==c.charAt(0)&&(c="/"+c)),"undefined"==typeof s||null===s)break;if(""===c&&u===l.length-1&&l.length>1){s=null;break}c.length>0&&(s=s[c])}var p=a.key;l=a.key.split("/");var h=l[l.length-1];h.indexOf("#")>=0&&(h=h.split("#")[1]),null!==s&&"undefined"!=typeof s?i[p]={name:h,obj:s,key:a.key,root:a.root}:r[p]={root:a.root,location:a.location}},a.prototype.finish=function(e,t,n,i,r,a,o){var s;for(s in n){var l=n[s],u=l.key,c=i[u];if(c)if(e.definitions=e.definitions||{},"ref"===l.resolveAs){if(o!==!0)for(u in c.obj)var p=this.retainRoot(c.obj[u],l.root);e.definitions[c.name]=c.obj,l.obj.$ref="#/definitions/"+c.name}else if("inline"===l.resolveAs){var h=l.obj;h["x-resolved-from"]=[l.key],delete h.$ref;for(u in c.obj){var p=c.obj[u];o!==!0&&(p=this.retainRoot(c.obj[u],l.root)),h[u]=p}}}var f=this.countUnresolvedRefs(e);0===f||this.iteration>5?(this.resolveAllOf(e.definitions),a.call(this.scope,e,r)):(this.iteration+=1,this.resolve(e,t,a,this.scope))},a.prototype.countUnresolvedRefs=function(e){var t,n=this.getRefs(e),i=[],r=[];for(t in n)0===t.indexOf("#")?i.push(t.substring(1)):r.push(t);for(t=0;t<i.length;t++)for(var a=i[t],o=a.split("/"),s=e,l=0;l<o.length;l++){var u=o[l];if(""!==u&&(s=s[u],"undefined"==typeof s)){r.push(a);break}}return r.length},a.prototype.getRefs=function(e,t){t=t||e;var n={};for(var i in t)if(t.hasOwnProperty(i)){var a=t[i];if("$ref"===i&&"string"==typeof a)n[a]=null;else if(r.isObject(a)){var o=this.getRefs(a);for(var s in o)n[s]=null}}return n},a.prototype.retainRoot=function(e,t){for(var n in e){var i=e[n];if("$ref"===n&&"string"==typeof i){if(0!==i.indexOf("http://")&&0!==i.indexOf("https://")){var a=!0;if(t){var o=t.slice(-1);if("/"!==o&&0!==i.indexOf("#")&&0!==i.indexOf("http://")&&i.indexOf("https://")){a=!1;var s=t.split("/");s=s.splice(0,s.length-1),t="";for(var l=0;l<s.length;l++)t+=s[l]+"/"}}0!==i.indexOf("#")&&a&&(i="#"+i),i=(t||"")+i,e[n]=i}}else r.isObject(i)&&this.retainRoot(i,t)}return e},a.prototype.resolveInline=function(e,t,n,i,r,a){var o,s,l,u,c=n.$ref,p=n.$ref,h=!1;if(e=e||"",p){if(0===p.indexOf("../")){for(s=p.split("../"),l=e.split("/"),p="",o=0;o<s.length;o++)""===s[o]?l=l.slice(0,l.length-1):p+=s[o];for(e="",o=0;o<l.length-1;o++)o>0&&(e+="/"),e+=l[o];h=!0}if(p.indexOf("#")>=0)if(0===p.indexOf("/"))u=p.split("#"),s=e.split("//"),l=s[1].split("/"),e=s[0]+"//"+l[0]+u[0],a=u[1];else{if(u=p.split("#"),""!==u[0]){if(l=e.split("/"),l=l.slice(0,l.length-1),!h){e="";for(var f=0;f<l.length;f++)f>0&&(e+="/"),e+=l[f]}e+="/"+p.split("#")[0]}a=u[1]}if(0===p.indexOf("http"))p.indexOf("#")>=0?(e=p.split("#")[0],a=p.split("#")[1]):(e=p,a=""),i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a});else if(0===p.indexOf("#"))a=p.split("#")[1],i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a});else if(0===p.indexOf("/")&&-1===p.indexOf("#")){a=p;var d=e.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);d&&(e=d[0]+p.substring(1),a=""),i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a})}else i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a})}else"array"===n.type&&this.resolveTo(e,n.items,i,a)},a.prototype.resolveTo=function(e,t,n,i){var a,o,s=t.$ref,l=e;if("undefined"!=typeof s&&null!==s){if(s.indexOf("#")>=0){var u=s.split("#");if(u[0]&&0===s.indexOf("/"));else if(u[0]&&0===u[0].indexOf("http"))l=u[0],s=u[1];else if(u[0]&&u[0].length>0){for(a=e.split("/"),l="",o=0;o<a.length-1;o++)l+=a[o]+"/";l+=u[0]}i=u[1]}else if(0===s.indexOf("http://")||0===s.indexOf("https://"))l=s,i="";else{for(a=e.split("/"),l="",o=0;o<a.length-1;o++)l+=a[o]+"/";l+=s,i=""}n.push({obj:t,resolveAs:"ref",root:l,key:s,location:i})}else if("array"===t.type){var c=t.items;this.resolveTo(e,c,n,i)}else if(t&&t.properties){var p=this.uniqueName("inline_model");t.title&&(p=this.uniqueName(t.title)),delete t.title,this.spec.definitions[p]=r.cloneDeep(t),t.$ref="#/definitions/"+p,delete t.type,delete t.properties}},a.prototype.uniqueName=function(e){for(var t=e,n=0;;){if(!r.isObject(this.spec.definitions[t]))return t;t=e+"_"+n,n++}},a.prototype.resolveAllOf=function(e,t,n){n=n||0,t=t||e;var i;for(var a in t)if(t.hasOwnProperty(a)){var o=t[a];if(null===o)throw new TypeError("Swagger 2.0 does not support null types ("+t+").  See https://github.com/swagger-api/swagger-spec/issues/229.");if("object"==typeof o&&this.resolveAllOf(e,o,n+1),o&&"undefined"!=typeof o.allOf){var s=o.allOf;if(r.isArray(s)){var l=r.cloneDeep(o);delete l.allOf,l["x-composed"]=!0,"undefined"!=typeof o["x-resolved-from"]&&(l["x-resolved-from"]=o["x-resolved-from"]);for(var u=0;u<s.length;u++){var c=s[u],p="self";"undefined"!=typeof c["x-resolved-from"]&&(p=c["x-resolved-from"][0]);for(var h in c)if(l.hasOwnProperty(h))if("properties"===h){var f=c[h];for(i in f){l.properties[i]=r.cloneDeep(f[i]);var d=f[i]["x-resolved-from"];"undefined"!=typeof d&&"self"!==d||(d=p),l.properties[i]["x-resolved-from"]=d}}else if("required"===h){for(var m=l.required.concat(c[h]),y=0;y<m.length;++y)for(var g=y+1;g<m.length;++g)m[y]===m[g]&&m.splice(g--,1);l.required=m}else"x-resolved-from"===h&&l["x-resolved-from"].push(p);else if(l[h]=r.cloneDeep(c[h]),"properties"===h)for(i in l[h])l[h][i]["x-resolved-from"]=p}t[a]=l}}}}},{"./http":5,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isObject":144}],7:[function(e,t,n){"use strict";function i(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"}function r(e,t){var n;return"integer"===e&&"int32"===t?n="integer":"integer"===e&&"int64"===t?n="long":"integer"===e&&"undefined"==typeof t?n="long":"string"===e&&"date-time"===t?n="date-time":"string"===e&&"date"===t?n="date":"number"===e&&"float"===t?n="float":"number"===e&&"double"===t?n="double":"number"===e&&"undefined"==typeof t?n="double":"boolean"===e?n="boolean":"string"===e&&(n="string"),n}function a(e,t){var n="";return"undefined"!=typeof e.$ref?n+=l.simpleRef(e.$ref):"undefined"==typeof e.type?n+="object":"array"===e.type?t?n+=a(e.items||e.$ref||{}):(n+="Array[",n+=a(e.items||e.$ref||{}),n+="]"):n+="integer"===e.type&&"int32"===e.format?"integer":"integer"===e.type&&"int64"===e.format?"long":"integer"===e.type&&"undefined"==typeof e.format?"long":"string"===e.type&&"date-time"===e.format?"date-time":"string"===e.type&&"date"===e.format?"date":"string"===e.type&&"undefined"==typeof e.format?"string":"number"===e.type&&"float"===e.format?"float":"number"===e.type&&"double"===e.format?"double":"number"===e.type&&"undefined"==typeof e.format?"double":"boolean"===e.type?"boolean":e.$ref?l.simpleRef(e.$ref):e.type,n}function o(e,t,n,i){e=l.resolveSchema(e),"function"!=typeof i&&(i=function(e){return(e||{})["default"]}),n=n||{};var r,a,s=e.type||"object",c=e.format;return u.isUndefined(e.example)?u.isUndefined(e.items)&&u.isArray(e["enum"])&&(a=e["enum"][0]):a=e.example,u.isUndefined(a)&&(e.$ref?(r=t[l.simpleRef(e.$ref)],u.isUndefined(r)||(u.isUndefined(n[r.name])?(n[r.name]=r,a=o(r.definition,t,n,i),delete n[r.name]):a="array"===r.type?[]:{})):u.isUndefined(e["default"])?"string"===s?a="date-time"===c?(new Date).toISOString():"date"===c?(new Date).toISOString().split("T")[0]:"string":"integer"===s?a=0:"number"===s?a=0:"boolean"===s?a=!0:"object"===s?(a={},u.forEach(e.properties,function(e,r){var s=u.cloneDeep(e);s["default"]=i(e),a[r]=o(s,t,n,i)})):"array"===s&&(a=[],u.isArray(e.items)?u.forEach(e.items,function(e){a.push(o(e,t,n,i))}):u.isPlainObject(e.items)?a.push(o(e.items,t,n,i)):u.isUndefined(e.items)?a.push({}):l.log("Array type's 'items' property is not an array or an object, cannot process")):a=e["default"]),a}function s(e,t,n,r){function a(e,t,i){var r,a=t;return e.$ref?(a=e.title||l.simpleRef(e.$ref),r=n[a]):u.isUndefined(t)&&(a=e.title||"Inline Model "+ ++m,r={definition:e}),i!==!0&&(f[a]=u.isUndefined(r)?{}:r.definition),a}function o(e){var t='<span class="propType">',n=e.type||"object";return e.$ref?t+=a(e,l.simpleRef(e.$ref)):"object"===n?t+=u.isUndefined(e.properties)?"object":a(e):"array"===n?(t+="Array[",u.isArray(e.items)?t+=u.map(e.items,a).join(","):u.isPlainObject(e.items)?t+=u.isUndefined(e.items.$ref)?u.isUndefined(e.items.type)||-1!==u.indexOf(["array","object"],e.items.type)?a(e.items):e.items.type:a(e.items,l.simpleRef(e.items.$ref)):(l.log("Array type's 'items' schema is not an array or an object, cannot process"),t+="object"),t+="]"):t+=e.type,t+="</span>"}function s(e,t){var n="",r=e.type||"object",a="array"===r;switch(a&&(r=u.isPlainObject(e.items)&&!u.isUndefined(e.items.type)?e.items.type:"object"),u.isUndefined(e["default"])||(n+=i("Default",e["default"])),r){case"string":e.minLength&&(n+=i("Min. Length",e.minLength)),e.maxLength&&(n+=i("Max. Length",e.maxLength)),e.pattern&&(n+=i("Reg. Exp.",e.pattern));break;case"integer":case"number":e.minimum&&(n+=i("Min. Value",e.minimum)),e.exclusiveMinimum&&(n+=i("Exclusive Min.","true")),e.maximum&&(n+=i("Max. Value",e.maximum)),e.exclusiveMaximum&&(n+=i("Exclusive Max.","true")),e.multipleOf&&(n+=i("Multiple Of",e.multipleOf))}if(a&&(e.minItems&&(n+=i("Min. Items",e.minItems)),e.maxItems&&(n+=i("Max. Items",e.maxItems)),e.uniqueItems&&(n+=i("Unique Items","true")),e.collectionFormat&&(n+=i("Coll. Format",e.collectionFormat))),u.isUndefined(e.items)&&u.isArray(e["enum"])){var o;o="number"===r||"integer"===r?e["enum"].join(", "):'"'+e["enum"].join('", "')+'"',n+=i("Enum",o)}return n.length>0&&(t='<span class="propWrap">'+t+'<table class="optionsWrapper"><tr><th colspan="2">'+r+"</th></tr>"+n+"</table></span>"),t}function c(e,t){var i=e.type||"object",c="array"===e.type,f=p+t+" "+(c?"[":"{")+h;if(t&&d.push(t),c)u.isArray(e.items)?f+="<div>"+u.map(e.items,function(e){var t=e.type||"object";return u.isUndefined(e.$ref)?u.indexOf(["array","object"],t)>-1?"object"===t&&u.isUndefined(e.properties)?"object":a(e):s(e,t):a(e,l.simpleRef(e.$ref))}).join(",</div><div>"):u.isPlainObject(e.items)?f+=u.isUndefined(e.items.$ref)?u.indexOf(["array","object"],e.items.type||"object")>-1?(u.isUndefined(e.items.type)||"object"===e.items.type)&&u.isUndefined(e.items.properties)?"<div>object</div>":"<div>"+a(e.items)+"</div>":"<div>"+s(e.items,e.items.type)+"</div>":"<div>"+a(e.items,l.simpleRef(e.items.$ref))+"</div>":(l.log("Array type's 'items' property is not an array or an object, cannot process"),f+="<div>object</div>");else if(e.$ref)f+="<div>"+a(e,t)+"</div>";else if("object"===i){if(u.isPlainObject(e.properties)){var m=u.map(e.properties,function(t,i){var a,c,p=u.indexOf(e.required,i)>=0,h=u.cloneDeep(t),f=p?"required":"",d='<span class="propName '+f+'">'+i+"</span> (";return h["default"]=r(h),h=l.resolveSchema(h),c=t.description||h.description,u.isUndefined(h.$ref)||(a=n[l.simpleRef(h.$ref)],u.isUndefined(a)||-1!==u.indexOf([void 0,"array","object"],a.definition.type)||(h=l.resolveSchema(a.definition))),d+=o(h),p||(d+=', <span class="propOptKey">optional</span>'),t.readOnly&&(d+=', <span class="propReadOnly">read only</span>'),d+=")",u.isUndefined(c)||(d+=': <span class="propDesc">'+c+"</span>"),h["enum"]&&(d+=' = <span class="propVals">[\''+h["enum"].join("', '")+"']</span>"),"<div"+(t.readOnly?' class="readOnly"':"")+">"+s(h,d)}).join(",</div>");m&&(f+=m+"</div>")}}else f+="<div>"+s(e,i)+"</div>";return f+p+(c?"]":"}")+h}var p='<span class="strong">',h="</span>";if(u.isObject(arguments[0])&&(e=void 0,t=arguments[0],n=arguments[1],r=arguments[2]),n=n||{},t=l.resolveSchema(t),u.isEmpty(t))return p+"Empty"+h;if("string"==typeof t.$ref&&(e=l.simpleRef(t.$ref),t=n[e],"undefined"==typeof t))return p+e+" is not defined!"+h;"string"!=typeof e&&(e=t.title||"Inline Model"),t.definition&&(t=t.definition),"function"!=typeof r&&(r=function(e){return(e||{})["default"]});for(var f={},d=[],m=0,y=c(t,e);u.keys(f).length>0;)u.forEach(f,function(e,t){var n=u.indexOf(d,t)>-1;delete f[t],n||(d.push(t),y+="<br />"+c(e,t))});return y}var l=e("./helpers"),u={isPlainObject:e("lodash-compat/lang/isPlainObject"),isUndefined:e("lodash-compat/lang/isUndefined"),isArray:e("lodash-compat/lang/isArray"),isObject:e("lodash-compat/lang/isObject"),isEmpty:e("lodash-compat/lang/isEmpty"),map:e("lodash-compat/collection/map"),indexOf:e("lodash-compat/array/indexOf"),cloneDeep:e("lodash-compat/lang/cloneDeep"),keys:e("lodash-compat/object/keys"),forEach:e("lodash-compat/collection/forEach")};t.exports.optionHtml=i,t.exports.typeFromJsonSchema=r,t.exports.getStringSignature=a,t.exports.schemaToHTML=s,t.exports.schemaToJSON=o},{"./helpers":4,"lodash-compat/array/indexOf":49,"lodash-compat/collection/forEach":54,"lodash-compat/collection/map":56,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isEmpty":141,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isUndefined":148,"lodash-compat/object/keys":149}],8:[function(e,t,n){"use strict";var i=e("./http"),r={isObject:e("lodash-compat/lang/isObject")},a=t.exports=function(){this.errors=[],this.warnings=[],this.modelMap={}};a.prototype.setDocumentationLocation=function(e){this.docLocation=e},a.prototype.convert=function(e,t,n,i){if(!e||!Array.isArray(e.apis))return this.finish(i,null);this.clientAuthorizations=t;var r={swagger:"2.0"};r.originalVersion=e.swaggerVersion,this.apiInfo(e,r),this.securityDefinitions(e,r),e.basePath&&this.setDocumentationLocation(e.basePath);var a,o=!1;for(a=0;a<e.apis.length;a++){var s=e.apis[a];Array.isArray(s.operations)&&(o=!0)}o?(this.declaration(e,r),this.finish(i,r)):this.resourceListing(e,r,n,i)},a.prototype.declaration=function(e,t){var n,i,a,o;if(e.apis){0===e.basePath.indexOf("http://")?(a=e.basePath.substring("http://".length),o=a.indexOf("/"),o>0?(t.host=a.substring(0,o),t.basePath=a.substring(o)):(t.host=a,t.basePath="/")):0===e.basePath.indexOf("https://")?(a=e.basePath.substring("https://".length),o=a.indexOf("/"),o>0?(t.host=a.substring(0,o),t.basePath=a.substring(o)):(t.host=a,t.basePath="/")):t.basePath=e.basePath;var s;if(e.authorizations&&(s=e.authorizations),e.consumes&&(t.consumes=e.consumes),e.produces&&(t.produces=e.produces),r.isObject(e))for(n in e.models){var l=e.models[n],u=l.id||n;this.modelMap[u]=n}for(i=0;i<e.apis.length;i++){var c=e.apis[i],p=c.path,h=c.operations;this.operations(p,e.resourcePath,h,s,t)}var f=e.models||{};this.models(f,t)}},a.prototype.models=function(e,t){if(r.isObject(e)){var n;t.definitions=t.definitions||{};for(n in e){var i,a=e[n],o=[],s={properties:{}};for(i in a.properties){var l=a.properties[i],u={};this.dataType(l,u),l.description&&(u.description=l.description),l["enum"]&&(u["enum"]=l["enum"]),"boolean"==typeof l.required&&l.required===!0&&o.push(i),"string"==typeof l.required&&"true"===l.required&&o.push(i),s.properties[i]=u}o.length>0?s.required=o:s.required=a.required,t.definitions[n]=s}}},a.prototype.extractTag=function(e){var t=e||"default";return 0!==t.indexOf("http:")&&0!==t.indexOf("https:")||(t=t.split(["/"]),t=t[t.length-1].substring()),t.endsWith(".json")&&(t=t.substring(0,t.length-".json".length)),t.replace("/","")},a.prototype.operations=function(e,t,n,i,r){if(Array.isArray(n)){var a;r.paths||(r.paths={});var o=r.paths[e]||{},s=this.extractTag(t);r.tags=r.tags||[];var l=!1;for(a=0;a<r.tags.length;a++){var u=r.tags[a];u.name===s&&(l=!0)}for(l||r.tags.push({name:s}),a=0;a<n.length;a++){var c=n[a],p=(c.method||c.httpMethod).toLowerCase(),h={tags:[s]},f=c.authorizations;if(f&&0===Object.keys(f).length&&(f=i),"undefined"!=typeof f){var d;for(var m in f){h.security=h.security||[];var y=f[m];if(y){var g=[];for(var v in y)g.push(y[v].scope);d={},d[m]=g,h.security.push(d)}else d={},d[m]=[],h.security.push(d)}}c.consumes?h.consumes=c.consumes:r.consumes&&(h.consumes=r.consumes),c.produces?h.produces=c.produces:r.produces&&(h.produces=r.produces),c.summary&&(h.summary=c.summary),c.notes&&(h.description=c.notes),c.nickname&&(h.operationId=c.nickname),c.deprecated&&(h.deprecated=c.deprecated),this.authorizations(f,r),this.parameters(h,c.parameters,r),this.responseMessages(h,c,r),o[p]=h}r.paths[e]=o}},a.prototype.responseMessages=function(e,t){if(r.isObject(t)){var n={};this.dataType(t,n),!n.schema&&n.type&&(n={schema:n}),e.responses=e.responses||{};var i=!1;if(Array.isArray(t.responseMessages)){var a,o=t.responseMessages;for(a=0;a<o.length;a++){var s=o[a],l={description:s.message};200===s.code&&(i=!0),s.responseModel&&(l.schema={$ref:"#/definitions/"+s.responseModel}),e.responses[""+s.code]=l}}i?e.responses["default"]=n:e.responses[200]=n}},a.prototype.authorizations=function(e){!r.isObject(e)},a.prototype.parameters=function(e,t){if(Array.isArray(t)){var n;for(n=0;n<t.length;n++){var i=t[n],r={};if(r.name=i.name,r.description=i.description,r.required=i.required,r["in"]=i.paramType,"body"===r["in"]&&(r.name="body"),"form"===r["in"]&&(r["in"]="formData"),i["enum"]&&(r["enum"]=i["enum"]),i.allowMultiple===!0||"true"===i.allowMultiple){var a={};if(this.dataType(i,a),r.type="array",r.items=a,i.allowableValues){var o=i.allowableValues;"LIST"===o.valueType&&(r["enum"]=o.values)}}else this.dataType(i,r);"undefined"!=typeof i.defaultValue&&(r["default"]=i.defaultValue),e.parameters=e.parameters||[],e.parameters.push(r)}}},a.prototype.dataType=function(e,t){if(r.isObject(e)){e.minimum&&(t.minimum=e.minimum),e.maximum&&(t.maximum=e.maximum),e.format&&(t.format=e.format),"undefined"!=typeof e.defaultValue&&(t["default"]=e.defaultValue);var n=this.toJsonSchema(e);n&&(t=t||{},n.type&&(t.type=n.type),n.format&&(t.format=n.format),n.$ref&&(t.schema={$ref:n.$ref}),n.items&&(t.items=n.items))}},a.prototype.toJsonSchema=function(e){if(!e)return"object";var t=e.type||e.dataType||e.responseClass||"",n=t.toLowerCase(),i=(e.format||"").toLowerCase();if(0===n.indexOf("list[")){var r=t.substring(5,t.length-1),a=this.toJsonSchema({type:r});return{type:"array",items:a}}if("int"===n||"integer"===n&&"int32"===i)return{type:"integer",format:"int32"};if("long"===n||"integer"===n&&"int64"===i)return{type:"integer",format:"int64"};if("integer"===n)return{type:"integer",format:"int64"};if("float"===n||"number"===n&&"float"===i)return{type:"number",format:"float"};if("double"===n||"number"===n&&"double"===i)return{type:"number",format:"double"};if("string"===n&&"date-time"===i||"date"===n)return{type:"string",format:"date-time"};if("string"===n)return{type:"string"};if("file"===n)return{type:"file"};if("boolean"===n)return{type:"boolean"};if("boolean"===n)return{type:"boolean"};if("array"===n||"list"===n){if(e.items){var o=this.toJsonSchema(e.items);return{type:"array",items:o}}return{type:"array",items:{type:"object"}}}return e.$ref?{$ref:this.modelMap[e.$ref]?"#/definitions/"+this.modelMap[e.$ref]:e.$ref}:"void"===n||""===n?{}:this.modelMap[e.type]?{$ref:"#/definitions/"+this.modelMap[e.type]}:{type:e.type}},a.prototype.resourceListing=function(e,t,n,r){var a,o=0,s=this,l=e.apis.length,u=t,c={};n&&n.requestInterceptor&&(c.requestInterceptor=n.requestInterceptor),n&&n.responseInterceptor&&(c.responseInterceptor=n.responseInterceptor);var p="application/json";for(n&&n.swaggerRequestHeaders&&(p=n.swaggerRequestHeaders),0===l&&this.finish(r,t),a=0;l>a;a++){var h=e.apis[a],f=h.path,d=this.getAbsolutePath(e.swaggerVersion,this.docLocation,f);h.description&&(t.tags=t.tags||[],t.tags.push({name:this.extractTag(h.path),description:h.description||""}));var m={url:d,headers:{accept:p},on:{},method:"get"};m.on.response=function(e){o+=1;var t=e.obj;t&&s.declaration(t,u),o===l&&s.finish(r,u)},m.on.error=function(e){console.error(e),o+=1,o===l&&s.finish(r,u)},this.clientAuthorizations&&"function"==typeof this.clientAuthorizations.apply&&this.clientAuthorizations.apply(m),(new i).execute(m,c)}},a.prototype.getAbsolutePath=function(e,t,n){if("1.0"===e&&t.endsWith(".json")){var i=t.lastIndexOf("/");i>0&&(t=t.substring(0,i))}var r=t;return 0===n.indexOf("http://")||0===n.indexOf("https://")?r=n:(t.endsWith("/")&&(r=t.substring(0,t.length-1)),r+=n),r=r.replace("{format}","json")},a.prototype.securityDefinitions=function(e,t){if(e.authorizations){var n;for(n in e.authorizations){var i=!1,r={},a=e.authorizations[n];if("apiKey"===a.type)r.type="apiKey",r["in"]=a.passAs,r.name=a.keyname||n,i=!0;else if("basicAuth"===a.type)r.type="basicAuth",i=!0;else if("oauth2"===a.type){var o,s=a.scopes||[],l={};for(o in s){var u=s[o];l[u.scope]=u.description}if(r.type="oauth2",o>0&&(r.scopes=l),a.grantTypes){if(a.grantTypes.implicit){var c=a.grantTypes.implicit;r.flow="implicit",r.authorizationUrl=c.loginEndpoint,i=!0}if(a.grantTypes.authorization_code&&!r.flow){var p=a.grantTypes.authorization_code;r.flow="accessCode",r.authorizationUrl=p.tokenRequestEndpoint.url,r.tokenUrl=p.tokenEndpoint.url,i=!0}}}i&&(t.securityDefinitions=t.securityDefinitions||{},t.securityDefinitions[n]=r)}}},a.prototype.apiInfo=function(e,t){if(e.info){var n=e.info;t.info={},n.contact&&(t.info.contact={},t.info.contact.email=n.contact),n.description&&(t.info.description=n.description),n.title&&(t.info.title=n.title),n.termsOfServiceUrl&&(t.info.termsOfService=n.termsOfServiceUrl),(n.license||n.licenseUrl)&&(t.license={},n.license&&(t.license.name=n.license),n.licenseUrl&&(t.license.url=n.licenseUrl))}else this.warnings.push("missing info section")},a.prototype.finish=function(e,t){e(t)}},{"./http":5,"lodash-compat/lang/isObject":144}],9:[function(e,t,n){"use strict";var i=(e("../helpers").log,{isPlainObject:e("lodash-compat/lang/isPlainObject"),isString:e("lodash-compat/lang/isString")}),r=e("../schema-markup.js"),a=e("js-yaml"),o=t.exports=function(e,t,n,i){return this.definition=t||{},this.isArray="array"===t.type,this.models=n||{},this.name=e||t.title||"Inline Model",this.modelPropertyMacro=i||function(e){return e["default"]},this};o.prototype.createJSONSample=o.prototype.getSampleValue=function(e){return e=e||{},e[this.name]=this,this.examples&&i.isPlainObject(this.examples)&&this.examples["application/json"]?(this.definition.example=this.examples["application/json"],
+i.isString(this.definition.example)&&(this.definition.example=a.safeLoad(this.definition.example))):this.definition.example||(this.definition.example=this.examples),r.schemaToJSON(this.definition,this.models,e,this.modelPropertyMacro)},o.prototype.getMockSignature=function(){return r.schemaToHTML(this.name,this.definition,this.models,this.modelPropertyMacro)}},{"../helpers":4,"../schema-markup.js":7,"js-yaml":19,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isString":146}],10:[function(e,t,n){"use strict";function i(e,t){if(r.isEmpty(t))return e[0];for(var n=0,i=t.length;i>n;n++)if(e.indexOf(t[n])>-1)return t[n];return e[0]}var r={cloneDeep:e("lodash-compat/lang/cloneDeep"),isUndefined:e("lodash-compat/lang/isUndefined"),isEmpty:e("lodash-compat/lang/isEmpty"),isObject:e("lodash-compat/lang/isObject")},a=e("../helpers"),o=e("./model"),s=e("../http"),l=e("q"),u=t.exports=function(e,t,n,i,r,a,s,l,u){var c=[];if(e=e||{},a=a||{},e&&e.options&&(this.client=e.options.client||null,this.requestInterceptor=e.options.requestInterceptor||null,this.responseInterceptor=e.options.responseInterceptor||null),this.authorizations=a.security,this.basePath=e.basePath||"/",this.clientAuthorizations=u,this.consumes=a.consumes||e.consumes||["application/json"],this.produces=a.produces||e.produces||["application/json"],this.deprecated=a.deprecated,this.description=a.description,this.host=e.host||"localhost",this.method=i||c.push("Operation "+n+" is missing method."),this.models=l||{},this.nickname=n||c.push("Operations must have a nickname."),this.operation=a,this.operations={},this.parameters=null!==a?a.parameters||[]:{},this.parent=e,this.path=r||c.push("Operation "+this.nickname+" is missing path."),this.responses=a.responses||{},this.scheme=t||e.scheme||"http",this.schemes=a.schemes||e.schemes,this.security=a.security||e.security,this.summary=a.summary||"",this.type=null,this.useJQuery=e.useJQuery,this.jqueryAjaxCache=e.jqueryAjaxCache,this.enableCookies=e.enableCookies,this.parameterMacro=e.parameterMacro||function(e,t){return t["default"]},this.inlineModels=[],"/"!==this.basePath&&"/"==this.basePath.slice(-1)&&(this.basePath=this.basePath.slice(0,-1)),"string"==typeof this.deprecated)switch(this.deprecated.toLowerCase()){case"true":case"yes":case"1":this.deprecated=!0;break;case"false":case"no":case"0":case null:this.deprecated=!1;break;default:this.deprecated=Boolean(this.deprecated)}var p,h;if(s){var f;for(f in s)h=new o(f,s[f],this.models,e.modelPropertyMacro),h&&(this.models[f]=h)}else s={};for(p=0;p<this.parameters.length;p++){var d=this.parameters[p];d["default"]=this.parameterMacro(this,d),"array"===d.type&&(d.isList=!0,d.allowMultiple=!0);var m=this.getType(d);if(m&&"boolean"===m.toString().toLowerCase()&&(d.allowableValues={},d.isList=!0,d["enum"]=[!0,!1]),"undefined"!=typeof d["x-example"]){var y=d["x-example"];d["default"]=y}if(d["x-examples"]){var y=d["x-examples"]["default"];"undefined"!=typeof y&&(d["default"]=y)}var g=d["enum"]||d.items&&d.items["enum"];if("undefined"!=typeof g){var v;for(d.allowableValues={},d.allowableValues.values=[],d.allowableValues.descriptiveValues=[],v=0;v<g.length;v++){var b=g[v],w=b===d["default"]||b+""===d["default"];d.allowableValues.values.push(b),d.allowableValues.descriptiveValues.push({value:b+"",isDefault:w})}}"array"===d.type&&(m=[m],"undefined"==typeof d.allowableValues&&(delete d.isList,delete d.allowMultiple)),d.modelSignature={type:m,definitions:this.models},d.signature=this.getModelSignature(m,this.models).toString(),d.sampleJSON=this.getModelSampleJSON(m,this.models),d.responseClassSignature=d.signature}var x,A,j=this.responses;if(j[200]?(A=j[200],x="200"):j[201]?(A=j[201],x="201"):j[202]?(A=j[202],x="202"):j[203]?(A=j[203],x="203"):j[204]?(A=j[204],x="204"):j[205]?(A=j[205],x="205"):j[206]?(A=j[206],x="206"):j["default"]&&(A=j["default"],x="default"),A&&A.schema){var O,_=this.resolveModel(A.schema,s);delete j[x],_?(this.successResponse={},O=this.successResponse[x]=_):A.schema.type&&"object"!==A.schema.type&&"array"!==A.schema.type?(this.successResponse={},O=this.successResponse[x]=A.schema):(this.successResponse={},O=this.successResponse[x]=new o(void 0,A.schema||{},this.models,e.modelPropertyMacro)),O&&(A.description&&(O.description=A.description),A.examples&&(O.examples=A.examples),A.headers&&(O.headers=A.headers)),this.type=A}return c.length>0&&this.resource&&this.resource.api&&this.resource.api.fail&&this.resource.api.fail(c),this};u.prototype.isDefaultArrayItemValue=function(e,t){return t["default"]&&Array.isArray(t["default"])?-1!==t["default"].indexOf(e):e===t["default"]},u.prototype.getType=function(e){var t,n=e.type,i=e.format,r=!1;"integer"===n&&"int32"===i?t="integer":"integer"===n&&"int64"===i?t="long":"integer"===n?t="integer":"string"===n?t="date-time"===i?"date-time":"date"===i?"date":"string":"number"===n&&"float"===i?t="float":"number"===n&&"double"===i?t="double":"number"===n?t="double":"boolean"===n?t="boolean":"array"===n?(r=!0,e.items&&(t=this.getType(e.items))):"file"===n&&(t="file"),e.$ref&&(t=a.simpleRef(e.$ref));var o=e.schema;if(o){var s=o.$ref;return s?(s=a.simpleRef(s),r?[s]:s):"object"===o.type?this.addInlineModel(o):this.getType(o)}return r?[t]:t},u.prototype.addInlineModel=function(e){var t=this.inlineModels.length,n=this.resolveModel(e,{});return n?(this.inlineModels.push(n),"Inline Model "+t):null},u.prototype.getInlineModel=function(e){if(/^Inline Model \d+$/.test(e)){var t=parseInt(e.substr("Inline Model".length).trim(),10),n=this.inlineModels[t];return n}return null},u.prototype.resolveModel=function(e,t){if("undefined"!=typeof e.$ref){var n=e.$ref;if(0===n.indexOf("#/definitions/")&&(n=n.substring("#/definitions/".length)),t[n])return new o(n,t[n],this.models,this.parent.modelPropertyMacro)}else if(e&&"object"==typeof e&&("object"===e.type||r.isUndefined(e.type)))return new o(void 0,e,this.models,this.parent.modelPropertyMacro);return null},u.prototype.help=function(e){for(var t=this.nickname+": "+this.summary+"\n",n=0;n<this.parameters.length;n++){var i=this.parameters[n],r=i.signature;t+="\n  * "+i.name+" ("+r+"): "+i.description}return"undefined"==typeof e&&a.log(t),t},u.prototype.getModelSignature=function(e,t){var n,i;return e instanceof Array&&(i=!0,e=e[0]),"undefined"==typeof e?(e="undefined",n=!0):t[e]?(e=t[e],n=!1):this.getInlineModel(e)?(e=this.getInlineModel(e),n=!1):n=!0,n?i?"Array["+e+"]":e.toString():i?"Array["+e.getMockSignature()+"]":e.getMockSignature()},u.prototype.supportHeaderParams=function(){return!0},u.prototype.supportedSubmitMethods=function(){return this.parent.supportedSubmitMethods},u.prototype.getHeaderParams=function(e){for(var t=this.setContentTypes(e,{}),n=0;n<this.parameters.length;n++){var i=this.parameters[n];if("undefined"!=typeof e[i.name]&&"header"===i["in"]){var r=e[i.name];Array.isArray(r)&&(r=r.toString()),t[i.name]=r}}return t},u.prototype.urlify=function(e){for(var t={},n=this.path.replace(/#.*/,""),i="",r=0;r<this.parameters.length;r++){var a=this.parameters[r];if("undefined"!=typeof e[a.name])if("path"===a["in"]){var o=new RegExp("{"+a.name+"}","gi"),s=e[a.name];s=Array.isArray(s)?this.encodePathCollection(a.collectionFormat,a.name,s):this.encodePathParam(s),n=n.replace(o,s)}else if("query"===a["in"]&&"undefined"!=typeof e[a.name])if(i+=""===i&&n.indexOf("?")<0?"?":"&","undefined"!=typeof a.collectionFormat){var l=e[a.name];i+=Array.isArray(l)?this.encodeQueryCollection(a.collectionFormat,a.name,l):this.encodeQueryKey(a.name)+"="+this.encodeQueryParam(e[a.name])}else i+=this.encodeQueryKey(a.name)+"="+this.encodeQueryParam(e[a.name]);else"formData"===a["in"]&&(t[a.name]=e[a.name])}var u=this.scheme+"://"+this.host;return"/"!==this.basePath&&(u+=this.basePath),u+n+i},u.prototype.getMissingParams=function(e){var t,n=[];for(t=0;t<this.parameters.length;t++){var i=this.parameters[t];i.required===!0&&"undefined"==typeof e[i.name]&&(n=i.name)}return n},u.prototype.getBody=function(e,t,n){for(var i,r,a,o,s={},l=!1,u=0;u<this.parameters.length;u++){var c=this.parameters[u];"undefined"!=typeof t[c.name]?"body"===c["in"]?r=t[c.name]:"formData"===c["in"]&&(s[c.name]={param:c,value:t[c.name]},i=!0):"body"===c["in"]&&(l=!0)}if(l&&"undefined"==typeof r){var p=e["Content-Type"];p&&0===p.indexOf("application/json")&&(r="{}")}var h=!1;if(e["Content-Type"]&&e["Content-Type"].indexOf("multipart/form-data")>=0&&(h=!0),i&&!h){var f="";for(a in s){var c=s[a].param;o=s[a].value,"undefined"!=typeof o&&(Array.isArray(o)?(""!==f&&(f+="&"),f+=this.encodeQueryCollection(c.collectionFormat,a,o)):(""!==f&&(f+="&"),f+=encodeURIComponent(a)+"="+encodeURIComponent(o)))}r=f}else if(h){if("function"==typeof FormData){var d=new FormData;d.type="formData";for(a in s)o=t[a],"undefined"!=typeof o&&("[object File]"==={}.toString.apply(o)?d.append(a,o):"file"===o.type&&o.value?d.append(a,o.value):Array.isArray(o)?d.append(a,this.encodeQueryCollection(c.collectionFormat,a,o)):d.append(a,o));r=d}else{d={};for(a in s)if(o=t[a],Array.isArray(o)){var m,y=c.collectionFormat||"multi";m="ssv"===y?" ":"pipes"===y?"|":"tsv"===y?"	":",";var g;o.forEach(function(e){g?g+=m:g="",g+=e}),d[a]=g}else d[a]=o;r=d}e["Content-Type"]="multipart/form-data"}return r},u.prototype.getModelSampleJSON=function(e,t){var n,i,a;if(t=t||{},n=e instanceof Array,a=n?e[0]:e,t[a]?i=t[a].createJSONSample():this.getInlineModel(a)&&(i=this.getInlineModel(a).createJSONSample()),i){if(i=n?[i]:i,"string"==typeof i)return i;if(r.isObject(i)){var o=i;if(i instanceof Array&&i.length>0&&(o=i[0]),o.nodeName&&"Node"==typeof o){var s=(new XMLSerializer).serializeToString(o);return this.formatXml(s)}return JSON.stringify(i,null,2)}return i}},u.prototype["do"]=function(e,t,n,i,r){return this.execute(e,t,n,i,r)},u.prototype.execute=function(e,t,n,i,o){var u,c,p,h=e||{},f={};r.isObject(t)&&(f=t,u=n,c=i),this.client&&(f.client=this.client),!f.requestInterceptor&&this.requestInterceptor&&(f.requestInterceptor=this.requestInterceptor),!f.responseInterceptor&&this.responseInterceptor&&(f.responseInterceptor=this.responseInterceptor),"function"==typeof t&&(u=t,c=n),this.parent.usePromise?p=l.defer():(u=u||this.parent.defaultSuccessCallback||a.log,c=c||this.parent.defaultErrorCallback||a.log),"undefined"==typeof f.useJQuery&&(f.useJQuery=this.useJQuery),"undefined"==typeof f.jqueryAjaxCache&&(f.jqueryAjaxCache=this.jqueryAjaxCache),"undefined"==typeof f.enableCookies&&(f.enableCookies=this.enableCookies);var d=this.getMissingParams(h);if(d.length>0){var m="missing required params: "+d;return a.fail(m),this.parent.usePromise?(p.reject(m),p.promise):(c(m,o),{})}var y,g=this.getHeaderParams(h),v=this.setContentTypes(h,f),b={};for(y in g)b[y]=g[y];for(y in v)b[y]=v[y];var w=this.getBody(v,h,f),x=this.urlify(h);if(x.indexOf(".{format}")>0&&b){var A=b.Accept||b.accept;A&&A.indexOf("json")>0?x=x.replace(".{format}",".json"):A&&A.indexOf("xml")>0&&(x=x.replace(".{format}",".xml"))}var j={url:x,method:this.method.toUpperCase(),body:w,enableCookies:f.enableCookies,useJQuery:f.useJQuery,jqueryAjaxCache:f.jqueryAjaxCache,deferred:p,headers:b,clientAuthorizations:f.clientAuthorizations,on:{response:function(e){return p?(p.resolve(e),p.promise):u(e,o)},error:function(e){return p?(p.reject(e),p.promise):c(e,o)}}};return this.clientAuthorizations.apply(j,this.operation.security),f.mock===!0?j:(new s).execute(j,f)},u.prototype.setContentTypes=function(e,t){var n,r,o=this.parameters,s=e.parameterContentType||i(this.consumes,["application/json","application/yaml"]),l=t.responseContentType||i(this.produces,["application/json","application/yaml"]),u=[],c=[],p={};for(r=0;r<o.length;r++){var h=o[r];if("formData"===h["in"])"file"===h.type?u.push(h):c.push(h);else if("header"===h["in"]&&t){var f=h.name,d=t[h.name];"undefined"!=typeof t[h.name]&&(p[f]=d)}else"body"===h["in"]&&"undefined"!=typeof e[h.name]&&(n=e[h.name])}var m=n||u.length||c.length;if("post"===this.method||"put"===this.method||"patch"===this.method||("delete"===this.method||"get"===this.method)&&m){if(t.requestContentType&&(s=t.requestContentType),c.length>0){if(s=void 0,t.requestContentType)s=t.requestContentType;else if(u.length>0)s="multipart/form-data";else if(this.consumes&&this.consumes.length>0)for(var y in this.consumes){var g=this.consumes[y];0!==g.indexOf("application/x-www-form-urlencoded")&&0!==g.indexOf("multipart/form-data")||(s=g)}"undefined"==typeof s&&(s="application/x-www-form-urlencoded")}}else s=null;return s&&this.consumes&&-1===this.consumes.indexOf(s)&&a.log("server doesn't consume "+s+", try "+JSON.stringify(this.consumes)),this.matchesAccept(l)||a.log("server can't produce "+l),s&&""!==n||"application/x-www-form-urlencoded"===s?p["Content-Type"]=s:this.consumes&&this.consumes.length>0&&"application/x-www-form-urlencoded"===this.consumes[0]&&(p["Content-Type"]=this.consumes[0]),l&&(p.Accept=l),p},u.prototype.matchesAccept=function(e){return e&&this.produces?-1!==this.produces.indexOf(e)||-1!==this.produces.indexOf("*/*"):!0},u.prototype.asCurl=function(e,t){var n={mock:!0};if("object"==typeof t)for(var i in t)n[i]=t[i];var a=this.execute(e,n);this.clientAuthorizations.apply(a,this.operation.security);var o=[];if(o.push("-X "+this.method.toUpperCase()),"undefined"!=typeof a.headers){var s;for(s in a.headers){var l=a.headers[s];"string"==typeof l&&(l=l.replace(/\'/g,"\\u0027")),o.push("--header '"+s+": "+l+"'")}}var u=!1,c=!1,p=a.headers["Content-Type"];if(p&&0===p.indexOf("application/x-www-form-urlencoded")?u=!0:p&&0===p.indexOf("multipart/form-data")&&(u=!0,c=!0),a.body){var h;if(r.isObject(a.body)){if(c){c=!0;for(var f=0;f<this.parameters.length;f++){var d=this.parameters[f];if("formData"===d["in"]){h||(h="");var m;m="function"==typeof FormData&&a.body instanceof FormData?a.body.get(d.name):a.body[d.name],m&&("file"===d.type?m.name&&(h+="-F "+d.name+'=@"'+m.name+'" '):(h+="-F ",h+=Array.isArray(m)?this.encodeQueryCollection(d.collectionFormat,d.name,m):this.encodeQueryKey(d.name)+"="+m,h+=" "))}}}h||(h=JSON.stringify(a.body))}else h=a.body;h=h.replace(/\'/g,"%27").replace(/\n/g," \\ \n "),u||(h=h.replace(/&/g,"%26")),c?o.push(h):o.push("-d '"+h.replace(/@/g,"%40")+"'")}return"curl "+o.join(" ")+" '"+a.url+"'"},u.prototype.encodePathCollection=function(e,t,n){var i,r="",a="";for(a="ssv"===e?"%20":"tsv"===e?"%09":"pipes"===e?"|":",",i=0;i<n.length;i++)0===i?r=this.encodeQueryParam(n[i]):r+=a+this.encodeQueryParam(n[i]);return r},u.prototype.encodeQueryCollection=function(e,t,n){var i,r="";if(e=e||"default","default"===e||"multi"===e)for(i=0;i<n.length;i++)i>0&&(r+="&"),r+=this.encodeQueryKey(t)+"="+this.encodeQueryParam(n[i]);else{var a="";if("csv"===e)a=",";else if("ssv"===e)a="%20";else if("tsv"===e)a="%09";else if("pipes"===e)a="|";else if("brackets"===e)for(i=0;i<n.length;i++)0!==i&&(r+="&"),r+=this.encodeQueryKey(t)+"[]="+this.encodeQueryParam(n[i]);if(""!==a)for(i=0;i<n.length;i++)0===i?r=this.encodeQueryKey(t)+"="+this.encodeQueryParam(n[i]):r+=a+this.encodeQueryParam(n[i])}return r},u.prototype.encodeQueryKey=function(e){return encodeURIComponent(e).replace("%5B","[").replace("%5D","]").replace("%24","$")},u.prototype.encodeQueryParam=function(e){return encodeURIComponent(e)},u.prototype.encodePathParam=function(e){return encodeURIComponent(e)}},{"../helpers":4,"../http":5,"./model":9,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isEmpty":141,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isUndefined":148,q:157}],11:[function(e,t,n){"use strict";var i=t.exports=function(e,t,n,i){this.description=t,this.externalDocs=n,this.name=e,this.operation=i,this.operationsArray=[],this.path=e,this.tag=e};i.prototype.sort=function(){}},{}],12:[function(e,t,n){function i(){if(!s){s=!0;for(var e,t=o.length;t;){e=o,o=[];for(var n=-1;++n<t;)e[n]();t=o.length}s=!1}}function r(){}var a=t.exports={},o=[],s=!1;a.nextTick=function(e){o.push(e),s||setTimeout(i,0)},a.title="browser",a.browser=!0,a.env={},a.argv=[],a.version="",a.versions={},a.on=r,a.addListener=r,a.once=r,a.off=r,a.removeListener=r,a.removeAllListeners=r,a.emit=r,a.binding=function(e){throw new Error("process.binding is not supported")},a.cwd=function(){return"/"},a.chdir=function(e){throw new Error("process.chdir is not supported")},a.umask=function(){return 0}},{}],13:[function(e,t,n){(function(e){!function(){"use strict";function n(t){var n;return n=t instanceof e?t:new e(t.toString(),"binary"),n.toString("base64")}t.exports=n}()}).call(this,e("buffer").Buffer)},{buffer:14}],14:[function(e,t,n){function i(){return r.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function r(e){return this instanceof r?(this.length=0,this.parent=void 0,"number"==typeof e?a(this,e):"string"==typeof e?o(this,e,arguments.length>1?arguments[1]:"utf8"):s(this,e)):arguments.length>1?new r(e,arguments[1]):new r(e)}function a(e,t){if(e=d(e,0>t?0:0|m(t)),!r.TYPED_ARRAY_SUPPORT)for(var n=0;t>n;n++)e[n]=0;return e}function o(e,t,n){"string"==typeof n&&""!==n||(n="utf8");var i=0|g(t,n);return e=d(e,i),e.write(t,n),e}function s(e,t){if(r.isBuffer(t))return l(e,t);if(K(t))return u(e,t);if(null==t)throw new TypeError("must start with number, buffer, array or string");if("undefined"!=typeof ArrayBuffer){if(t.buffer instanceof ArrayBuffer)return c(e,t);if(t instanceof ArrayBuffer)return p(e,t)}return t.length?h(e,t):f(e,t)}function l(e,t){var n=0|m(t.length);return e=d(e,n),t.copy(e,0,0,n),e}function u(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function c(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function p(e,t){return r.TYPED_ARRAY_SUPPORT?(t.byteLength,e=r._augment(new Uint8Array(t))):e=c(e,new Uint8Array(t)),e}function h(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function f(e,t){var n,i=0;"Buffer"===t.type&&K(t.data)&&(n=t.data,i=0|m(n.length)),e=d(e,i);for(var r=0;i>r;r+=1)e[r]=255&n[r];return e}function d(e,t){r.TYPED_ARRAY_SUPPORT?e=r._augment(new Uint8Array(t)):(e.length=t,e._isBuffer=!0);var n=0!==t&&t<=r.poolSize>>>1;return n&&(e.parent=W),e}function m(e){if(e>=i())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i().toString(16)+" bytes");return 0|e}function y(e,t){if(!(this instanceof y))return new y(e,t);var n=new r(e,t);return delete n.parent,n}function g(e,t){"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var i=!1;;)switch(t){case"ascii":case"binary":case"raw":case"raws":return n;case"utf8":case"utf-8":return q(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return H(e).length;default:if(i)return q(e).length;t=(""+t).toLowerCase(),i=!0}}function v(e,t,n){var i=!1;if(t=0|t,n=void 0===n||n===1/0?this.length:0|n,e||(e="utf8"),0>t&&(t=0),n>this.length&&(n=this.length),t>=n)return"";for(;;)switch(e){case"hex":return I(this,t,n);case"utf8":case"utf-8":return S(this,t,n);case"ascii":return C(this,t,n);case"binary":return E(this,t,n);case"base64":return _(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(i)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),i=!0}}function b(e,t,n,i){n=Number(n)||0;var r=e.length-n;i?(i=Number(i),i>r&&(i=r)):i=r;var a=t.length;if(a%2!==0)throw new Error("Invalid hex string");i>a/2&&(i=a/2);for(var o=0;i>o;o++){var s=parseInt(t.substr(2*o,2),16);if(isNaN(s))throw new Error("Invalid hex string");e[n+o]=s}return o}function w(e,t,n,i){return J(q(t,e.length-n),e,n,i)}function x(e,t,n,i){return J(V(t),e,n,i)}function A(e,t,n,i){return x(e,t,n,i)}function j(e,t,n,i){return J(H(t),e,n,i)}function O(e,t,n,i){return J(z(t,e.length-n),e,n,i)}function _(e,t,n){return 0===t&&n===e.length?Q.fromByteArray(e):Q.fromByteArray(e.slice(t,n))}function S(e,t,n){n=Math.min(e.length,n);for(var i=[],r=t;n>r;){var a=e[r],o=null,s=a>239?4:a>223?3:a>191?2:1;if(n>=r+s){var l,u,c,p;switch(s){case 1:128>a&&(o=a);break;case 2:l=e[r+1],128===(192&l)&&(p=(31&a)<<6|63&l,p>127&&(o=p));break;case 3:l=e[r+1],u=e[r+2],128===(192&l)&&128===(192&u)&&(p=(15&a)<<12|(63&l)<<6|63&u,p>2047&&(55296>p||p>57343)&&(o=p));break;case 4:l=e[r+1],u=e[r+2],c=e[r+3],128===(192&l)&&128===(192&u)&&128===(192&c)&&(p=(15&a)<<18|(63&l)<<12|(63&u)<<6|63&c,p>65535&&1114112>p&&(o=p))}}null===o?(o=65533,s=1):o>65535&&(o-=65536,i.push(o>>>10&1023|55296),o=56320|1023&o),i.push(o),r+=s}return k(i)}function k(e){var t=e.length;if(X>=t)return String.fromCharCode.apply(String,e);for(var n="",i=0;t>i;)n+=String.fromCharCode.apply(String,e.slice(i,i+=X));return n}function C(e,t,n){var i="";n=Math.min(e.length,n);for(var r=t;n>r;r++)i+=String.fromCharCode(127&e[r]);return i}function E(e,t,n){var i="";n=Math.min(e.length,n);for(var r=t;n>r;r++)i+=String.fromCharCode(e[r]);return i}function I(e,t,n){var i=e.length;(!t||0>t)&&(t=0),(!n||0>n||n>i)&&(n=i);for(var r="",a=t;n>a;a++)r+=B(e[a]);return r}function T(e,t,n){for(var i=e.slice(t,n),r="",a=0;a<i.length;a+=2)r+=String.fromCharCode(i[a]+256*i[a+1]);return r}function $(e,t,n){if(e%1!==0||0>e)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,n,i,a,o){if(!r.isBuffer(e))throw new TypeError("buffer must be a Buffer instance");if(t>a||o>t)throw new RangeError("value is out of bounds");if(n+i>e.length)throw new RangeError("index out of range")}function U(e,t,n,i){0>t&&(t=65535+t+1);for(var r=0,a=Math.min(e.length-n,2);a>r;r++)e[n+r]=(t&255<<8*(i?r:1-r))>>>8*(i?r:1-r)}function P(e,t,n,i){0>t&&(t=4294967295+t+1);for(var r=0,a=Math.min(e.length-n,4);a>r;r++)e[n+r]=t>>>8*(i?r:3-r)&255}function L(e,t,n,i,r,a){if(t>r||a>t)throw new RangeError("value is out of bounds");if(n+i>e.length)throw new RangeError("index out of range");if(0>n)throw new RangeError("index out of range")}function D(e,t,n,i,r){return r||L(e,t,n,4,3.4028234663852886e38,-3.4028234663852886e38),Y.write(e,t,n,i,23,4),n+4}function R(e,t,n,i,r){return r||L(e,t,n,8,1.7976931348623157e308,-1.7976931348623157e308),Y.write(e,t,n,i,52,8),n+8}function N(e){if(e=F(e).replace(Z,""),e.length<2)return"";for(;e.length%4!==0;)e+="=";return e}function F(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function B(e){return 16>e?"0"+e.toString(16):e.toString(16)}function q(e,t){t=t||1/0;for(var n,i=e.length,r=null,a=[],o=0;i>o;o++){if(n=e.charCodeAt(o),n>55295&&57344>n){if(!r){if(n>56319){(t-=3)>-1&&a.push(239,191,189);continue}if(o+1===i){(t-=3)>-1&&a.push(239,191,189);continue}r=n;continue}if(56320>n){(t-=3)>-1&&a.push(239,191,189),r=n;continue}n=r-55296<<10|n-56320|65536}else r&&(t-=3)>-1&&a.push(239,191,189);if(r=null,128>n){if((t-=1)<0)break;a.push(n)}else if(2048>n){if((t-=2)<0)break;a.push(n>>6|192,63&n|128)}else if(65536>n){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(1114112>n))throw new Error("Invalid code point");if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return a}function V(e){for(var t=[],n=0;n<e.length;n++)t.push(255&e.charCodeAt(n));return t}function z(e,t){for(var n,i,r,a=[],o=0;o<e.length&&!((t-=2)<0);o++)n=e.charCodeAt(o),i=n>>8,r=n%256,a.push(r),a.push(i);return a}function H(e){return Q.toByteArray(N(e))}function J(e,t,n,i){for(var r=0;i>r&&!(r+n>=t.length||r>=e.length);r++)t[r+n]=e[r];return r}var Q=e("base64-js"),Y=e("ieee754"),K=e("is-array");n.Buffer=r,n.SlowBuffer=y,n.INSPECT_MAX_BYTES=50,r.poolSize=8192;var W={};r.TYPED_ARRAY_SUPPORT=function(){function e(){}try{var t=new Uint8Array(1);return t.foo=function(){return 42},t.constructor=e,42===t.foo()&&t.constructor===e&&"function"==typeof t.subarray&&0===t.subarray(1,1).byteLength}catch(n){return!1}}(),r.isBuffer=function(e){return!(null==e||!e._isBuffer)},r.compare=function(e,t){if(!r.isBuffer(e)||!r.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,i=t.length,a=0,o=Math.min(n,i);o>a&&e[a]===t[a];)++a;return a!==o&&(n=e[a],i=t[a]),i>n?-1:n>i?1:0},r.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},r.concat=function(e,t){if(!K(e))throw new TypeError("list argument must be an Array of Buffers.");if(0===e.length)return new r(0);var n;if(void 0===t)for(t=0,n=0;n<e.length;n++)t+=e[n].length;var i=new r(t),a=0;for(n=0;n<e.length;n++){var o=e[n];o.copy(i,a),a+=o.length}return i},r.byteLength=g,r.prototype.length=void 0,r.prototype.parent=void 0,r.prototype.toString=function(){var e=0|this.length;return 0===e?"":0===arguments.length?S(this,0,e):v.apply(this,arguments)},r.prototype.equals=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?!0:0===r.compare(this,e)},r.prototype.inspect=function(){var e="",t=n.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,t).match(/.{2}/g).join(" "),this.length>t&&(e+=" ... ")),"<Buffer "+e+">"},r.prototype.compare=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?0:r.compare(this,e)},r.prototype.indexOf=function(e,t){function n(e,t,n){for(var i=-1,r=0;n+r<e.length;r++)if(e[n+r]===t[-1===i?0:r-i]){if(-1===i&&(i=r),r-i+1===t.length)return n+i}else i=-1;return-1}if(t>2147483647?t=2147483647:-2147483648>t&&(t=-2147483648),t>>=0,0===this.length)return-1;if(t>=this.length)return-1;if(0>t&&(t=Math.max(this.length+t,0)),"string"==typeof e)return 0===e.length?-1:String.prototype.indexOf.call(this,e,t);if(r.isBuffer(e))return n(this,e,t);if("number"==typeof e)return r.TYPED_ARRAY_SUPPORT&&"function"===Uint8Array.prototype.indexOf?Uint8Array.prototype.indexOf.call(this,e,t):n(this,[e],t);throw new TypeError("val must be string, number or Buffer")},r.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},r.prototype.set=function(e,t){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,t)},r.prototype.write=function(e,t,n,i){if(void 0===t)i="utf8",n=this.length,t=0;else if(void 0===n&&"string"==typeof t)i=t,n=this.length,t=0;else if(isFinite(t))t=0|t,isFinite(n)?(n=0|n,void 0===i&&(i="utf8")):(i=n,n=void 0);else{var r=i;i=t,t=0|n,n=r}var a=this.length-t;if((void 0===n||n>a)&&(n=a),e.length>0&&(0>n||0>t)||t>this.length)throw new RangeError("attempt to write outside buffer bounds");i||(i="utf8");for(var o=!1;;)switch(i){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return x(this,e,t,n);case"binary":return A(this,e,t,n);case"base64":return j(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return O(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+i);i=(""+i).toLowerCase(),o=!0}},r.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var X=4096;r.prototype.slice=function(e,t){var n=this.length;e=~~e,t=void 0===t?n:~~t,0>e?(e+=n,0>e&&(e=0)):e>n&&(e=n),0>t?(t+=n,0>t&&(t=0)):t>n&&(t=n),e>t&&(t=e);var i;if(r.TYPED_ARRAY_SUPPORT)i=r._augment(this.subarray(e,t));else{var a=t-e;i=new r(a,void 0);for(var o=0;a>o;o++)i[o]=this[o+e]}return i.length&&(i.parent=this.parent||this),i},r.prototype.readUIntLE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e],r=1,a=0;++a<t&&(r*=256);)i+=this[e+a]*r;return i},r.prototype.readUIntBE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e+--t],r=1;t>0&&(r*=256);)i+=this[e+--t]*r;return i},r.prototype.readUInt8=function(e,t){return t||$(e,1,this.length),this[e]},r.prototype.readUInt16LE=function(e,t){return t||$(e,2,this.length),this[e]|this[e+1]<<8},r.prototype.readUInt16BE=function(e,t){return t||$(e,2,this.length),this[e]<<8|this[e+1]},r.prototype.readUInt32LE=function(e,t){return t||$(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},r.prototype.readUInt32BE=function(e,t){return t||$(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},r.prototype.readIntLE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e],r=1,a=0;++a<t&&(r*=256);)i+=this[e+a]*r;return r*=128,i>=r&&(i-=Math.pow(2,8*t)),i},r.prototype.readIntBE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=t,r=1,a=this[e+--i];i>0&&(r*=256);)a+=this[e+--i]*r;return r*=128,a>=r&&(a-=Math.pow(2,8*t)),a},r.prototype.readInt8=function(e,t){return t||$(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},r.prototype.readInt16LE=function(e,t){t||$(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt16BE=function(e,t){t||$(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt32LE=function(e,t){return t||$(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},r.prototype.readInt32BE=function(e,t){return t||$(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},r.prototype.readFloatLE=function(e,t){return t||$(e,4,this.length),Y.read(this,e,!0,23,4)},r.prototype.readFloatBE=function(e,t){return t||$(e,4,this.length),Y.read(this,e,!1,23,4)},r.prototype.readDoubleLE=function(e,t){return t||$(e,8,this.length),Y.read(this,e,!0,52,8)},r.prototype.readDoubleBE=function(e,t){return t||$(e,8,this.length),Y.read(this,e,!1,52,8)},r.prototype.writeUIntLE=function(e,t,n,i){e=+e,t=0|t,n=0|n,i||M(this,e,t,n,Math.pow(2,8*n),0);var r=1,a=0;for(this[t]=255&e;++a<n&&(r*=256);)this[t+a]=e/r&255;return t+n},r.prototype.writeUIntBE=function(e,t,n,i){e=+e,t=0|t,n=0|n,i||M(this,e,t,n,Math.pow(2,8*n),0);var r=n-1,a=1;for(this[t+r]=255&e;--r>=0&&(a*=256);)this[t+r]=e/a&255;return t+n},r.prototype.writeUInt8=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,1,255,0),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=e,t+1},r.prototype.writeUInt16LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):U(this,e,t,!0),t+2},r.prototype.writeUInt16BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):U(this,e,t,!1),t+2},r.prototype.writeUInt32LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=e):P(this,e,t,!0),t+4},r.prototype.writeUInt32BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):P(this,e,t,!1),t+4},r.prototype.writeIntLE=function(e,t,n,i){if(e=+e,t=0|t,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var a=0,o=1,s=0>e?1:0;for(this[t]=255&e;++a<n&&(o*=256);)this[t+a]=(e/o>>0)-s&255;return t+n},r.prototype.writeIntBE=function(e,t,n,i){if(e=+e,t=0|t,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var a=n-1,o=1,s=0>e?1:0;for(this[t+a]=255&e;--a>=0&&(o*=256);)this[t+a]=(e/o>>0)-s&255;return t+n},r.prototype.writeInt8=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,1,127,-128),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),0>e&&(e=255+e+1),this[t]=e,t+1},r.prototype.writeInt16LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):U(this,e,t,!0),t+2},r.prototype.writeInt16BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):U(this,e,t,!1),t+2},r.prototype.writeInt32LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,2147483647,-2147483648),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):P(this,e,t,!0),t+4},r.prototype.writeInt32BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,2147483647,-2147483648),0>e&&(e=4294967295+e+1),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):P(this,e,t,!1),t+4},r.prototype.writeFloatLE=function(e,t,n){return D(this,e,t,!0,n)},r.prototype.writeFloatBE=function(e,t,n){return D(this,e,t,!1,n)},r.prototype.writeDoubleLE=function(e,t,n){return R(this,e,t,!0,n)},r.prototype.writeDoubleBE=function(e,t,n){
+return R(this,e,t,!1,n)},r.prototype.copy=function(e,t,n,i){if(n||(n=0),i||0===i||(i=this.length),t>=e.length&&(t=e.length),t||(t=0),i>0&&n>i&&(i=n),i===n)return 0;if(0===e.length||0===this.length)return 0;if(0>t)throw new RangeError("targetStart out of bounds");if(0>n||n>=this.length)throw new RangeError("sourceStart out of bounds");if(0>i)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),e.length-t<i-n&&(i=e.length-t+n);var a,o=i-n;if(this===e&&t>n&&i>t)for(a=o-1;a>=0;a--)e[a+t]=this[a+n];else if(1e3>o||!r.TYPED_ARRAY_SUPPORT)for(a=0;o>a;a++)e[a+t]=this[a+n];else e._set(this.subarray(n,n+o),t);return o},r.prototype.fill=function(e,t,n){if(e||(e=0),t||(t=0),n||(n=this.length),t>n)throw new RangeError("end < start");if(n!==t&&0!==this.length){if(0>t||t>=this.length)throw new RangeError("start out of bounds");if(0>n||n>this.length)throw new RangeError("end out of bounds");var i;if("number"==typeof e)for(i=t;n>i;i++)this[i]=e;else{var r=q(e.toString()),a=r.length;for(i=t;n>i;i++)this[i]=r[i%a]}return this}},r.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(r.TYPED_ARRAY_SUPPORT)return new r(this).buffer;for(var e=new Uint8Array(this.length),t=0,n=e.length;n>t;t+=1)e[t]=this[t];return e.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser")};var G=r.prototype;r._augment=function(e){return e.constructor=r,e._isBuffer=!0,e._set=e.set,e.get=G.get,e.set=G.set,e.write=G.write,e.toString=G.toString,e.toLocaleString=G.toString,e.toJSON=G.toJSON,e.equals=G.equals,e.compare=G.compare,e.indexOf=G.indexOf,e.copy=G.copy,e.slice=G.slice,e.readUIntLE=G.readUIntLE,e.readUIntBE=G.readUIntBE,e.readUInt8=G.readUInt8,e.readUInt16LE=G.readUInt16LE,e.readUInt16BE=G.readUInt16BE,e.readUInt32LE=G.readUInt32LE,e.readUInt32BE=G.readUInt32BE,e.readIntLE=G.readIntLE,e.readIntBE=G.readIntBE,e.readInt8=G.readInt8,e.readInt16LE=G.readInt16LE,e.readInt16BE=G.readInt16BE,e.readInt32LE=G.readInt32LE,e.readInt32BE=G.readInt32BE,e.readFloatLE=G.readFloatLE,e.readFloatBE=G.readFloatBE,e.readDoubleLE=G.readDoubleLE,e.readDoubleBE=G.readDoubleBE,e.writeUInt8=G.writeUInt8,e.writeUIntLE=G.writeUIntLE,e.writeUIntBE=G.writeUIntBE,e.writeUInt16LE=G.writeUInt16LE,e.writeUInt16BE=G.writeUInt16BE,e.writeUInt32LE=G.writeUInt32LE,e.writeUInt32BE=G.writeUInt32BE,e.writeIntLE=G.writeIntLE,e.writeIntBE=G.writeIntBE,e.writeInt8=G.writeInt8,e.writeInt16LE=G.writeInt16LE,e.writeInt16BE=G.writeInt16BE,e.writeInt32LE=G.writeInt32LE,e.writeInt32BE=G.writeInt32BE,e.writeFloatLE=G.writeFloatLE,e.writeFloatBE=G.writeFloatBE,e.writeDoubleLE=G.writeDoubleLE,e.writeDoubleBE=G.writeDoubleBE,e.fill=G.fill,e.inspect=G.inspect,e.toArrayBuffer=G.toArrayBuffer,e};var Z=/[^+\/0-9A-Za-z-_]/g},{"base64-js":15,ieee754:16,"is-array":17}],15:[function(e,t,n){var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function t(e){var t=e.charCodeAt(0);return t===o||t===p?62:t===s||t===h?63:l>t?-1:l+10>t?t-l+26+26:c+26>t?t-c:u+26>t?t-u+26:void 0}function n(e){function n(e){u[p++]=e}var i,r,o,s,l,u;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var c=e.length;l="="===e.charAt(c-2)?2:"="===e.charAt(c-1)?1:0,u=new a(3*e.length/4-l),o=l>0?e.length-4:e.length;var p=0;for(i=0,r=0;o>i;i+=4,r+=3)s=t(e.charAt(i))<<18|t(e.charAt(i+1))<<12|t(e.charAt(i+2))<<6|t(e.charAt(i+3)),n((16711680&s)>>16),n((65280&s)>>8),n(255&s);return 2===l?(s=t(e.charAt(i))<<2|t(e.charAt(i+1))>>4,n(255&s)):1===l&&(s=t(e.charAt(i))<<10|t(e.charAt(i+1))<<4|t(e.charAt(i+2))>>2,n(s>>8&255),n(255&s)),u}function r(e){function t(e){return i.charAt(e)}function n(e){return t(e>>18&63)+t(e>>12&63)+t(e>>6&63)+t(63&e)}var r,a,o,s=e.length%3,l="";for(r=0,o=e.length-s;o>r;r+=3)a=(e[r]<<16)+(e[r+1]<<8)+e[r+2],l+=n(a);switch(s){case 1:a=e[e.length-1],l+=t(a>>2),l+=t(a<<4&63),l+="==";break;case 2:a=(e[e.length-2]<<8)+e[e.length-1],l+=t(a>>10),l+=t(a>>4&63),l+=t(a<<2&63),l+="="}return l}var a="undefined"!=typeof Uint8Array?Uint8Array:Array,o="+".charCodeAt(0),s="/".charCodeAt(0),l="0".charCodeAt(0),u="a".charCodeAt(0),c="A".charCodeAt(0),p="-".charCodeAt(0),h="_".charCodeAt(0);e.toByteArray=n,e.fromByteArray=r}("undefined"==typeof n?this.base64js={}:n)},{}],16:[function(e,t,n){n.read=function(e,t,n,i,r){var a,o,s=8*r-i-1,l=(1<<s)-1,u=l>>1,c=-7,p=n?r-1:0,h=n?-1:1,f=e[t+p];for(p+=h,a=f&(1<<-c)-1,f>>=-c,c+=s;c>0;a=256*a+e[t+p],p+=h,c-=8);for(o=a&(1<<-c)-1,a>>=-c,c+=i;c>0;o=256*o+e[t+p],p+=h,c-=8);if(0===a)a=1-u;else{if(a===l)return o?NaN:(f?-1:1)*(1/0);o+=Math.pow(2,i),a-=u}return(f?-1:1)*o*Math.pow(2,a-i)},n.write=function(e,t,n,i,r,a){var o,s,l,u=8*a-r-1,c=(1<<u)-1,p=c>>1,h=23===r?Math.pow(2,-24)-Math.pow(2,-77):0,f=i?0:a-1,d=i?1:-1,m=0>t||0===t&&0>1/t?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=c):(o=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-o))<1&&(o--,l*=2),t+=o+p>=1?h/l:h*Math.pow(2,1-p),t*l>=2&&(o++,l/=2),o+p>=c?(s=0,o=c):o+p>=1?(s=(t*l-1)*Math.pow(2,r),o+=p):(s=t*Math.pow(2,p-1)*Math.pow(2,r),o=0));r>=8;e[n+f]=255&s,f+=d,s/=256,r-=8);for(o=o<<r|s,u+=r;u>0;e[n+f]=255&o,f+=d,o/=256,u-=8);e[n+f-d]|=128*m}},{}],17:[function(e,t,n){var i=Array.isArray,r=Object.prototype.toString;t.exports=i||function(e){return!!e&&"[object Array]"==r.call(e)}},{}],18:[function(e,t,n){!function(){"use strict";function e(t,n,i,r){return this instanceof e?(this.domain=t||void 0,this.path=n||"/",this.secure=!!i,this.script=!!r,this):new e(t,n,i,r)}function t(e,n,i){return e instanceof t?e:this instanceof t?(this.name=null,this.value=null,this.expiration_date=1/0,this.path=String(i||"/"),this.explicit_path=!1,this.domain=n||null,this.explicit_domain=!1,this.secure=!1,this.noscript=!1,e&&this.parse(e,n,i),this):new t(e,n,i)}function i(){var e,n,r;return this instanceof i?(e=Object.create(null),this.setCookie=function(i,a,o){var s,l;if(i=new t(i,a,o),s=i.expiration_date<=Date.now(),void 0!==e[i.name]){for(n=e[i.name],l=0;l<n.length;l+=1)if(r=n[l],r.collidesWith(i))return s?(n.splice(l,1),0===n.length&&delete e[i.name],!1):(n[l]=i,i);return s?!1:(n.push(i),i)}return s?!1:(e[i.name]=[i],e[i.name])},this.getCookie=function(t,i){var r,a;if(n=e[t])for(a=0;a<n.length;a+=1)if(r=n[a],r.expiration_date<=Date.now())0===n.length&&delete e[r.name];else if(r.matches(i))return r},this.getCookies=function(t){var n,i,r=[];for(n in e)i=this.getCookie(n,t),i&&r.push(i);return r.toString=function(){return r.join(":")},r.toValueString=function(){return r.map(function(e){return e.toValueString()}).join(";")},r},this):new i}n.CookieAccessInfo=e,n.Cookie=t,t.prototype.toString=function(){var e=[this.name+"="+this.value];return this.expiration_date!==1/0&&e.push("expires="+new Date(this.expiration_date).toGMTString()),this.domain&&e.push("domain="+this.domain),this.path&&e.push("path="+this.path),this.secure&&e.push("secure"),this.noscript&&e.push("httponly"),e.join("; ")},t.prototype.toValueString=function(){return this.name+"="+this.value};var r=/[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;t.prototype.parse=function(e,n,i){if(this instanceof t){var r,a=e.split(";").filter(function(e){return!!e}),o=a[0].match(/([^=]+)=([\s\S]*)/),s=o[1],l=o[2];for(this.name=s,this.value=l,r=1;r<a.length;r+=1)switch(o=a[r].match(/([^=]+)(?:=([\s\S]*))?/),s=o[1].trim().toLowerCase(),l=o[2],s){case"httponly":this.noscript=!0;break;case"expires":this.expiration_date=l?Number(Date.parse(l)):1/0;break;case"path":this.path=l?l.trim():"",this.explicit_path=!0;break;case"domain":this.domain=l?l.trim():"",this.explicit_domain=!!this.domain;break;case"secure":this.secure=!0}return this.explicit_path||(this.path=i||"/"),this.explicit_domain||(this.domain=n),this}return(new t).parse(e,n,i)},t.prototype.matches=function(e){return!(this.noscript&&e.script||this.secure&&!e.secure)&&this.collidesWith(e)},t.prototype.collidesWith=function(e){if(this.path&&!e.path||this.domain&&!e.domain)return!1;if(this.path&&0!==e.path.indexOf(this.path))return!1;if(this.explicit_path&&0!==e.path.indexOf(this.path))return!1;var t=e.domain&&e.domain.replace(/^[\.]/,""),n=this.domain&&this.domain.replace(/^[\.]/,"");if(n===t)return!0;if(n){if(!this.explicit_domain)return!1;var i=t.indexOf(n);return-1!==i&&i===t.length-n.length}return!0},n.CookieJar=i,i.prototype.setCookies=function(e,n,i){e=Array.isArray(e)?e:e.split(r);var a,o,s=[];for(e=e.map(function(e){return new t(e,n,i)}),a=0;a<e.length;a+=1)o=e[a],this.setCookie(o,n,i)&&s.push(o);return s}}()},{}],19:[function(e,t,n){"use strict";var i=e("./lib/js-yaml.js");t.exports=i},{"./lib/js-yaml.js":20}],20:[function(e,t,n){"use strict";function i(e){return function(){throw new Error("Function "+e+" is deprecated and cannot be used.")}}var r=e("./js-yaml/loader"),a=e("./js-yaml/dumper");t.exports.Type=e("./js-yaml/type"),t.exports.Schema=e("./js-yaml/schema"),t.exports.FAILSAFE_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.JSON_SCHEMA=e("./js-yaml/schema/json"),t.exports.CORE_SCHEMA=e("./js-yaml/schema/core"),t.exports.DEFAULT_SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_FULL_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.load=r.load,t.exports.loadAll=r.loadAll,t.exports.safeLoad=r.safeLoad,t.exports.safeLoadAll=r.safeLoadAll,t.exports.dump=a.dump,t.exports.safeDump=a.safeDump,t.exports.YAMLException=e("./js-yaml/exception"),t.exports.MINIMAL_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.scan=i("scan"),t.exports.parse=i("parse"),t.exports.compose=i("compose"),t.exports.addConstructor=i("addConstructor")},{"./js-yaml/dumper":22,"./js-yaml/exception":23,"./js-yaml/loader":24,"./js-yaml/schema":26,"./js-yaml/schema/core":27,"./js-yaml/schema/default_full":28,"./js-yaml/schema/default_safe":29,"./js-yaml/schema/failsafe":30,"./js-yaml/schema/json":31,"./js-yaml/type":32}],21:[function(e,t,n){"use strict";function i(e){return"undefined"==typeof e||null===e}function r(e){return"object"==typeof e&&null!==e}function a(e){return Array.isArray(e)?e:i(e)?[]:[e]}function o(e,t){var n,i,r,a;if(t)for(a=Object.keys(t),n=0,i=a.length;i>n;n+=1)r=a[n],e[r]=t[r];return e}function s(e,t){var n,i="";for(n=0;t>n;n+=1)i+=e;return i}function l(e){return 0===e&&Number.NEGATIVE_INFINITY===1/e}t.exports.isNothing=i,t.exports.isObject=r,t.exports.toArray=a,t.exports.repeat=s,t.exports.isNegativeZero=l,t.exports.extend=o},{}],22:[function(e,t,n){"use strict";function i(e,t){var n,i,r,a,o,s,l;if(null===t)return{};for(n={},i=Object.keys(t),r=0,a=i.length;a>r;r+=1)o=i[r],s=String(t[o]),"!!"===o.slice(0,2)&&(o="tag:yaml.org,2002:"+o.slice(2)),l=e.compiledTypeMap[o],l&&P.call(l.styleAliases,s)&&(s=l.styleAliases[s]),n[o]=s;return n}function r(e){var t,n,i;if(t=e.toString(16).toUpperCase(),255>=e)n="x",i=2;else if(65535>=e)n="u",i=4;else{if(!(4294967295>=e))throw new T("code point within a string may not be greater than 0xFFFFFFFF");n="U",i=8}return"\\"+n+I.repeat("0",i-t.length)+t}function a(e){this.schema=e.schema||$,this.indent=Math.max(1,e.indent||2),this.skipInvalid=e.skipInvalid||!1,this.flowLevel=I.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=i(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function o(e,t){for(var n,i=I.repeat(" ",t),r=0,a=-1,o="",s=e.length;s>r;)a=e.indexOf("\n",r),-1===a?(n=e.slice(r),r=s):(n=e.slice(r,a+1),r=a+1),n.length&&"\n"!==n&&(o+=i),o+=n;return o}function s(e,t){return"\n"+I.repeat(" ",e.indent*t)}function l(e,t){var n,i,r;for(n=0,i=e.implicitTypes.length;i>n;n+=1)if(r=e.implicitTypes[n],r.resolve(t))return!0;return!1}function u(e){return e===R||e===L}function c(e){return e>=32&&126>=e||e>=161&&55295>=e&&8232!==e&&8233!==e||e>=57344&&65533>=e&&65279!==e||e>=65536&&1114111>=e}function p(e){return c(e)&&65279!==e&&e!==J&&e!==G&&e!==Z&&e!==te&&e!==ie&&e!==Y&&e!==B}function h(e){return c(e)&&65279!==e&&!u(e)&&e!==Q&&e!==W&&e!==Y&&e!==J&&e!==G&&e!==Z&&e!==te&&e!==ie&&e!==B&&e!==V&&e!==H&&e!==N&&e!==ne&&e!==K&&e!==z&&e!==F&&e!==q&&e!==X&&e!==ee}function f(e,t,n,i,r){var a,o,s=!1,l=!1,f=-1!==i,d=-1,m=h(e.charCodeAt(0))&&!u(e.charCodeAt(e.length-1));if(t)for(a=0;a<e.length;a++){if(o=e.charCodeAt(a),!c(o))return ce;m=m&&p(o)}else{for(a=0;a<e.length;a++){if(o=e.charCodeAt(a),o===D)s=!0,f&&(l=l||a-d-1>i&&" "!==e[d+1],d=a);else if(!c(o))return ce;m=m&&p(o)}l=l||f&&a-d-1>i&&" "!==e[d+1]}return s||l?" "===e[0]&&n>9?ce:l?ue:le:m&&!r(e)?oe:se}function d(e,t,n,i){e.dump=function(){function r(t){return l(e,t)}if(0===t.length)return"''";if(!e.noCompatMode&&-1!==ae.indexOf(t))return"'"+t+"'";var a=e.indent*Math.max(1,n),s=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-a),u=i||e.flowLevel>-1&&n>=e.flowLevel;switch(f(t,u,e.indent,s,r)){case oe:return t;case se:return"'"+t.replace(/'/g,"''")+"'";case le:return"|"+m(t,e.indent)+y(o(t,a));case ue:return">"+m(t,e.indent)+y(o(g(t,s),a));case ce:return'"'+b(t,s)+'"';default:throw new T("impossible error: invalid scalar style")}}()}function m(e,t){var n=" "===e[0]?String(t):"",i="\n"===e[e.length-1],r=i&&("\n"===e[e.length-2]||"\n"===e),a=r?"+":i?"":"-";return n+a+"\n"}function y(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function g(e,t){for(var n,i,r=/(\n+)([^\n]*)/g,a=function(){var n=e.indexOf("\n");return n=-1!==n?n:e.length,r.lastIndex=n,v(e.slice(0,n),t)}(),o="\n"===e[0]||" "===e[0];i=r.exec(e);){var s=i[1],l=i[2];n=" "===l[0],a+=s+(o||n||""===l?"":"\n")+v(l,t),o=n}return a}function v(e,t){if(""===e||" "===e[0])return e;for(var n,i,r=/ [^ ]/g,a=0,o=0,s=0,l="";n=r.exec(e);)s=n.index,s-a>t&&(i=o>a?o:s,l+="\n"+e.slice(a,i),a=i+1),o=s;return l+="\n",l+=e.length-a>t&&o>a?e.slice(a,o)+"\n"+e.slice(o+1):e.slice(a),l.slice(1)}function b(e){for(var t,n,i="",a=0;a<e.length;a++)t=e.charCodeAt(a),n=re[t],i+=!n&&c(t)?e[a]:n||r(t);return i}function w(e,t,n){var i,r,a="",o=e.tag;for(i=0,r=n.length;r>i;i+=1)_(e,t,n[i],!1,!1)&&(0!==i&&(a+=", "),a+=e.dump);e.tag=o,e.dump="["+a+"]"}function x(e,t,n,i){var r,a,o="",l=e.tag;for(r=0,a=n.length;a>r;r+=1)_(e,t+1,n[r],!0,!0)&&(i&&0===r||(o+=s(e,t)),o+="- "+e.dump);e.tag=l,e.dump=o||"[]"}function A(e,t,n){var i,r,a,o,s,l="",u=e.tag,c=Object.keys(n);for(i=0,r=c.length;r>i;i+=1)s="",0!==i&&(s+=", "),a=c[i],o=n[a],_(e,t,a,!1,!1)&&(e.dump.length>1024&&(s+="? "),s+=e.dump+": ",_(e,t,o,!1,!1)&&(s+=e.dump,l+=s));e.tag=u,e.dump="{"+l+"}"}function j(e,t,n,i){var r,a,o,l,u,c,p="",h=e.tag,f=Object.keys(n);if(e.sortKeys===!0)f.sort();else if("function"==typeof e.sortKeys)f.sort(e.sortKeys);else if(e.sortKeys)throw new T("sortKeys must be a boolean or a function");for(r=0,a=f.length;a>r;r+=1)c="",i&&0===r||(c+=s(e,t)),o=f[r],l=n[o],_(e,t+1,o,!0,!0,!0)&&(u=null!==e.tag&&"?"!==e.tag||e.dump&&e.dump.length>1024,u&&(c+=e.dump&&D===e.dump.charCodeAt(0)?"?":"? "),c+=e.dump,u&&(c+=s(e,t)),_(e,t+1,l,!0,u)&&(c+=e.dump&&D===e.dump.charCodeAt(0)?":":": ",c+=e.dump,p+=c));e.tag=h,e.dump=p||"{}"}function O(e,t,n){var i,r,a,o,s,l;for(r=n?e.explicitTypes:e.implicitTypes,a=0,o=r.length;o>a;a+=1)if(s=r[a],(s.instanceOf||s.predicate)&&(!s.instanceOf||"object"==typeof t&&t instanceof s.instanceOf)&&(!s.predicate||s.predicate(t))){if(e.tag=n?s.tag:"?",s.represent){if(l=e.styleMap[s.tag]||s.defaultStyle,"[object Function]"===U.call(s.represent))i=s.represent(t,l);else{if(!P.call(s.represent,l))throw new T("!<"+s.tag+'> tag resolver accepts not "'+l+'" style');i=s.represent[l](t,l)}e.dump=i}return!0}return!1}function _(e,t,n,i,r,a){e.tag=null,e.dump=n,O(e,n,!1)||O(e,n,!0);var o=U.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,l,u="[object Object]"===o||"[object Array]"===o;if(u&&(s=e.duplicates.indexOf(n),l=-1!==s),(null!==e.tag&&"?"!==e.tag||l||2!==e.indent&&t>0)&&(r=!1),l&&e.usedDuplicates[s])e.dump="*ref_"+s;else{if(u&&l&&!e.usedDuplicates[s]&&(e.usedDuplicates[s]=!0),"[object Object]"===o)i&&0!==Object.keys(e.dump).length?(j(e,t,e.dump,r),l&&(e.dump="&ref_"+s+e.dump)):(A(e,t,e.dump),l&&(e.dump="&ref_"+s+" "+e.dump));else if("[object Array]"===o)i&&0!==e.dump.length?(x(e,t,e.dump,r),l&&(e.dump="&ref_"+s+e.dump)):(w(e,t,e.dump),l&&(e.dump="&ref_"+s+" "+e.dump));else{if("[object String]"!==o){if(e.skipInvalid)return!1;throw new T("unacceptable kind of an object to dump "+o)}"?"!==e.tag&&d(e,e.dump,t,a)}null!==e.tag&&"?"!==e.tag&&(e.dump="!<"+e.tag+"> "+e.dump)}return!0}function S(e,t){var n,i,r=[],a=[];for(k(e,r,a),n=0,i=a.length;i>n;n+=1)t.duplicates.push(r[a[n]]);t.usedDuplicates=new Array(i)}function k(e,t,n){var i,r,a;if(null!==e&&"object"==typeof e)if(r=t.indexOf(e),-1!==r)-1===n.indexOf(r)&&n.push(r);else if(t.push(e),Array.isArray(e))for(r=0,a=e.length;a>r;r+=1)k(e[r],t,n);else for(i=Object.keys(e),r=0,a=i.length;a>r;r+=1)k(e[i[r]],t,n)}function C(e,t){t=t||{};var n=new a(t);return n.noRefs||S(e,n),_(n,0,e,!0,!0)?n.dump+"\n":""}function E(e,t){return C(e,I.extend({schema:M},t))}var I=e("./common"),T=e("./exception"),$=e("./schema/default_full"),M=e("./schema/default_safe"),U=Object.prototype.toString,P=Object.prototype.hasOwnProperty,L=9,D=10,R=32,N=33,F=34,B=35,q=37,V=38,z=39,H=42,J=44,Q=45,Y=58,K=62,W=63,X=64,G=91,Z=93,ee=96,te=123,ne=124,ie=125,re={};re[0]="\\0",re[7]="\\a",re[8]="\\b",re[9]="\\t",re[10]="\\n",re[11]="\\v",re[12]="\\f",re[13]="\\r",re[27]="\\e",re[34]='\\"',re[92]="\\\\",re[133]="\\N",re[160]="\\_",re[8232]="\\L",re[8233]="\\P";var ae=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],oe=1,se=2,le=3,ue=4,ce=5;t.exports.dump=C,t.exports.safeDump=E},{"./common":21,"./exception":23,"./schema/default_full":28,"./schema/default_safe":29}],23:[function(e,t,n){"use strict";function i(e,t){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||"",this.name="YAMLException",this.reason=e,this.mark=t,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():"")}i.prototype=Object.create(Error.prototype),i.prototype.constructor=i,i.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t},t.exports=i},{}],24:[function(e,t,n){"use strict";function i(e){return 10===e||13===e}function r(e){return 9===e||32===e}function a(e){return 9===e||32===e||10===e||13===e}function o(e){return 44===e||91===e||93===e||123===e||125===e}function s(e){var t;return e>=48&&57>=e?e-48:(t=32|e,t>=97&&102>=t?t-97+10:-1)}function l(e){return 120===e?2:117===e?4:85===e?8:0}function u(e){return e>=48&&57>=e?e-48:-1}function c(e){return 48===e?"\x00":97===e?"":98===e?"\b":116===e?"	":9===e?"	":110===e?"\n":118===e?"\x0B":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function p(e){return 65535>=e?String.fromCharCode(e):String.fromCharCode((e-65536>>10)+55296,(e-65536&1023)+56320)}function h(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||z,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function f(e,t){return new B(t,new q(e.filename,e.input,e.position,e.line,e.position-e.lineStart))}function d(e,t){throw f(e,t)}function m(e,t){e.onWarning&&e.onWarning.call(null,f(e,t))}function y(e,t,n,i){var r,a,o,s;if(n>t){if(s=e.input.slice(t,n),i)for(r=0,a=s.length;a>r;r+=1)o=s.charCodeAt(r),9===o||o>=32&&1114111>=o||d(e,"expected valid JSON character");else Z.test(s)&&d(e,"the stream contains non-printable characters");e.result+=s}}function g(e,t,n,i){var r,a,o,s;for(F.isObject(n)||d(e,"cannot merge mappings; the provided source object is unacceptable"),r=Object.keys(n),o=0,s=r.length;s>o;o+=1)a=r[o],H.call(t,a)||(t[a]=n[a],i[a]=!0)}function v(e,t,n,i,r,a){var o,s;if(r=String(r),null===t&&(t={}),"tag:yaml.org,2002:merge"===i)if(Array.isArray(a))for(o=0,s=a.length;s>o;o+=1)g(e,t,a[o],n);else g(e,t,a,n);else e.json||H.call(n,r)||!H.call(t,r)||d(e,"duplicated mapping key"),t[r]=a,delete n[r];return t}function b(e){var t;t=e.input.charCodeAt(e.position),10===t?e.position++:13===t?(e.position++,10===e.input.charCodeAt(e.position)&&e.position++):d(e,"a line break is expected"),e.line+=1,e.lineStart=e.position}function w(e,t,n){for(var a=0,o=e.input.charCodeAt(e.position);0!==o;){for(;r(o);)o=e.input.charCodeAt(++e.position);if(t&&35===o)do o=e.input.charCodeAt(++e.position);while(10!==o&&13!==o&&0!==o);if(!i(o))break;for(b(e),o=e.input.charCodeAt(e.position),a++,e.lineIndent=0;32===o;)e.lineIndent++,o=e.input.charCodeAt(++e.position)}return-1!==n&&0!==a&&e.lineIndent<n&&m(e,"deficient indentation"),a}function x(e){var t,n=e.position;return t=e.input.charCodeAt(n),(45===t||46===t)&&t===e.input.charCodeAt(n+1)&&t===e.input.charCodeAt(n+2)&&(n+=3,t=e.input.charCodeAt(n),0===t||a(t))}function A(e,t){1===t?e.result+=" ":t>1&&(e.result+=F.repeat("\n",t-1))}function j(e,t,n){var s,l,u,c,p,h,f,d,m,g=e.kind,v=e.result;if(m=e.input.charCodeAt(e.position),a(m)||o(m)||35===m||38===m||42===m||33===m||124===m||62===m||39===m||34===m||37===m||64===m||96===m)return!1;if((63===m||45===m)&&(l=e.input.charCodeAt(e.position+1),a(l)||n&&o(l)))return!1;for(e.kind="scalar",e.result="",u=c=e.position,p=!1;0!==m;){if(58===m){if(l=e.input.charCodeAt(e.position+1),a(l)||n&&o(l))break}else if(35===m){if(s=e.input.charCodeAt(e.position-1),a(s))break}else{if(e.position===e.lineStart&&x(e)||n&&o(m))break;if(i(m)){if(h=e.line,f=e.lineStart,d=e.lineIndent,w(e,!1,-1),e.lineIndent>=t){p=!0,m=e.input.charCodeAt(e.position);continue}e.position=c,e.line=h,e.lineStart=f,e.lineIndent=d;break}}p&&(y(e,u,c,!1),A(e,e.line-h),u=c=e.position,p=!1),r(m)||(c=e.position+1),m=e.input.charCodeAt(++e.position)}return y(e,u,c,!1),e.result?!0:(e.kind=g,e.result=v,!1)}function O(e,t){var n,r,a;if(n=e.input.charCodeAt(e.position),39!==n)return!1;for(e.kind="scalar",e.result="",e.position++,r=a=e.position;0!==(n=e.input.charCodeAt(e.position));)if(39===n){if(y(e,r,e.position,!0),n=e.input.charCodeAt(++e.position),39!==n)return!0;r=a=e.position,e.position++}else i(n)?(y(e,r,a,!0),A(e,w(e,!1,t)),r=a=e.position):e.position===e.lineStart&&x(e)?d(e,"unexpected end of the document within a single quoted scalar"):(e.position++,a=e.position);d(e,"unexpected end of the stream within a single quoted scalar")}function _(e,t){var n,r,a,o,u,c;if(c=e.input.charCodeAt(e.position),34!==c)return!1;for(e.kind="scalar",e.result="",e.position++,n=r=e.position;0!==(c=e.input.charCodeAt(e.position));){if(34===c)return y(e,n,e.position,!0),e.position++,!0;if(92===c){if(y(e,n,e.position,!0),c=e.input.charCodeAt(++e.position),i(c))w(e,!1,t);else if(256>c&&re[c])e.result+=ae[c],e.position++;else if((u=l(c))>0){for(a=u,o=0;a>0;a--)c=e.input.charCodeAt(++e.position),(u=s(c))>=0?o=(o<<4)+u:d(e,"expected hexadecimal character");e.result+=p(o),e.position++}else d(e,"unknown escape sequence");n=r=e.position}else i(c)?(y(e,n,r,!0),A(e,w(e,!1,t)),n=r=e.position):e.position===e.lineStart&&x(e)?d(e,"unexpected end of the document within a double quoted scalar"):(e.position++,r=e.position)}d(e,"unexpected end of the stream within a double quoted scalar")}function S(e,t){var n,i,r,o,s,l,u,c,p,h,f,m=!0,y=e.tag,g=e.anchor,b={};if(f=e.input.charCodeAt(e.position),91===f)o=93,u=!1,i=[];else{if(123!==f)return!1;o=125,u=!0,i={}}for(null!==e.anchor&&(e.anchorMap[e.anchor]=i),f=e.input.charCodeAt(++e.position);0!==f;){if(w(e,!0,t),f=e.input.charCodeAt(e.position),f===o)return e.position++,e.tag=y,e.anchor=g,e.kind=u?"mapping":"sequence",e.result=i,!0;m||d(e,"missed comma between flow collection entries"),p=c=h=null,s=l=!1,63===f&&(r=e.input.charCodeAt(e.position+1),a(r)&&(s=l=!0,e.position++,w(e,!0,t))),n=e.line,M(e,t,J,!1,!0),p=e.tag,c=e.result,w(e,!0,t),f=e.input.charCodeAt(e.position),!l&&e.line!==n||58!==f||(s=!0,f=e.input.charCodeAt(++e.position),w(e,!0,t),M(e,t,J,!1,!0),h=e.result),u?v(e,i,b,p,c,h):s?i.push(v(e,null,b,p,c,h)):i.push(c),w(e,!0,t),f=e.input.charCodeAt(e.position),44===f?(m=!0,f=e.input.charCodeAt(++e.position)):m=!1}d(e,"unexpected end of the stream within a flow collection")}function k(e,t){var n,a,o,s,l=W,c=!1,p=!1,h=t,f=0,m=!1;if(s=e.input.charCodeAt(e.position),124===s)a=!1;else{if(62!==s)return!1;a=!0}for(e.kind="scalar",e.result="";0!==s;)if(s=e.input.charCodeAt(++e.position),43===s||45===s)W===l?l=43===s?G:X:d(e,"repeat of a chomping mode identifier");else{if(!((o=u(s))>=0))break;0===o?d(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):p?d(e,"repeat of an indentation width identifier"):(h=t+o-1,p=!0)}if(r(s)){do s=e.input.charCodeAt(++e.position);while(r(s));if(35===s)do s=e.input.charCodeAt(++e.position);while(!i(s)&&0!==s)}for(;0!==s;){for(b(e),e.lineIndent=0,s=e.input.charCodeAt(e.position);(!p||e.lineIndent<h)&&32===s;)e.lineIndent++,s=e.input.charCodeAt(++e.position);if(!p&&e.lineIndent>h&&(h=e.lineIndent),i(s))f++;else{if(e.lineIndent<h){l===G?e.result+=F.repeat("\n",c?1+f:f):l===W&&c&&(e.result+="\n");break}for(a?r(s)?(m=!0,e.result+=F.repeat("\n",c?1+f:f)):m?(m=!1,e.result+=F.repeat("\n",f+1)):0===f?c&&(e.result+=" "):e.result+=F.repeat("\n",f):e.result+=F.repeat("\n",c?1+f:f),c=!0,p=!0,f=0,n=e.position;!i(s)&&0!==s;)s=e.input.charCodeAt(++e.position);y(e,n,e.position,!1)}}return!0}function C(e,t){var n,i,r,o=e.tag,s=e.anchor,l=[],u=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=l),r=e.input.charCodeAt(e.position);0!==r&&45===r&&(i=e.input.charCodeAt(e.position+1),a(i));)if(u=!0,e.position++,w(e,!0,-1)&&e.lineIndent<=t)l.push(null),r=e.input.charCodeAt(e.position);else if(n=e.line,M(e,t,Y,!1,!0),l.push(e.result),w(e,!0,-1),r=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==r)d(e,"bad indentation of a sequence entry");else if(e.lineIndent<t)break;return u?(e.tag=o,e.anchor=s,e.kind="sequence",e.result=l,!0):!1}function E(e,t,n){var i,o,s,l,u=e.tag,c=e.anchor,p={},h={},f=null,m=null,y=null,g=!1,b=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=p),l=e.input.charCodeAt(e.position);0!==l;){if(i=e.input.charCodeAt(e.position+1),s=e.line,63!==l&&58!==l||!a(i)){if(!M(e,n,Q,!1,!0))break;if(e.line===s){for(l=e.input.charCodeAt(e.position);r(l);)l=e.input.charCodeAt(++e.position);if(58===l)l=e.input.charCodeAt(++e.position),a(l)||d(e,"a whitespace character is expected after the key-value separator within a block mapping"),g&&(v(e,p,h,f,m,null),f=m=y=null),b=!0,g=!1,o=!1,f=e.tag,m=e.result;else{if(!b)return e.tag=u,e.anchor=c,!0;d(e,"can not read an implicit mapping pair; a colon is missed")}}else{if(!b)return e.tag=u,e.anchor=c,!0;d(e,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===l?(g&&(v(e,p,h,f,m,null),f=m=y=null),b=!0,g=!0,o=!0):g?(g=!1,o=!0):d(e,"incomplete explicit mapping pair; a key node is missed"),e.position+=1,l=i;if((e.line===s||e.lineIndent>t)&&(M(e,t,K,!0,o)&&(g?m=e.result:y=e.result),g||(v(e,p,h,f,m,y),f=m=y=null),w(e,!0,-1),l=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==l)d(e,"bad indentation of a mapping entry");else if(e.lineIndent<t)break}return g&&v(e,p,h,f,m,null),b&&(e.tag=u,e.anchor=c,e.kind="mapping",e.result=p),b}function I(e){var t,n,i,r,o=!1,s=!1;if(r=e.input.charCodeAt(e.position),33!==r)return!1;if(null!==e.tag&&d(e,"duplication of a tag property"),r=e.input.charCodeAt(++e.position),60===r?(o=!0,r=e.input.charCodeAt(++e.position)):33===r?(s=!0,n="!!",r=e.input.charCodeAt(++e.position)):n="!",t=e.position,o){do r=e.input.charCodeAt(++e.position);while(0!==r&&62!==r);e.position<e.length?(i=e.input.slice(t,e.position),r=e.input.charCodeAt(++e.position)):d(e,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!a(r);)33===r&&(s?d(e,"tag suffix cannot contain exclamation marks"):(n=e.input.slice(t-1,e.position+1),ne.test(n)||d(e,"named tag handle cannot contain such characters"),s=!0,t=e.position+1)),r=e.input.charCodeAt(++e.position);i=e.input.slice(t,e.position),te.test(i)&&d(e,"tag suffix cannot contain flow indicator characters")}return i&&!ie.test(i)&&d(e,"tag name cannot contain such characters: "+i),o?e.tag=i:H.call(e.tagMap,n)?e.tag=e.tagMap[n]+i:"!"===n?e.tag="!"+i:"!!"===n?e.tag="tag:yaml.org,2002:"+i:d(e,'undeclared tag handle "'+n+'"'),!0}function T(e){var t,n;if(n=e.input.charCodeAt(e.position),38!==n)return!1;for(null!==e.anchor&&d(e,"duplication of an anchor property"),n=e.input.charCodeAt(++e.position),t=e.position;0!==n&&!a(n)&&!o(n);)n=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an anchor node must contain at least one character"),e.anchor=e.input.slice(t,e.position),!0}function $(e){var t,n,i;if(i=e.input.charCodeAt(e.position),42!==i)return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!a(i)&&!o(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||d(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],w(e,!0,-1),!0}function M(e,t,n,i,r){var a,o,s,l,u,c,p,h,f=1,m=!1,y=!1;if(null!==e.listener&&e.listener("open",e),e.tag=null,e.anchor=null,e.kind=null,e.result=null,a=o=s=K===n||Y===n,i&&w(e,!0,-1)&&(m=!0,e.lineIndent>t?f=1:e.lineIndent===t?f=0:e.lineIndent<t&&(f=-1)),1===f)for(;I(e)||T(e);)w(e,!0,-1)?(m=!0,s=a,e.lineIndent>t?f=1:e.lineIndent===t?f=0:e.lineIndent<t&&(f=-1)):s=!1;if(s&&(s=m||r),1!==f&&K!==n||(p=J===n||Q===n?t:t+1,h=e.position-e.lineStart,1===f?s&&(C(e,h)||E(e,h,p))||S(e,p)?y=!0:(o&&k(e,p)||O(e,p)||_(e,p)?y=!0:$(e)?(y=!0,null===e.tag&&null===e.anchor||d(e,"alias node should not have any properties")):j(e,p,J===n)&&(y=!0,null===e.tag&&(e.tag="?")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===f&&(y=s&&C(e,h))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(l=0,u=e.implicitTypes.length;u>l;l+=1)if(c=e.implicitTypes[l],c.resolve(e.result)){e.result=c.construct(e.result),e.tag=c.tag,null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);break}}else H.call(e.typeMap,e.tag)?(c=e.typeMap[e.tag],null!==e.result&&c.kind!==e.kind&&d(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+c.kind+'", not "'+e.kind+'"'),c.resolve(e.result)?(e.result=c.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):d(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):d(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||y}function U(e){var t,n,o,s,l=e.position,u=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(s=e.input.charCodeAt(e.position))&&(w(e,!0,-1),s=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==s));){for(u=!0,s=e.input.charCodeAt(++e.position),t=e.position;0!==s&&!a(s);)s=e.input.charCodeAt(++e.position);for(n=e.input.slice(t,e.position),o=[],n.length<1&&d(e,"directive name must not be less than one character in length");0!==s;){for(;r(s);)s=e.input.charCodeAt(++e.position);if(35===s){do s=e.input.charCodeAt(++e.position);while(0!==s&&!i(s));break}if(i(s))break;for(t=e.position;0!==s&&!a(s);)s=e.input.charCodeAt(++e.position);o.push(e.input.slice(t,e.position))}0!==s&&b(e),H.call(se,n)?se[n](e,n,o):m(e,'unknown document directive "'+n+'"')}return w(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,w(e,!0,-1)):u&&d(e,"directives end mark is expected"),M(e,e.lineIndent-1,K,!1,!0),w(e,!0,-1),e.checkLineBreaks&&ee.test(e.input.slice(l,e.position))&&m(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&x(e)?void(46===e.input.charCodeAt(e.position)&&(e.position+=3,
+w(e,!0,-1))):void(e.position<e.length-1&&d(e,"end of the stream or a document separator is expected"))}function P(e,t){e=String(e),t=t||{},0!==e.length&&(10!==e.charCodeAt(e.length-1)&&13!==e.charCodeAt(e.length-1)&&(e+="\n"),65279===e.charCodeAt(0)&&(e=e.slice(1)));var n=new h(e,t);for(n.input+="\x00";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)U(n);return n.documents}function L(e,t,n){var i,r,a=P(e,n);for(i=0,r=a.length;r>i;i+=1)t(a[i])}function D(e,t){var n=P(e,t);if(0!==n.length){if(1===n.length)return n[0];throw new B("expected a single document in the stream, but found more")}}function R(e,t,n){L(e,t,F.extend({schema:V},n))}function N(e,t){return D(e,F.extend({schema:V},t))}for(var F=e("./common"),B=e("./exception"),q=e("./mark"),V=e("./schema/default_safe"),z=e("./schema/default_full"),H=Object.prototype.hasOwnProperty,J=1,Q=2,Y=3,K=4,W=1,X=2,G=3,Z=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,ee=/[\x85\u2028\u2029]/,te=/[,\[\]\{\}]/,ne=/^(?:!|!!|![a-z\-]+!)$/i,ie=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i,re=new Array(256),ae=new Array(256),oe=0;256>oe;oe++)re[oe]=c(oe)?1:0,ae[oe]=c(oe);var se={YAML:function(e,t,n){var i,r,a;null!==e.version&&d(e,"duplication of %YAML directive"),1!==n.length&&d(e,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===i&&d(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),a=parseInt(i[2],10),1!==r&&d(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=2>a,1!==a&&2!==a&&m(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&d(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],ne.test(i)||d(e,"ill-formed tag handle (first argument) of the TAG directive"),H.call(e.tagMap,i)&&d(e,'there is a previously declared suffix for "'+i+'" tag handle'),ie.test(r)||d(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[i]=r}};t.exports.loadAll=L,t.exports.load=D,t.exports.safeLoadAll=R,t.exports.safeLoad=N},{"./common":21,"./exception":23,"./mark":25,"./schema/default_full":28,"./schema/default_safe":29}],25:[function(e,t,n){"use strict";function i(e,t,n,i,r){this.name=e,this.buffer=t,this.position=n,this.line=i,this.column=r}var r=e("./common");i.prototype.getSnippet=function(e,t){var n,i,a,o,s;if(!this.buffer)return null;for(e=e||4,t=t||75,n="",i=this.position;i>0&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(i-1));)if(i-=1,this.position-i>t/2-1){n=" ... ",i+=5;break}for(a="",o=this.position;o<this.buffer.length&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(o));)if(o+=1,o-this.position>t/2-1){a=" ... ",o-=5;break}return s=this.buffer.slice(i,o),r.repeat(" ",e)+n+s+a+"\n"+r.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet(),t&&(n+=":\n"+t)),n},t.exports=i},{"./common":21}],26:[function(e,t,n){"use strict";function i(e,t,n){var r=[];return e.include.forEach(function(e){n=i(e,t,n)}),e[t].forEach(function(e){n.forEach(function(t,n){t.tag===e.tag&&r.push(n)}),n.push(e)}),n.filter(function(e,t){return-1===r.indexOf(t)})}function r(){function e(e){i[e.tag]=e}var t,n,i={};for(t=0,n=arguments.length;n>t;t+=1)arguments[t].forEach(e);return i}function a(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=i(this,"implicit",[]),this.compiledExplicit=i(this,"explicit",[]),this.compiledTypeMap=r(this.compiledImplicit,this.compiledExplicit)}var o=e("./common"),s=e("./exception"),l=e("./type");a.DEFAULT=null,a.create=function(){var e,t;switch(arguments.length){case 1:e=a.DEFAULT,t=arguments[0];break;case 2:e=arguments[0],t=arguments[1];break;default:throw new s("Wrong number of arguments for Schema.create function")}if(e=o.toArray(e),t=o.toArray(t),!e.every(function(e){return e instanceof a}))throw new s("Specified list of super schemas (or a single Schema object) contains a non-Schema object.");if(!t.every(function(e){return e instanceof l}))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");return new a({include:e,explicit:t})},t.exports=a},{"./common":21,"./exception":23,"./type":32}],27:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./json")]})},{"../schema":26,"./json":31}],28:[function(e,t,n){"use strict";var i=e("../schema");t.exports=i.DEFAULT=new i({include:[e("./default_safe")],explicit:[e("../type/js/undefined"),e("../type/js/regexp"),e("../type/js/function")]})},{"../schema":26,"../type/js/function":37,"../type/js/regexp":38,"../type/js/undefined":39,"./default_safe":29}],29:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./core")],implicit:[e("../type/timestamp"),e("../type/merge")],explicit:[e("../type/binary"),e("../type/omap"),e("../type/pairs"),e("../type/set")]})},{"../schema":26,"../type/binary":33,"../type/merge":41,"../type/omap":43,"../type/pairs":44,"../type/set":46,"../type/timestamp":48,"./core":27}],30:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({explicit:[e("../type/str"),e("../type/seq"),e("../type/map")]})},{"../schema":26,"../type/map":40,"../type/seq":45,"../type/str":47}],31:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./failsafe")],implicit:[e("../type/null"),e("../type/bool"),e("../type/int"),e("../type/float")]})},{"../schema":26,"../type/bool":34,"../type/float":35,"../type/int":36,"../type/null":42,"./failsafe":30}],32:[function(e,t,n){"use strict";function i(e){var t={};return null!==e&&Object.keys(e).forEach(function(n){e[n].forEach(function(e){t[String(e)]=n})}),t}function r(e,t){if(t=t||{},Object.keys(t).forEach(function(t){if(-1===o.indexOf(t))throw new a('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')}),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=i(t.styleAliases||null),-1===s.indexOf(this.kind))throw new a('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}var a=e("./exception"),o=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],s=["scalar","sequence","mapping"];t.exports=r},{"./exception":23}],33:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t,n,i=0,r=e.length,a=p;for(n=0;r>n;n++)if(t=a.indexOf(e.charAt(n)),!(t>64)){if(0>t)return!1;i+=6}return i%8===0}function r(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,a=p,o=0,l=[];for(t=0;r>t;t++)t%4===0&&t&&(l.push(o>>16&255),l.push(o>>8&255),l.push(255&o)),o=o<<6|a.indexOf(i.charAt(t));return n=r%4*6,0===n?(l.push(o>>16&255),l.push(o>>8&255),l.push(255&o)):18===n?(l.push(o>>10&255),l.push(o>>2&255)):12===n&&l.push(o>>4&255),s?new s(l):l}function a(e){var t,n,i="",r=0,a=e.length,o=p;for(t=0;a>t;t++)t%3===0&&t&&(i+=o[r>>18&63],i+=o[r>>12&63],i+=o[r>>6&63],i+=o[63&r]),r=(r<<8)+e[t];return n=a%3,0===n?(i+=o[r>>18&63],i+=o[r>>12&63],i+=o[r>>6&63],i+=o[63&r]):2===n?(i+=o[r>>10&63],i+=o[r>>4&63],i+=o[r<<2&63],i+=o[64]):1===n&&(i+=o[r>>2&63],i+=o[r<<4&63],i+=o[64],i+=o[64]),i}function o(e){return s&&s.isBuffer(e)}var s;try{var l=e;s=l("buffer").Buffer}catch(u){}var c=e("../type"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";t.exports=new c("tag:yaml.org,2002:binary",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../type":32}],34:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)}function r(e){return"true"===e||"True"===e||"TRUE"===e}function a(e){return"[object Boolean]"===Object.prototype.toString.call(e)}var o=e("../type");t.exports=new o("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":32}],35:[function(e,t,n){"use strict";function i(e){return null===e?!1:!!u.test(e)}function r(e){var t,n,i,r;return t=e.replace(/_/g,"").toLowerCase(),n="-"===t[0]?-1:1,r=[],"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:t.indexOf(":")>=0?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)}function a(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(e))return"-0.0";return n=e.toString(10),c.test(n)?n.replace("e",".e"):n}function o(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!==0||s.isNegativeZero(e))}var s=e("../common"),l=e("../type"),u=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),c=/^[-+]?[0-9]+e/;t.exports=new l("tag:yaml.org,2002:float",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a,defaultStyle:"lowercase"})},{"../common":21,"../type":32}],36:[function(e,t,n){"use strict";function i(e){return e>=48&&57>=e||e>=65&&70>=e||e>=97&&102>=e}function r(e){return e>=48&&55>=e}function a(e){return e>=48&&57>=e}function o(e){if(null===e)return!1;var t,n=e.length,o=0,s=!1;if(!n)return!1;if(t=e[o],"-"!==t&&"+"!==t||(t=e[++o]),"0"===t){if(o+1===n)return!0;if(t=e[++o],"b"===t){for(o++;n>o;o++)if(t=e[o],"_"!==t){if("0"!==t&&"1"!==t)return!1;s=!0}return s}if("x"===t){for(o++;n>o;o++)if(t=e[o],"_"!==t){if(!i(e.charCodeAt(o)))return!1;s=!0}return s}for(;n>o;o++)if(t=e[o],"_"!==t){if(!r(e.charCodeAt(o)))return!1;s=!0}return s}for(;n>o;o++)if(t=e[o],"_"!==t){if(":"===t)break;if(!a(e.charCodeAt(o)))return!1;s=!0}return s?":"!==t?!0:/^(:[0-5]?[0-9])+$/.test(e.slice(o)):!1}function s(e){var t,n,i=e,r=1,a=[];return-1!==i.indexOf("_")&&(i=i.replace(/_/g,"")),t=i[0],"-"!==t&&"+"!==t||("-"===t&&(r=-1),i=i.slice(1),t=i[0]),"0"===i?0:"0"===t?"b"===i[1]?r*parseInt(i.slice(2),2):"x"===i[1]?r*parseInt(i,16):r*parseInt(i,8):-1!==i.indexOf(":")?(i.split(":").forEach(function(e){a.unshift(parseInt(e,10))}),i=0,n=1,a.forEach(function(e){i+=e*n,n*=60}),r*i):r*parseInt(i,10)}function l(e){return"[object Number]"===Object.prototype.toString.call(e)&&e%1===0&&!u.isNegativeZero(e)}var u=e("../common"),c=e("../type");t.exports=new c("tag:yaml.org,2002:int",{kind:"scalar",resolve:o,construct:s,predicate:l,represent:{binary:function(e){return"0b"+e.toString(2)},octal:function(e){return"0"+e.toString(8)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return"0x"+e.toString(16).toUpperCase()}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},{"../common":21,"../type":32}],37:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;try{var t="("+e+")",n=s.parse(t,{range:!0});return"Program"===n.type&&1===n.body.length&&"ExpressionStatement"===n.body[0].type&&"FunctionExpression"===n.body[0].expression.type}catch(i){return!1}}function r(e){var t,n="("+e+")",i=s.parse(n,{range:!0}),r=[];if("Program"!==i.type||1!==i.body.length||"ExpressionStatement"!==i.body[0].type||"FunctionExpression"!==i.body[0].expression.type)throw new Error("Failed to resolve function");return i.body[0].expression.params.forEach(function(e){r.push(e.name)}),t=i.body[0].expression.body.range,new Function(r,n.slice(t[0]+1,t[1]-1))}function a(e){return e.toString()}function o(e){return"[object Function]"===Object.prototype.toString.call(e)}var s;try{var l=e;s=l("esprima")}catch(u){"undefined"!=typeof window&&(s=window.esprima)}var c=e("../../type");t.exports=new c("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],38:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;if(0===e.length)return!1;var t=e,n=/\/([gim]*)$/.exec(e),i="";if("/"===t[0]){if(n&&(i=n[1]),i.length>3)return!1;if("/"!==t[t.length-i.length-1])return!1}return!0}function r(e){var t=e,n=/\/([gim]*)$/.exec(e),i="";return"/"===t[0]&&(n&&(i=n[1]),t=t.slice(1,t.length-i.length-1)),new RegExp(t,i)}function a(e){var t="/"+e.source+"/";return e.global&&(t+="g"),e.multiline&&(t+="m"),e.ignoreCase&&(t+="i"),t}function o(e){return"[object RegExp]"===Object.prototype.toString.call(e)}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],39:[function(e,t,n){"use strict";function i(){return!0}function r(){}function a(){return""}function o(e){return"undefined"==typeof e}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],40:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}})},{"../type":32}],41:[function(e,t,n){"use strict";function i(e){return"<<"===e||null===e}var r=e("../type");t.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},{"../type":32}],42:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)}function r(){return null}function a(e){return null===e}var o=e("../type");t.exports=new o("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},{"../type":32}],43:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,a,l=[],u=e;for(t=0,n=u.length;n>t;t+=1){if(i=u[t],a=!1,"[object Object]"!==s.call(i))return!1;for(r in i)if(o.call(i,r)){if(a)return!1;a=!0}if(!a)return!1;if(-1!==l.indexOf(r))return!1;l.push(r)}return!0}function r(e){return null!==e?e:[]}var a=e("../type"),o=Object.prototype.hasOwnProperty,s=Object.prototype.toString;t.exports=new a("tag:yaml.org,2002:omap",{kind:"sequence",resolve:i,construct:r})},{"../type":32}],44:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,a,s=e;for(a=new Array(s.length),t=0,n=s.length;n>t;t+=1){if(i=s[t],"[object Object]"!==o.call(i))return!1;if(r=Object.keys(i),1!==r.length)return!1;a[t]=[r[0],i[r[0]]]}return!0}function r(e){if(null===e)return[];var t,n,i,r,a,o=e;for(a=new Array(o.length),t=0,n=o.length;n>t;t+=1)i=o[t],r=Object.keys(i),a[t]=[r[0],i[r[0]]];return a}var a=e("../type"),o=Object.prototype.toString;t.exports=new a("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:i,construct:r})},{"../type":32}],45:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}})},{"../type":32}],46:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n=e;for(t in n)if(o.call(n,t)&&null!==n[t])return!1;return!0}function r(e){return null!==e?e:{}}var a=e("../type"),o=Object.prototype.hasOwnProperty;t.exports=new a("tag:yaml.org,2002:set",{kind:"mapping",resolve:i,construct:r})},{"../type":32}],47:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}})},{"../type":32}],48:[function(e,t,n){"use strict";function i(e){return null===e?!1:null!==s.exec(e)?!0:null!==l.exec(e)}function r(e){var t,n,i,r,a,o,u,c,p,h,f=0,d=null;if(t=s.exec(e),null===t&&(t=l.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(a=+t[4],o=+t[5],u=+t[6],t[7]){for(f=t[7].slice(0,3);f.length<3;)f+="0";f=+f}return t[9]&&(c=+t[10],p=+(t[11]||0),d=6e4*(60*c+p),"-"===t[9]&&(d=-d)),h=new Date(Date.UTC(n,i,r,a,o,u,f)),d&&h.setTime(h.getTime()-d),h}function a(e){return e.toISOString()}var o=e("../type"),s=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),l=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");t.exports=new o("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:r,instanceOf:Date,represent:a})},{"../type":32}],49:[function(e,t,n){function i(e,t,n){var i=e?e.length:0;if(!i)return-1;if("number"==typeof n)n=0>n?o(i+n,0):n;else if(n){var s=a(e,t);return i>s&&(t===t?t===e[s]:e[s]!==e[s])?s:-1}return r(e,t,n||0)}var r=e("../internal/baseIndexOf"),a=e("../internal/binaryIndex"),o=Math.max;t.exports=i},{"../internal/baseIndexOf":78,"../internal/binaryIndex":92}],50:[function(e,t,n){function i(e){var t=e?e.length:0;return t?e[t-1]:void 0}t.exports=i},{}],51:[function(e,t,n){function i(e){if(l(e)&&!s(e)&&!(e instanceof r)){if(e instanceof a)return e;if(p.call(e,"__chain__")&&p.call(e,"__wrapped__"))return u(e)}return new a(e)}var r=e("../internal/LazyWrapper"),a=e("../internal/LodashWrapper"),o=e("../internal/baseLodash"),s=e("../lang/isArray"),l=e("../internal/isObjectLike"),u=e("../internal/wrapperClone"),c=Object.prototype,p=c.hasOwnProperty;i.prototype=o.prototype,t.exports=i},{"../internal/LazyWrapper":60,"../internal/LodashWrapper":61,"../internal/baseLodash":82,"../internal/isObjectLike":126,"../internal/wrapperClone":137,"../lang/isArray":140}],52:[function(e,t,n){t.exports=e("./forEach")},{"./forEach":54}],53:[function(e,t,n){var i=e("../internal/baseEach"),r=e("../internal/createFind"),a=r(i);t.exports=a},{"../internal/baseEach":71,"../internal/createFind":102}],54:[function(e,t,n){var i=e("../internal/arrayEach"),r=e("../internal/baseEach"),a=e("../internal/createForEach"),o=a(i,r);t.exports=o},{"../internal/arrayEach":63,"../internal/baseEach":71,"../internal/createForEach":103}],55:[function(e,t,n){function i(e,t,n,i){var h=e?a(e):0;return l(h)||(e=c(e),h=e.length),n="number"!=typeof n||i&&s(t,n,i)?0:0>n?p(h+n,0):n||0,"string"==typeof e||!o(e)&&u(e)?h>=n&&e.indexOf(t,n)>-1:!!h&&r(e,t,n)>-1}var r=e("../internal/baseIndexOf"),a=e("../internal/getLength"),o=e("../lang/isArray"),s=e("../internal/isIterateeCall"),l=e("../internal/isLength"),u=e("../lang/isString"),c=e("../object/values"),p=Math.max;t.exports=i},{"../internal/baseIndexOf":78,"../internal/getLength":112,"../internal/isIterateeCall":122,"../internal/isLength":125,"../lang/isArray":140,"../lang/isString":146,"../object/values":152}],56:[function(e,t,n){function i(e,t,n){var i=s(e)?r:o;return t=a(t,n,3),i(e,t)}var r=e("../internal/arrayMap"),a=e("../internal/baseCallback"),o=e("../internal/baseMap"),s=e("../lang/isArray");t.exports=i},{"../internal/arrayMap":64,"../internal/baseCallback":67,"../internal/baseMap":83,"../lang/isArray":140}],57:[function(e,t,n){var i=e("../internal/getNative"),r=i(Date,"now"),a=r||function(){return(new Date).getTime()};t.exports=a},{"../internal/getNative":114}],58:[function(e,t,n){var i=e("../internal/createWrapper"),r=e("../internal/replaceHolders"),a=e("./restParam"),o=1,s=32,l=a(function(e,t,n){var a=o;if(n.length){var u=r(n,l.placeholder);a|=s}return i(e,a,t,n,u)});l.placeholder={},t.exports=l},{"../internal/createWrapper":106,"../internal/replaceHolders":132,"./restParam":59}],59:[function(e,t,n){function i(e,t){if("function"!=typeof e)throw new TypeError(r);return t=a(void 0===t?e.length-1:+t||0,0),function(){for(var n=arguments,i=-1,r=a(n.length-t,0),o=Array(r);++i<r;)o[i]=n[t+i];switch(t){case 0:return e.call(this,o);case 1:return e.call(this,n[0],o);case 2:return e.call(this,n[0],n[1],o)}var s=Array(t+1);for(i=-1;++i<t;)s[i]=n[i];return s[t]=o,e.apply(this,s)}}var r="Expected a function",a=Math.max;t.exports=i},{}],60:[function(e,t,n){function i(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=o,this.__views__=[]}var r=e("./baseCreate"),a=e("./baseLodash"),o=Number.POSITIVE_INFINITY;i.prototype=r(a.prototype),i.prototype.constructor=i,t.exports=i},{"./baseCreate":70,"./baseLodash":82}],61:[function(e,t,n){function i(e,t,n){this.__wrapped__=e,this.__actions__=n||[],this.__chain__=!!t}var r=e("./baseCreate"),a=e("./baseLodash");i.prototype=r(a.prototype),i.prototype.constructor=i,t.exports=i},{"./baseCreate":70,"./baseLodash":82}],62:[function(e,t,n){function i(e,t){var n=-1,i=e.length;for(t||(t=Array(i));++n<i;)t[n]=e[n];return t}t.exports=i},{}],63:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length;++n<i&&t(e[n],n,e)!==!1;);return e}t.exports=i},{}],64:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length,r=Array(i);++n<i;)r[n]=t(e[n],n,e);return r}t.exports=i},{}],65:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length;++n<i;)if(t(e[n],n,e))return!0;return!1}t.exports=i},{}],66:[function(e,t,n){function i(e,t){return null==t?e:r(t,a(t),e)}var r=e("./baseCopy"),a=e("../object/keys");t.exports=i},{"../object/keys":149,"./baseCopy":69}],67:[function(e,t,n){function i(e,t,n){var i=typeof e;return"function"==i?void 0===t?e:o(e,t,n):null==e?s:"object"==i?r(e):void 0===t?l(e):a(e,t)}var r=e("./baseMatches"),a=e("./baseMatchesProperty"),o=e("./bindCallback"),s=e("../utility/identity"),l=e("../utility/property");t.exports=i},{"../utility/identity":154,"../utility/property":156,"./baseMatches":84,"./baseMatchesProperty":85,"./bindCallback":94}],68:[function(e,t,n){function i(e,t,n,m,y,g,v){var w;if(n&&(w=y?n(e,m,y):n(e)),void 0!==w)return w;if(!f(e))return e;var x=p(e);if(x){if(w=l(e),!t)return r(e,w)}else{var j=N.call(e),O=j==b;if(j!=A&&j!=d&&(!O||y))return D[j]?u(e,j,t):y?e:{};if(h(e))return y?e:{};if(w=c(O?{}:e),!t)return o(w,e)}g||(g=[]),v||(v=[]);for(var _=g.length;_--;)if(g[_]==e)return v[_];return g.push(e),v.push(w),(x?a:s)(e,function(r,a){w[a]=i(r,t,n,a,e,g,v)}),w}var r=e("./arrayCopy"),a=e("./arrayEach"),o=e("./baseAssign"),s=e("./baseForOwn"),l=e("./initCloneArray"),u=e("./initCloneByTag"),c=e("./initCloneObject"),p=e("../lang/isArray"),h=e("./isHostObject"),f=e("../lang/isObject"),d="[object Arguments]",m="[object Array]",y="[object Boolean]",g="[object Date]",v="[object Error]",b="[object Function]",w="[object Map]",x="[object Number]",A="[object Object]",j="[object RegExp]",O="[object Set]",_="[object String]",S="[object WeakMap]",k="[object ArrayBuffer]",C="[object Float32Array]",E="[object Float64Array]",I="[object Int8Array]",T="[object Int16Array]",$="[object Int32Array]",M="[object Uint8Array]",U="[object Uint8ClampedArray]",P="[object Uint16Array]",L="[object Uint32Array]",D={};D[d]=D[m]=D[k]=D[y]=D[g]=D[C]=D[E]=D[I]=D[T]=D[$]=D[x]=D[A]=D[j]=D[_]=D[M]=D[U]=D[P]=D[L]=!0,D[v]=D[b]=D[w]=D[O]=D[S]=!1;var R=Object.prototype,N=R.toString;t.exports=i},{"../lang/isArray":140,"../lang/isObject":144,"./arrayCopy":62,"./arrayEach":63,"./baseAssign":66,"./baseForOwn":76,"./initCloneArray":116,"./initCloneByTag":117,"./initCloneObject":118,"./isHostObject":120}],69:[function(e,t,n){function i(e,t,n){n||(n={});for(var i=-1,r=t.length;++i<r;){var a=t[i];n[a]=e[a]}return n}t.exports=i},{}],70:[function(e,t,n){var i=e("../lang/isObject"),r=function(){function e(){}return function(t){if(i(t)){e.prototype=t;var n=new e;e.prototype=void 0}return n||{}}}();t.exports=r},{"../lang/isObject":144}],71:[function(e,t,n){var i=e("./baseForOwn"),r=e("./createBaseEach"),a=r(i);t.exports=a},{"./baseForOwn":76,"./createBaseEach":98}],72:[function(e,t,n){function i(e,t,n,i){var r;return n(e,function(e,n,a){return t(e,n,a)?(r=i?n:e,!1):void 0}),r}t.exports=i},{}],73:[function(e,t,n){function i(e,t,n){for(var i=e.length,r=n?i:-1;n?r--:++r<i;)if(t(e[r],r,e))return r;return-1}t.exports=i},{}],74:[function(e,t,n){var i=e("./createBaseFor"),r=i();t.exports=r},{"./createBaseFor":99}],75:[function(e,t,n){function i(e,t){return r(e,t,a)}var r=e("./baseFor"),a=e("../object/keysIn");t.exports=i},{"../object/keysIn":150,"./baseFor":74}],76:[function(e,t,n){function i(e,t){return r(e,t,a)}var r=e("./baseFor"),a=e("../object/keys");t.exports=i},{"../object/keys":149,"./baseFor":74}],77:[function(e,t,n){function i(e,t,n){if(null!=e){e=r(e),void 0!==n&&n in e&&(t=[n]);for(var i=0,a=t.length;null!=e&&a>i;)e=r(e)[t[i++]];return i&&i==a?e:void 0}}var r=e("./toObject");t.exports=i},{"./toObject":135}],78:[function(e,t,n){function i(e,t,n){if(t!==t)return r(e,n);for(var i=n-1,a=e.length;++i<a;)if(e[i]===t)return i;return-1}var r=e("./indexOfNaN");t.exports=i},{"./indexOfNaN":115}],79:[function(e,t,n){function i(e,t,n,s,l,u){return e===t?!0:null==e||null==t||!a(e)&&!o(t)?e!==e&&t!==t:r(e,t,i,n,s,l,u)}var r=e("./baseIsEqualDeep"),a=e("../lang/isObject"),o=e("./isObjectLike");t.exports=i},{"../lang/isObject":144,"./baseIsEqualDeep":80,"./isObjectLike":126}],80:[function(e,t,n){function i(e,t,n,i,f,y,g){var v=s(e),b=s(t),w=p,x=p;v||(w=m.call(e),w==c?w=h:w!=h&&(v=u(e))),b||(x=m.call(t),x==c?x=h:x!=h&&(b=u(t)));var A=w==h&&!l(e),j=x==h&&!l(t),O=w==x;if(O&&!v&&!A)return a(e,t,w);if(!f){var _=A&&d.call(e,"__wrapped__"),S=j&&d.call(t,"__wrapped__");if(_||S)return n(_?e.value():e,S?t.value():t,i,f,y,g)}if(!O)return!1;y||(y=[]),g||(g=[]);for(var k=y.length;k--;)if(y[k]==e)return g[k]==t;y.push(e),g.push(t);var C=(v?r:o)(e,t,n,i,f,y,g);return y.pop(),g.pop(),C}var r=e("./equalArrays"),a=e("./equalByTag"),o=e("./equalObjects"),s=e("../lang/isArray"),l=e("./isHostObject"),u=e("../lang/isTypedArray"),c="[object Arguments]",p="[object Array]",h="[object Object]",f=Object.prototype,d=f.hasOwnProperty,m=f.toString;t.exports=i},{"../lang/isArray":140,"../lang/isTypedArray":147,"./equalArrays":107,"./equalByTag":108,"./equalObjects":109,"./isHostObject":120}],81:[function(e,t,n){function i(e,t,n){var i=t.length,o=i,s=!n;if(null==e)return!o;for(e=a(e);i--;){var l=t[i];if(s&&l[2]?l[1]!==e[l[0]]:!(l[0]in e))return!1}for(;++i<o;){l=t[i];var u=l[0],c=e[u],p=l[1];if(s&&l[2]){if(void 0===c&&!(u in e))return!1}else{var h=n?n(c,p,u):void 0;if(!(void 0===h?r(p,c,n,!0):h))return!1}}return!0}var r=e("./baseIsEqual"),a=e("./toObject");t.exports=i},{"./baseIsEqual":79,"./toObject":135}],82:[function(e,t,n){function i(){}t.exports=i},{}],83:[function(e,t,n){function i(e,t){var n=-1,i=a(e)?Array(e.length):[];return r(e,function(e,r,a){i[++n]=t(e,r,a)}),i}var r=e("./baseEach"),a=e("./isArrayLike");t.exports=i},{"./baseEach":71,"./isArrayLike":119}],84:[function(e,t,n){function i(e){var t=a(e);if(1==t.length&&t[0][2]){var n=t[0][0],i=t[0][1];return function(e){return null==e?!1:(e=o(e),e[n]===i&&(void 0!==i||n in e))}}return function(e){return r(e,t)}}var r=e("./baseIsMatch"),a=e("./getMatchData"),o=e("./toObject");t.exports=i},{"./baseIsMatch":81,"./getMatchData":113,"./toObject":135}],85:[function(e,t,n){function i(e,t){var n=s(e),i=l(e)&&u(t),f=e+"";return e=h(e),function(s){if(null==s)return!1;var l=f;if(s=p(s),(n||!i)&&!(l in s)){if(s=1==e.length?s:r(s,o(e,0,-1)),null==s)return!1;l=c(e),s=p(s)}return s[l]===t?void 0!==t||l in s:a(t,s[l],void 0,!0)}}var r=e("./baseGet"),a=e("./baseIsEqual"),o=e("./baseSlice"),s=e("../lang/isArray"),l=e("./isKey"),u=e("./isStrictComparable"),c=e("../array/last"),p=e("./toObject"),h=e("./toPath");t.exports=i},{"../array/last":50,"../lang/isArray":140,"./baseGet":77,"./baseIsEqual":79,"./baseSlice":89,"./isKey":123,"./isStrictComparable":127,"./toObject":135,"./toPath":136}],86:[function(e,t,n){function i(e){return function(t){return null==t?void 0:r(t)[e]}}var r=e("./toObject");t.exports=i},{"./toObject":135}],87:[function(e,t,n){function i(e){var t=e+"";return e=a(e),function(n){return r(n,e,t)}}var r=e("./baseGet"),a=e("./toPath");t.exports=i},{"./baseGet":77,"./toPath":136}],88:[function(e,t,n){var i=e("../utility/identity"),r=e("./metaMap"),a=r?function(e,t){return r.set(e,t),e}:i;t.exports=a},{"../utility/identity":154,"./metaMap":129}],89:[function(e,t,n){function i(e,t,n){var i=-1,r=e.length;t=null==t?0:+t||0,0>t&&(t=-t>r?0:r+t),n=void 0===n||n>r?r:+n||0,0>n&&(n+=r),r=t>n?0:n-t>>>0,t>>>=0;for(var a=Array(r);++i<r;)a[i]=e[i+t];return a}t.exports=i},{}],90:[function(e,t,n){function i(e){return null==e?"":e+""}t.exports=i},{}],91:[function(e,t,n){function i(e,t){for(var n=-1,i=t.length,r=Array(i);++n<i;)r[n]=e[t[n]];return r}t.exports=i},{}],92:[function(e,t,n){function i(e,t,n){var i=0,o=e?e.length:i;if("number"==typeof t&&t===t&&s>=o){for(;o>i;){var l=i+o>>>1,u=e[l];(n?t>=u:t>u)&&null!==u?i=l+1:o=l}return o}return r(e,t,a,n)}var r=e("./binaryIndexBy"),a=e("../utility/identity"),o=4294967295,s=o>>>1;t.exports=i},{"../utility/identity":154,"./binaryIndexBy":93}],93:[function(e,t,n){function i(e,t,n,i){t=n(t);for(var o=0,l=e?e.length:0,u=t!==t,c=null===t,p=void 0===t;l>o;){var h=r((o+l)/2),f=n(e[h]),d=void 0!==f,m=f===f;if(u)var y=m||i;else y=c?m&&d&&(i||null!=f):p?m&&(i||d):null==f?!1:i?t>=f:t>f;y?o=h+1:l=h}return a(l,s)}var r=Math.floor,a=Math.min,o=4294967295,s=o-1;t.exports=i},{}],94:[function(e,t,n){function i(e,t,n){if("function"!=typeof e)return r;if(void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,i,r){return e.call(t,n,i,r)};case 4:return function(n,i,r,a){return e.call(t,n,i,r,a)};case 5:return function(n,i,r,a,o){return e.call(t,n,i,r,a,o)}}return function(){return e.apply(t,arguments)}}var r=e("../utility/identity");t.exports=i},{"../utility/identity":154}],95:[function(e,t,n){(function(e){function n(e){var t=new i(e.byteLength),n=new r(t);return n.set(new r(e)),t}var i=e.ArrayBuffer,r=e.Uint8Array;t.exports=n}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],96:[function(e,t,n){function i(e,t,n){for(var i=n.length,a=-1,o=r(e.length-i,0),s=-1,l=t.length,u=Array(l+o);++s<l;)u[s]=t[s];for(;++a<i;)u[n[a]]=e[a];for(;o--;)u[s++]=e[a++];return u}var r=Math.max;t.exports=i},{}],97:[function(e,t,n){function i(e,t,n){for(var i=-1,a=n.length,o=-1,s=r(e.length-a,0),l=-1,u=t.length,c=Array(s+u);++o<s;)c[o]=e[o];for(var p=o;++l<u;)c[p+l]=t[l];for(;++i<a;)c[p+n[i]]=e[o++];return c}var r=Math.max;t.exports=i},{}],98:[function(e,t,n){function i(e,t){return function(n,i){var s=n?r(n):0;if(!a(s))return e(n,i);for(var l=t?s:-1,u=o(n);(t?l--:++l<s)&&i(u[l],l,u)!==!1;);return n}}var r=e("./getLength"),a=e("./isLength"),o=e("./toObject");t.exports=i},{"./getLength":112,"./isLength":125,"./toObject":135}],99:[function(e,t,n){function i(e){return function(t,n,i){for(var a=r(t),o=i(t),s=o.length,l=e?s:-1;e?l--:++l<s;){var u=o[l];if(n(a[u],u,a)===!1)break}return t}}var r=e("./toObject");t.exports=i},{"./toObject":135}],100:[function(e,t,n){(function(n){function i(e,t){function i(){var r=this&&this!==n&&this instanceof i?a:e;return r.apply(t,arguments)}var a=r(e);return i}var r=e("./createCtorWrapper");t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":101}],101:[function(e,t,n){function i(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:
+return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=r(e.prototype),i=e.apply(n,t);return a(i)?i:n}}var r=e("./baseCreate"),a=e("../lang/isObject");t.exports=i},{"../lang/isObject":144,"./baseCreate":70}],102:[function(e,t,n){function i(e,t){return function(n,i,l){if(i=r(i,l,3),s(n)){var u=o(n,i,t);return u>-1?n[u]:void 0}return a(n,i,e)}}var r=e("./baseCallback"),a=e("./baseFind"),o=e("./baseFindIndex"),s=e("../lang/isArray");t.exports=i},{"../lang/isArray":140,"./baseCallback":67,"./baseFind":72,"./baseFindIndex":73}],103:[function(e,t,n){function i(e,t){return function(n,i,o){return"function"==typeof i&&void 0===o&&a(n)?e(n,i):t(n,r(i,o,3))}}var r=e("./bindCallback"),a=e("../lang/isArray");t.exports=i},{"../lang/isArray":140,"./bindCallback":94}],104:[function(e,t,n){(function(n){function i(e,t,x,A,j,O,_,S,k,C){function E(){for(var d=arguments.length,m=d,y=Array(d);m--;)y[m]=arguments[m];if(A&&(y=a(y,A,j)),O&&(y=o(y,O,_)),M||P){var b=E.placeholder,D=c(y,b);if(d-=D.length,C>d){var R=S?r(S):void 0,N=w(C-d,0),F=M?D:void 0,B=M?void 0:D,q=M?y:void 0,V=M?void 0:y;t|=M?g:v,t&=~(M?v:g),U||(t&=~(h|f));var z=[e,t,x,q,F,V,B,R,k,N],H=i.apply(void 0,z);return l(e)&&p(H,z),H.placeholder=b,H}}var J=T?x:this,Q=$?J[e]:e;return S&&(y=u(y,S)),I&&k<y.length&&(y.length=k),this&&this!==n&&this instanceof E&&(Q=L||s(e)),Q.apply(J,y)}var I=t&b,T=t&h,$=t&f,M=t&m,U=t&d,P=t&y,L=$?void 0:s(e);return E}var r=e("./arrayCopy"),a=e("./composeArgs"),o=e("./composeArgsRight"),s=e("./createCtorWrapper"),l=e("./isLaziable"),u=e("./reorder"),c=e("./replaceHolders"),p=e("./setData"),h=1,f=2,d=4,m=8,y=16,g=32,v=64,b=128,w=Math.max;t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./arrayCopy":62,"./composeArgs":96,"./composeArgsRight":97,"./createCtorWrapper":101,"./isLaziable":124,"./reorder":131,"./replaceHolders":132,"./setData":133}],105:[function(e,t,n){(function(n){function i(e,t,i,o){function s(){for(var t=-1,r=arguments.length,a=-1,c=o.length,p=Array(c+r);++a<c;)p[a]=o[a];for(;r--;)p[a++]=arguments[++t];var h=this&&this!==n&&this instanceof s?u:e;return h.apply(l?i:this,p)}var l=t&a,u=r(e);return s}var r=e("./createCtorWrapper"),a=1;t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":101}],106:[function(e,t,n){function i(e,t,n,i,g,v,b,w){var x=t&h;if(!x&&"function"!=typeof e)throw new TypeError(m);var A=i?i.length:0;if(A||(t&=~(f|d),i=g=void 0),A-=g?g.length:0,t&d){var j=i,O=g;i=g=void 0}var _=x?void 0:l(e),S=[e,t,n,i,g,j,O,v,b,w];if(_&&(u(S,_),t=S[1],w=S[9]),S[9]=null==w?x?0:e.length:y(w-A,0)||0,t==p)var k=a(S[0],S[2]);else k=t!=f&&t!=(p|f)||S[4].length?o.apply(void 0,S):s.apply(void 0,S);var C=_?r:c;return C(k,S)}var r=e("./baseSetData"),a=e("./createBindWrapper"),o=e("./createHybridWrapper"),s=e("./createPartialWrapper"),l=e("./getData"),u=e("./mergeData"),c=e("./setData"),p=1,h=2,f=32,d=64,m="Expected a function",y=Math.max;t.exports=i},{"./baseSetData":88,"./createBindWrapper":100,"./createHybridWrapper":104,"./createPartialWrapper":105,"./getData":110,"./mergeData":128,"./setData":133}],107:[function(e,t,n){function i(e,t,n,i,a,o,s){var l=-1,u=e.length,c=t.length;if(u!=c&&!(a&&c>u))return!1;for(;++l<u;){var p=e[l],h=t[l],f=i?i(a?h:p,a?p:h,l):void 0;if(void 0!==f){if(f)continue;return!1}if(a){if(!r(t,function(e){return p===e||n(p,e,i,a,o,s)}))return!1}else if(p!==h&&!n(p,h,i,a,o,s))return!1}return!0}var r=e("./arraySome");t.exports=i},{"./arraySome":65}],108:[function(e,t,n){function i(e,t,n){switch(n){case r:case a:return+e==+t;case o:return e.name==t.name&&e.message==t.message;case s:return e!=+e?t!=+t:e==+t;case l:case u:return e==t+""}return!1}var r="[object Boolean]",a="[object Date]",o="[object Error]",s="[object Number]",l="[object RegExp]",u="[object String]";t.exports=i},{}],109:[function(e,t,n){function i(e,t,n,i,a,s,l){var u=r(e),c=u.length,p=r(t),h=p.length;if(c!=h&&!a)return!1;for(var f=c;f--;){var d=u[f];if(!(a?d in t:o.call(t,d)))return!1}for(var m=a;++f<c;){d=u[f];var y=e[d],g=t[d],v=i?i(a?g:y,a?y:g,d):void 0;if(!(void 0===v?n(y,g,i,a,s,l):v))return!1;m||(m="constructor"==d)}if(!m){var b=e.constructor,w=t.constructor;if(b!=w&&"constructor"in e&&"constructor"in t&&!("function"==typeof b&&b instanceof b&&"function"==typeof w&&w instanceof w))return!1}return!0}var r=e("../object/keys"),a=Object.prototype,o=a.hasOwnProperty;t.exports=i},{"../object/keys":149}],110:[function(e,t,n){var i=e("./metaMap"),r=e("../utility/noop"),a=i?function(e){return i.get(e)}:r;t.exports=a},{"../utility/noop":155,"./metaMap":129}],111:[function(e,t,n){function i(e){for(var t=e.name+"",n=r[t],i=n?n.length:0;i--;){var a=n[i],o=a.func;if(null==o||o==e)return a.name}return t}var r=e("./realNames");t.exports=i},{"./realNames":130}],112:[function(e,t,n){var i=e("./baseProperty"),r=i("length");t.exports=r},{"./baseProperty":86}],113:[function(e,t,n){function i(e){for(var t=a(e),n=t.length;n--;)t[n][2]=r(t[n][1]);return t}var r=e("./isStrictComparable"),a=e("../object/pairs");t.exports=i},{"../object/pairs":151,"./isStrictComparable":127}],114:[function(e,t,n){function i(e,t){var n=null==e?void 0:e[t];return r(n)?n:void 0}var r=e("../lang/isNative");t.exports=i},{"../lang/isNative":143}],115:[function(e,t,n){function i(e,t,n){for(var i=e.length,r=t+(n?0:-1);n?r--:++r<i;){var a=e[r];if(a!==a)return r}return-1}t.exports=i},{}],116:[function(e,t,n){function i(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&a.call(e,"index")&&(n.index=e.index,n.input=e.input),n}var r=Object.prototype,a=r.hasOwnProperty;t.exports=i},{}],117:[function(e,t,n){(function(n){function i(e,t,n){var i=e.constructor;switch(t){case c:return r(e);case a:case o:return new i(+e);case p:case h:case f:case d:case m:case y:case g:case v:case b:i instanceof i&&(i=A[t]);var x=e.buffer;return new i(n?r(x):x,e.byteOffset,e.length);case s:case u:return new i(e);case l:var j=new i(e.source,w.exec(e));j.lastIndex=e.lastIndex}return j}var r=e("./bufferClone"),a="[object Boolean]",o="[object Date]",s="[object Number]",l="[object RegExp]",u="[object String]",c="[object ArrayBuffer]",p="[object Float32Array]",h="[object Float64Array]",f="[object Int8Array]",d="[object Int16Array]",m="[object Int32Array]",y="[object Uint8Array]",g="[object Uint8ClampedArray]",v="[object Uint16Array]",b="[object Uint32Array]",w=/\w*$/,x=n.Uint8Array,A={};A[p]=n.Float32Array,A[h]=n.Float64Array,A[f]=n.Int8Array,A[d]=n.Int16Array,A[m]=n.Int32Array,A[y]=x,A[g]=n.Uint8ClampedArray,A[v]=n.Uint16Array,A[b]=n.Uint32Array,t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./bufferClone":95}],118:[function(e,t,n){function i(e){var t=e.constructor;return"function"==typeof t&&t instanceof t||(t=Object),new t}t.exports=i},{}],119:[function(e,t,n){function i(e){return null!=e&&a(r(e))}var r=e("./getLength"),a=e("./isLength");t.exports=i},{"./getLength":112,"./isLength":125}],120:[function(e,t,n){var i=function(){try{Object({toString:0}+"")}catch(e){return function(){return!1}}return function(e){return"function"!=typeof e.toString&&"string"==typeof(e+"")}}();t.exports=i},{}],121:[function(e,t,n){function i(e,t){return e="number"==typeof e||r.test(e)?+e:-1,t=null==t?a:t,e>-1&&e%1==0&&t>e}var r=/^\d+$/,a=9007199254740991;t.exports=i},{}],122:[function(e,t,n){function i(e,t,n){if(!o(n))return!1;var i=typeof t;if("number"==i?r(n)&&a(t,n.length):"string"==i&&t in n){var s=n[t];return e===e?e===s:s!==s}return!1}var r=e("./isArrayLike"),a=e("./isIndex"),o=e("../lang/isObject");t.exports=i},{"../lang/isObject":144,"./isArrayLike":119,"./isIndex":121}],123:[function(e,t,n){function i(e,t){var n=typeof e;if("string"==n&&s.test(e)||"number"==n)return!0;if(r(e))return!1;var i=!o.test(e);return i||null!=t&&e in a(t)}var r=e("../lang/isArray"),a=e("./toObject"),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,s=/^\w*$/;t.exports=i},{"../lang/isArray":140,"./toObject":135}],124:[function(e,t,n){function i(e){var t=o(e),n=s[t];if("function"!=typeof n||!(t in r.prototype))return!1;if(e===n)return!0;var i=a(n);return!!i&&e===i[0]}var r=e("./LazyWrapper"),a=e("./getData"),o=e("./getFuncName"),s=e("../chain/lodash");t.exports=i},{"../chain/lodash":51,"./LazyWrapper":60,"./getData":110,"./getFuncName":111}],125:[function(e,t,n){function i(e){return"number"==typeof e&&e>-1&&e%1==0&&r>=e}var r=9007199254740991;t.exports=i},{}],126:[function(e,t,n){function i(e){return!!e&&"object"==typeof e}t.exports=i},{}],127:[function(e,t,n){function i(e){return e===e&&!r(e)}var r=e("../lang/isObject");t.exports=i},{"../lang/isObject":144}],128:[function(e,t,n){function i(e,t){var n=e[1],i=t[1],m=n|i,y=p>m,g=i==p&&n==c||i==p&&n==h&&e[7].length<=t[8]||i==(p|h)&&n==c;if(!y&&!g)return e;i&l&&(e[2]=t[2],m|=n&l?0:u);var v=t[3];if(v){var b=e[3];e[3]=b?a(b,v,t[4]):r(v),e[4]=b?s(e[3],f):r(t[4])}return v=t[5],v&&(b=e[5],e[5]=b?o(b,v,t[6]):r(v),e[6]=b?s(e[5],f):r(t[6])),v=t[7],v&&(e[7]=r(v)),i&p&&(e[8]=null==e[8]?t[8]:d(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=m,e}var r=e("./arrayCopy"),a=e("./composeArgs"),o=e("./composeArgsRight"),s=e("./replaceHolders"),l=1,u=4,c=8,p=128,h=256,f="__lodash_placeholder__",d=Math.min;t.exports=i},{"./arrayCopy":62,"./composeArgs":96,"./composeArgsRight":97,"./replaceHolders":132}],129:[function(e,t,n){(function(n){var i=e("./getNative"),r=i(n,"WeakMap"),a=r&&new r;t.exports=a}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./getNative":114}],130:[function(e,t,n){var i={};t.exports=i},{}],131:[function(e,t,n){function i(e,t){for(var n=e.length,i=o(t.length,n),s=r(e);i--;){var l=t[i];e[i]=a(l,n)?s[l]:void 0}return e}var r=e("./arrayCopy"),a=e("./isIndex"),o=Math.min;t.exports=i},{"./arrayCopy":62,"./isIndex":121}],132:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length,a=-1,o=[];++n<i;)e[n]===t&&(e[n]=r,o[++a]=n);return o}var r="__lodash_placeholder__";t.exports=i},{}],133:[function(e,t,n){var i=e("./baseSetData"),r=e("../date/now"),a=150,o=16,s=function(){var e=0,t=0;return function(n,s){var l=r(),u=o-(l-t);if(t=l,u>0){if(++e>=a)return n}else e=0;return i(n,s)}}();t.exports=s},{"../date/now":57,"./baseSetData":88}],134:[function(e,t,n){function i(e){for(var t=u(e),n=t.length,i=n&&e.length,c=!!i&&s(i)&&(a(e)||r(e)||l(e)),h=-1,f=[];++h<n;){var d=t[h];(c&&o(d,i)||p.call(e,d))&&f.push(d)}return f}var r=e("../lang/isArguments"),a=e("../lang/isArray"),o=e("./isIndex"),s=e("./isLength"),l=e("../lang/isString"),u=e("../object/keysIn"),c=Object.prototype,p=c.hasOwnProperty;t.exports=i},{"../lang/isArguments":139,"../lang/isArray":140,"../lang/isString":146,"../object/keysIn":150,"./isIndex":121,"./isLength":125}],135:[function(e,t,n){function i(e){if(o.unindexedChars&&a(e)){for(var t=-1,n=e.length,i=Object(e);++t<n;)i[t]=e.charAt(t);return i}return r(e)?e:Object(e)}var r=e("../lang/isObject"),a=e("../lang/isString"),o=e("../support");t.exports=i},{"../lang/isObject":144,"../lang/isString":146,"../support":153}],136:[function(e,t,n){function i(e){if(a(e))return e;var t=[];return r(e).replace(o,function(e,n,i,r){t.push(i?r.replace(s,"$1"):n||e)}),t}var r=e("./baseToString"),a=e("../lang/isArray"),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,s=/\\(\\)?/g;t.exports=i},{"../lang/isArray":140,"./baseToString":90}],137:[function(e,t,n){function i(e){return e instanceof r?e.clone():new a(e.__wrapped__,e.__chain__,o(e.__actions__))}var r=e("./LazyWrapper"),a=e("./LodashWrapper"),o=e("./arrayCopy");t.exports=i},{"./LazyWrapper":60,"./LodashWrapper":61,"./arrayCopy":62}],138:[function(e,t,n){function i(e,t,n){return"function"==typeof t?r(e,!0,a(t,n,3)):r(e,!0)}var r=e("../internal/baseClone"),a=e("../internal/bindCallback");t.exports=i},{"../internal/baseClone":68,"../internal/bindCallback":94}],139:[function(e,t,n){function i(e){return a(e)&&r(e)&&s.call(e,"callee")&&!l.call(e,"callee")}var r=e("../internal/isArrayLike"),a=e("../internal/isObjectLike"),o=Object.prototype,s=o.hasOwnProperty,l=o.propertyIsEnumerable;t.exports=i},{"../internal/isArrayLike":119,"../internal/isObjectLike":126}],140:[function(e,t,n){var i=e("../internal/getNative"),r=e("../internal/isLength"),a=e("../internal/isObjectLike"),o="[object Array]",s=Object.prototype,l=s.toString,u=i(Array,"isArray"),c=u||function(e){return a(e)&&r(e.length)&&l.call(e)==o};t.exports=c},{"../internal/getNative":114,"../internal/isLength":125,"../internal/isObjectLike":126}],141:[function(e,t,n){function i(e){return null==e?!0:o(e)&&(a(e)||u(e)||r(e)||l(e)&&s(e.splice))?!e.length:!c(e).length}var r=e("./isArguments"),a=e("./isArray"),o=e("../internal/isArrayLike"),s=e("./isFunction"),l=e("../internal/isObjectLike"),u=e("./isString"),c=e("../object/keys");t.exports=i},{"../internal/isArrayLike":119,"../internal/isObjectLike":126,"../object/keys":149,"./isArguments":139,"./isArray":140,"./isFunction":142,"./isString":146}],142:[function(e,t,n){function i(e){return r(e)&&s.call(e)==a}var r=e("./isObject"),a="[object Function]",o=Object.prototype,s=o.toString;t.exports=i},{"./isObject":144}],143:[function(e,t,n){function i(e){return null==e?!1:r(e)?p.test(u.call(e)):o(e)&&(a(e)?p:s).test(e)}var r=e("./isFunction"),a=e("../internal/isHostObject"),o=e("../internal/isObjectLike"),s=/^\[object .+?Constructor\]$/,l=Object.prototype,u=Function.prototype.toString,c=l.hasOwnProperty,p=RegExp("^"+u.call(c).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=i},{"../internal/isHostObject":120,"../internal/isObjectLike":126,"./isFunction":142}],144:[function(e,t,n){function i(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}t.exports=i},{}],145:[function(e,t,n){function i(e){var t;if(!s(e)||h.call(e)!=u||o(e)||a(e)||!p.call(e,"constructor")&&(t=e.constructor,"function"==typeof t&&!(t instanceof t)))return!1;var n;return l.ownLast?(r(e,function(e,t,i){return n=p.call(i,t),!1}),n!==!1):(r(e,function(e,t){n=t}),void 0===n||p.call(e,n))}var r=e("../internal/baseForIn"),a=e("./isArguments"),o=e("../internal/isHostObject"),s=e("../internal/isObjectLike"),l=e("../support"),u="[object Object]",c=Object.prototype,p=c.hasOwnProperty,h=c.toString;t.exports=i},{"../internal/baseForIn":75,"../internal/isHostObject":120,"../internal/isObjectLike":126,"../support":153,"./isArguments":139}],146:[function(e,t,n){function i(e){return"string"==typeof e||r(e)&&s.call(e)==a}var r=e("../internal/isObjectLike"),a="[object String]",o=Object.prototype,s=o.toString;t.exports=i},{"../internal/isObjectLike":126}],147:[function(e,t,n){function i(e){return a(e)&&r(e.length)&&!!E[T.call(e)]}var r=e("../internal/isLength"),a=e("../internal/isObjectLike"),o="[object Arguments]",s="[object Array]",l="[object Boolean]",u="[object Date]",c="[object Error]",p="[object Function]",h="[object Map]",f="[object Number]",d="[object Object]",m="[object RegExp]",y="[object Set]",g="[object String]",v="[object WeakMap]",b="[object ArrayBuffer]",w="[object Float32Array]",x="[object Float64Array]",A="[object Int8Array]",j="[object Int16Array]",O="[object Int32Array]",_="[object Uint8Array]",S="[object Uint8ClampedArray]",k="[object Uint16Array]",C="[object Uint32Array]",E={};E[w]=E[x]=E[A]=E[j]=E[O]=E[_]=E[S]=E[k]=E[C]=!0,E[o]=E[s]=E[b]=E[l]=E[u]=E[c]=E[p]=E[h]=E[f]=E[d]=E[m]=E[y]=E[g]=E[v]=!1;var I=Object.prototype,T=I.toString;t.exports=i},{"../internal/isLength":125,"../internal/isObjectLike":126}],148:[function(e,t,n){function i(e){return void 0===e}t.exports=i},{}],149:[function(e,t,n){var i=e("../internal/getNative"),r=e("../internal/isArrayLike"),a=e("../lang/isObject"),o=e("../internal/shimKeys"),s=e("../support"),l=i(Object,"keys"),u=l?function(e){var t=null==e?void 0:e.constructor;return"function"==typeof t&&t.prototype===e||("function"==typeof e?s.enumPrototypes:r(e))?o(e):a(e)?l(e):[]}:o;t.exports=u},{"../internal/getNative":114,"../internal/isArrayLike":119,"../internal/shimKeys":134,"../lang/isObject":144,"../support":153}],150:[function(e,t,n){function i(e){if(null==e)return[];c(e)||(e=Object(e));var t=e.length;t=t&&u(t)&&(o(e)||a(e)||p(e))&&t||0;for(var n=e.constructor,i=-1,r=s(n)&&n.prototype||O,f=r===e,d=Array(t),m=t>0,g=h.enumErrorProps&&(e===j||e instanceof Error),v=h.enumPrototypes&&s(e);++i<t;)d[i]=i+"";for(var w in e)v&&"prototype"==w||g&&("message"==w||"name"==w)||m&&l(w,t)||"constructor"==w&&(f||!S.call(e,w))||d.push(w);if(h.nonEnumShadows&&e!==O){var E=e===_?x:e===j?y:k.call(e),I=C[E]||C[b];for(E==b&&(r=O),t=A.length;t--;){w=A[t];var T=I[w];f&&T||(T?!S.call(e,w):e[w]===r[w])||d.push(w)}}return d}var r=e("../internal/arrayEach"),a=e("../lang/isArguments"),o=e("../lang/isArray"),s=e("../lang/isFunction"),l=e("../internal/isIndex"),u=e("../internal/isLength"),c=e("../lang/isObject"),p=e("../lang/isString"),h=e("../support"),f="[object Array]",d="[object Boolean]",m="[object Date]",y="[object Error]",g="[object Function]",v="[object Number]",b="[object Object]",w="[object RegExp]",x="[object String]",A=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],j=Error.prototype,O=Object.prototype,_=String.prototype,S=O.hasOwnProperty,k=O.toString,C={};C[f]=C[m]=C[v]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},C[d]=C[x]={constructor:!0,toString:!0,valueOf:!0},C[y]=C[g]=C[w]={constructor:!0,toString:!0},C[b]={constructor:!0},r(A,function(e){for(var t in C)if(S.call(C,t)){var n=C[t];n[e]=S.call(n,e)}}),t.exports=i},{"../internal/arrayEach":63,"../internal/isIndex":121,"../internal/isLength":125,"../lang/isArguments":139,"../lang/isArray":140,"../lang/isFunction":142,"../lang/isObject":144,"../lang/isString":146,"../support":153}],151:[function(e,t,n){function i(e){e=a(e);for(var t=-1,n=r(e),i=n.length,o=Array(i);++t<i;){var s=n[t];o[t]=[s,e[s]]}return o}var r=e("./keys"),a=e("../internal/toObject");t.exports=i},{"../internal/toObject":135,"./keys":149}],152:[function(e,t,n){function i(e){return r(e,a(e))}var r=e("../internal/baseValues"),a=e("./keys");t.exports=i},{"../internal/baseValues":91,"./keys":149}],153:[function(e,t,n){var i=Array.prototype,r=Error.prototype,a=Object.prototype,o=a.propertyIsEnumerable,s=i.splice,l={};!function(e){var t=function(){this.x=e},n={0:e,length:e},i=[];t.prototype={valueOf:e,y:e};for(var a in new t)i.push(a);l.enumErrorProps=o.call(r,"message")||o.call(r,"name"),l.enumPrototypes=o.call(t,"prototype"),l.nonEnumShadows=!/valueOf/.test(i),l.ownLast="x"!=i[0],l.spliceObjects=(s.call(n,0,1),!n[0]),l.unindexedChars="x"[0]+Object("x")[0]!="xx"}(1,0),t.exports=l},{}],154:[function(e,t,n){function i(e){return e}t.exports=i},{}],155:[function(e,t,n){function i(){}t.exports=i},{}],156:[function(e,t,n){function i(e){return o(e)?r(e):a(e)}var r=e("../internal/baseProperty"),a=e("../internal/basePropertyDeep"),o=e("../internal/isKey");t.exports=i},{"../internal/baseProperty":86,"../internal/basePropertyDeep":87,"../internal/isKey":123}],157:[function(e,n,i){(function(e){!function(e){"use strict";if("function"==typeof bootstrap)bootstrap("promise",e);else if("object"==typeof i&&"object"==typeof n)n.exports=e();else if("function"==typeof t&&t.amd)t(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeQ=e}else{if("undefined"==typeof window&&"undefined"==typeof self)throw new Error("This environment was not anticipated by Q. Please file a bug.");var r="undefined"!=typeof window?window:self,a=r.Q;r.Q=e(),r.Q.noConflict=function(){return r.Q=a,this}}}(function(){"use strict";function t(e){return function(){return K.apply(e,arguments)}}function n(e){return e===Object(e)}function i(e){return"[object StopIteration]"===ie(e)||e instanceof H}function r(e,t){if(q&&t.stack&&"object"==typeof e&&null!==e&&e.stack&&-1===e.stack.indexOf(re)){for(var n=[],i=t;i;i=i.source)i.stack&&n.unshift(i.stack);n.unshift(e.stack);var r=n.join("\n"+re+"\n");e.stack=a(r)}}function a(e){for(var t=e.split("\n"),n=[],i=0;i<t.length;++i){var r=t[i];l(r)||o(r)||!r||n.push(r)}return n.join("\n")}function o(e){return-1!==e.indexOf("(module.js:")||-1!==e.indexOf("(node.js:")}function s(e){var t=/at .+ \((.+):(\d+):(?:\d+)\)$/.exec(e);if(t)return[t[1],Number(t[2])];var n=/at ([^ ]+):(\d+):(?:\d+)$/.exec(e);if(n)return[n[1],Number(n[2])];var i=/.*@(.+):(\d+)$/.exec(e);return i?[i[1],Number(i[2])]:void 0}function l(e){var t=s(e);if(!t)return!1;var n=t[0],i=t[1];return n===z&&i>=J&&ue>=i}function u(){if(q)try{throw new Error}catch(e){var t=e.stack.split("\n"),n=t[0].indexOf("@")>0?t[1]:t[2],i=s(n);if(!i)return;return z=i[0],i[1]}}function c(e,t,n){return function(){return"undefined"!=typeof console&&"function"==typeof console.warn&&console.warn(t+" is deprecated, use "+n+" instead.",new Error("").stack),e.apply(e,arguments)}}function p(e){return e instanceof m?e:b(e)?C(e):k(e)}function h(){function e(e){t=e,a.source=e,X(n,function(t,n){p.nextTick(function(){e.promiseDispatch.apply(e,n)})},void 0),n=void 0,i=void 0}var t,n=[],i=[],r=ee(h.prototype),a=ee(m.prototype);if(a.promiseDispatch=function(e,r,a){var o=W(arguments);n?(n.push(o),"when"===r&&a[1]&&i.push(a[1])):p.nextTick(function(){t.promiseDispatch.apply(t,o)})},a.valueOf=function(){if(n)return a;var e=g(t);return v(e)&&(t=e),e},a.inspect=function(){return t?t.inspect():{state:"pending"}},p.longStackSupport&&q)try{throw new Error}catch(o){a.stack=o.stack.substring(o.stack.indexOf("\n")+1)}return r.promise=a,r.resolve=function(n){t||e(p(n))},r.fulfill=function(n){t||e(k(n))},r.reject=function(n){t||e(S(n))},r.notify=function(e){t||X(i,function(t,n){p.nextTick(function(){n(e)})},void 0)},r}function f(e){if("function"!=typeof e)throw new TypeError("resolver must be a function.");var t=h();try{e(t.resolve,t.reject,t.notify)}catch(n){t.reject(n)}return t.promise}function d(e){return f(function(t,n){for(var i=0,r=e.length;r>i;i++)p(e[i]).then(t,n)})}function m(e,t,n){void 0===t&&(t=function(e){return S(new Error("Promise does not support operation: "+e))}),void 0===n&&(n=function(){return{state:"unknown"}});var i=ee(m.prototype);if(i.promiseDispatch=function(n,r,a){var o;try{o=e[r]?e[r].apply(i,a):t.call(i,r,a)}catch(s){o=S(s)}n&&n(o)},i.inspect=n,n){var r=n();"rejected"===r.state&&(i.exception=r.reason),i.valueOf=function(){var e=n();return"pending"===e.state||"rejected"===e.state?i:e.value}}return i}function y(e,t,n,i){return p(e).then(t,n,i)}function g(e){if(v(e)){var t=e.inspect();if("fulfilled"===t.state)return t.value}return e}function v(e){return e instanceof m}function b(e){return n(e)&&"function"==typeof e.then}function w(e){return v(e)&&"pending"===e.inspect().state}function x(e){return!v(e)||"fulfilled"===e.inspect().state}function A(e){return v(e)&&"rejected"===e.inspect().state}function j(){ae.length=0,oe.length=0,le||(le=!0)}function O(t,n){le&&("object"==typeof e&&"function"==typeof e.emit&&p.nextTick.runAfter(function(){-1!==G(oe,t)&&(e.emit("unhandledRejection",n,t),se.push(t))}),oe.push(t),n&&"undefined"!=typeof n.stack?ae.push(n.stack):ae.push("(no stack) "+n))}function _(t){if(le){var n=G(oe,t);-1!==n&&("object"==typeof e&&"function"==typeof e.emit&&p.nextTick.runAfter(function(){var i=G(se,t);-1!==i&&(e.emit("rejectionHandled",ae[n],t),se.splice(i,1))}),oe.splice(n,1),ae.splice(n,1))}}function S(e){var t=m({when:function(t){return t&&_(this),t?t(e):this}},function(){return this},function(){return{state:"rejected",reason:e}});return O(t,e),t}function k(e){return m({when:function(){return e},get:function(t){return e[t]},set:function(t,n){e[t]=n},"delete":function(t){delete e[t]},post:function(t,n){return null===t||void 0===t?e.apply(void 0,n):e[t].apply(e,n)},apply:function(t,n){return e.apply(t,n)},keys:function(){return ne(e)}},void 0,function(){return{state:"fulfilled",value:e}})}function C(e){var t=h();return p.nextTick(function(){try{e.then(t.resolve,t.reject,t.notify)}catch(n){t.reject(n)}}),t.promise}function E(e){return m({isDef:function(){}},function(t,n){return P(e,t,n)},function(){return p(e).inspect()})}function I(e,t,n){return p(e).spread(t,n)}function T(e){return function(){function t(e,t){var o;if("undefined"==typeof StopIteration){try{o=n[e](t)}catch(s){return S(s)}return o.done?p(o.value):y(o.value,r,a)}try{o=n[e](t)}catch(s){return i(s)?p(s.value):S(s)}return y(o,r,a)}var n=e.apply(this,arguments),r=t.bind(t,"next"),a=t.bind(t,"throw");return r()}}function $(e){p.done(p.async(e)())}function M(e){throw new H(e)}function U(e){return function(){return I([this,L(arguments)],function(t,n){return e.apply(t,n)})}}function P(e,t,n){return p(e).dispatch(t,n)}function L(e){return y(e,function(e){var t=0,n=h();return X(e,function(i,r,a){var o;v(r)&&"fulfilled"===(o=r.inspect()).state?e[a]=o.value:(++t,y(r,function(i){e[a]=i,0===--t&&n.resolve(e)},n.reject,function(e){n.notify({index:a,value:e})}))},void 0),0===t&&n.resolve(e),n.promise})}function D(e){if(0===e.length)return p.resolve();var t=p.defer(),n=0;return X(e,function(i,r,a){function o(e){t.resolve(e)}function s(){n--,0===n&&t.reject(new Error("Can't get fulfillment value from any promise, all promises were rejected."))}function l(e){t.notify({index:a,value:e})}var u=e[a];n++,y(u,o,s,l)},void 0),t.promise}function R(e){return y(e,function(e){return e=Z(e,p),y(L(Z(e,function(e){return y(e,Q,Q)})),function(){return e})})}function N(e){return p(e).allSettled()}function F(e,t){return p(e).then(void 0,void 0,t)}function B(e,t){return p(e).nodeify(t)}var q=!1;try{throw new Error}catch(V){q=!!V.stack}var z,H,J=u(),Q=function(){},Y=function(){function t(){for(var e,t;i.next;)i=i.next,e=i.task,i.task=void 0,t=i.domain,t&&(i.domain=void 0,t.enter()),n(e,t);for(;l.length;)e=l.pop(),n(e);a=!1}function n(e,n){try{e()}catch(i){if(s)throw n&&n.exit(),setTimeout(t,0),n&&n.enter(),i;setTimeout(function(){throw i},0)}n&&n.exit()}var i={task:void 0,next:null},r=i,a=!1,o=void 0,s=!1,l=[];if(Y=function(t){r=r.next={task:t,domain:s&&e.domain,next:null},a||(a=!0,o())},"object"==typeof e&&"[object process]"===e.toString()&&e.nextTick)s=!0,o=function(){e.nextTick(t)};else if("function"==typeof setImmediate)o="undefined"!=typeof window?setImmediate.bind(window,t):function(){setImmediate(t)};else if("undefined"!=typeof MessageChannel){var u=new MessageChannel;u.port1.onmessage=function(){o=c,u.port1.onmessage=t,t()};var c=function(){u.port2.postMessage(0)};o=function(){setTimeout(t,0),c()}}else o=function(){setTimeout(t,0)};return Y.runAfter=function(e){l.push(e),a||(a=!0,o())},Y}(),K=Function.call,W=t(Array.prototype.slice),X=t(Array.prototype.reduce||function(e,t){var n=0,i=this.length;if(1===arguments.length)for(;;){if(n in this){t=this[n++];break}if(++n>=i)throw new TypeError}for(;i>n;n++)n in this&&(t=e(t,this[n],n));return t}),G=t(Array.prototype.indexOf||function(e){for(var t=0;t<this.length;t++)if(this[t]===e)return t;return-1}),Z=t(Array.prototype.map||function(e,t){var n=this,i=[];return X(n,function(r,a,o){i.push(e.call(t,a,o,n))},void 0),i}),ee=Object.create||function(e){function t(){}return t.prototype=e,new t},te=t(Object.prototype.hasOwnProperty),ne=Object.keys||function(e){var t=[];for(var n in e)te(e,n)&&t.push(n);return t},ie=t(Object.prototype.toString);H="undefined"!=typeof ReturnValue?ReturnValue:function(e){this.value=e};var re="From previous event:";p.resolve=p,p.nextTick=Y,p.longStackSupport=!1,"object"==typeof e&&e&&e.env&&e.env.Q_DEBUG&&(p.longStackSupport=!0),p.defer=h,h.prototype.makeNodeResolver=function(){var e=this;return function(t,n){t?e.reject(t):arguments.length>2?e.resolve(W(arguments,1)):e.resolve(n)}},p.Promise=f,p.promise=f,f.race=d,f.all=L,f.reject=S,f.resolve=p,p.passByCopy=function(e){return e},m.prototype.passByCopy=function(){return this},p.join=function(e,t){return p(e).join(t)},m.prototype.join=function(e){return p([this,e]).spread(function(e,t){if(e===t)return e;throw new Error("Can't join: not the same: "+e+" "+t)})},p.race=d,m.prototype.race=function(){return this.then(p.race)},p.makePromise=m,m.prototype.toString=function(){return"[object Promise]"},m.prototype.then=function(e,t,n){function i(t){try{return"function"==typeof e?e(t):t}catch(n){return S(n)}}function a(e){if("function"==typeof t){r(e,s);try{return t(e)}catch(n){return S(n)}}return S(e)}function o(e){return"function"==typeof n?n(e):e}var s=this,l=h(),u=!1;return p.nextTick(function(){s.promiseDispatch(function(e){u||(u=!0,l.resolve(i(e)))},"when",[function(e){u||(u=!0,l.resolve(a(e)))}])}),s.promiseDispatch(void 0,"when",[void 0,function(e){var t,n=!1;try{t=o(e)}catch(i){if(n=!0,!p.onerror)throw i;p.onerror(i)}n||l.notify(t)}]),l.promise},p.tap=function(e,t){return p(e).tap(t)},m.prototype.tap=function(e){return e=p(e),this.then(function(t){return e.fcall(t).thenResolve(t)})},p.when=y,m.prototype.thenResolve=function(e){return this.then(function(){return e})},p.thenResolve=function(e,t){return p(e).thenResolve(t)},m.prototype.thenReject=function(e){return this.then(function(){throw e})},p.thenReject=function(e,t){return p(e).thenReject(t)},p.nearer=g,p.isPromise=v,p.isPromiseAlike=b,p.isPending=w,m.prototype.isPending=function(){return"pending"===this.inspect().state},p.isFulfilled=x,m.prototype.isFulfilled=function(){return"fulfilled"===this.inspect().state},p.isRejected=A,m.prototype.isRejected=function(){return"rejected"===this.inspect().state};var ae=[],oe=[],se=[],le=!0;p.resetUnhandledRejections=j,p.getUnhandledReasons=function(){return ae.slice()},p.stopUnhandledRejectionTracking=function(){j(),le=!1},j(),p.reject=S,p.fulfill=k,p.master=E,p.spread=I,m.prototype.spread=function(e,t){return this.all().then(function(t){return e.apply(void 0,t)},t)},p.async=T,p.spawn=$,p["return"]=M,p.promised=U,p.dispatch=P,m.prototype.dispatch=function(e,t){var n=this,i=h();return p.nextTick(function(){n.promiseDispatch(i.resolve,e,t)}),i.promise},p.get=function(e,t){return p(e).dispatch("get",[t])},m.prototype.get=function(e){return this.dispatch("get",[e])},p.set=function(e,t,n){return p(e).dispatch("set",[t,n])},m.prototype.set=function(e,t){return this.dispatch("set",[e,t])},p.del=p["delete"]=function(e,t){return p(e).dispatch("delete",[t])},m.prototype.del=m.prototype["delete"]=function(e){return this.dispatch("delete",[e])},p.mapply=p.post=function(e,t,n){return p(e).dispatch("post",[t,n])},m.prototype.mapply=m.prototype.post=function(e,t){return this.dispatch("post",[e,t])},p.send=p.mcall=p.invoke=function(e,t){return p(e).dispatch("post",[t,W(arguments,2)])},m.prototype.send=m.prototype.mcall=m.prototype.invoke=function(e){return this.dispatch("post",[e,W(arguments,1)])},p.fapply=function(e,t){return p(e).dispatch("apply",[void 0,t])},m.prototype.fapply=function(e){return this.dispatch("apply",[void 0,e])},p["try"]=p.fcall=function(e){return p(e).dispatch("apply",[void 0,W(arguments,1)])},m.prototype.fcall=function(){return this.dispatch("apply",[void 0,W(arguments)])},p.fbind=function(e){var t=p(e),n=W(arguments,1);return function(){return t.dispatch("apply",[this,n.concat(W(arguments))])}},m.prototype.fbind=function(){var e=this,t=W(arguments);return function(){return e.dispatch("apply",[this,t.concat(W(arguments))])}},p.keys=function(e){return p(e).dispatch("keys",[])},m.prototype.keys=function(){return this.dispatch("keys",[])},p.all=L,m.prototype.all=function(){return L(this)},p.any=D,m.prototype.any=function(){return D(this)},p.allResolved=c(R,"allResolved","allSettled"),m.prototype.allResolved=function(){return R(this)},p.allSettled=N,m.prototype.allSettled=function(){return this.then(function(e){return L(Z(e,function(e){function t(){return e.inspect()}return e=p(e),e.then(t,t)}))})},p.fail=p["catch"]=function(e,t){return p(e).then(void 0,t)},m.prototype.fail=m.prototype["catch"]=function(e){
+return this.then(void 0,e)},p.progress=F,m.prototype.progress=function(e){return this.then(void 0,void 0,e)},p.fin=p["finally"]=function(e,t){return p(e)["finally"](t)},m.prototype.fin=m.prototype["finally"]=function(e){return e=p(e),this.then(function(t){return e.fcall().then(function(){return t})},function(t){return e.fcall().then(function(){throw t})})},p.done=function(e,t,n,i){return p(e).done(t,n,i)},m.prototype.done=function(t,n,i){var a=function(e){p.nextTick(function(){if(r(e,o),!p.onerror)throw e;p.onerror(e)})},o=t||n||i?this.then(t,n,i):this;"object"==typeof e&&e&&e.domain&&(a=e.domain.bind(a)),o.then(void 0,a)},p.timeout=function(e,t,n){return p(e).timeout(t,n)},m.prototype.timeout=function(e,t){var n=h(),i=setTimeout(function(){t&&"string"!=typeof t||(t=new Error(t||"Timed out after "+e+" ms"),t.code="ETIMEDOUT"),n.reject(t)},e);return this.then(function(e){clearTimeout(i),n.resolve(e)},function(e){clearTimeout(i),n.reject(e)},n.notify),n.promise},p.delay=function(e,t){return void 0===t&&(t=e,e=void 0),p(e).delay(t)},m.prototype.delay=function(e){return this.then(function(t){var n=h();return setTimeout(function(){n.resolve(t)},e),n.promise})},p.nfapply=function(e,t){return p(e).nfapply(t)},m.prototype.nfapply=function(e){var t=h(),n=W(e);return n.push(t.makeNodeResolver()),this.fapply(n).fail(t.reject),t.promise},p.nfcall=function(e){var t=W(arguments,1);return p(e).nfapply(t)},m.prototype.nfcall=function(){var e=W(arguments),t=h();return e.push(t.makeNodeResolver()),this.fapply(e).fail(t.reject),t.promise},p.nfbind=p.denodeify=function(e){var t=W(arguments,1);return function(){var n=t.concat(W(arguments)),i=h();return n.push(i.makeNodeResolver()),p(e).fapply(n).fail(i.reject),i.promise}},m.prototype.nfbind=m.prototype.denodeify=function(){var e=W(arguments);return e.unshift(this),p.denodeify.apply(void 0,e)},p.nbind=function(e,t){var n=W(arguments,2);return function(){function i(){return e.apply(t,arguments)}var r=n.concat(W(arguments)),a=h();return r.push(a.makeNodeResolver()),p(i).fapply(r).fail(a.reject),a.promise}},m.prototype.nbind=function(){var e=W(arguments,0);return e.unshift(this),p.nbind.apply(void 0,e)},p.nmapply=p.npost=function(e,t,n){return p(e).npost(t,n)},m.prototype.nmapply=m.prototype.npost=function(e,t){var n=W(t||[]),i=h();return n.push(i.makeNodeResolver()),this.dispatch("post",[e,n]).fail(i.reject),i.promise},p.nsend=p.nmcall=p.ninvoke=function(e,t){var n=W(arguments,2),i=h();return n.push(i.makeNodeResolver()),p(e).dispatch("post",[t,n]).fail(i.reject),i.promise},m.prototype.nsend=m.prototype.nmcall=m.prototype.ninvoke=function(e){var t=W(arguments,1),n=h();return t.push(n.makeNodeResolver()),this.dispatch("post",[e,t]).fail(n.reject),n.promise},p.nodeify=B,m.prototype.nodeify=function(e){return e?void this.then(function(t){p.nextTick(function(){e(null,t)})},function(t){p.nextTick(function(){e(t)})}):this},p.noConflict=function(){throw new Error("Q.noConflict only works when Q is used as a global")};var ue=u();return p})}).call(this,e("_process"))},{_process:12}],158:[function(e,t,n){function i(){}function r(e){var t={}.toString.call(e);switch(t){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function a(e){if(!b(e))return e;var t=[];for(var n in e)null!=e[n]&&o(t,n,e[n]);return t.join("&")}function o(e,t,n){return Array.isArray(n)?n.forEach(function(n){o(e,t,n)}):void e.push(encodeURIComponent(t)+"="+encodeURIComponent(n))}function s(e){for(var t,n,i={},r=e.split("&"),a=0,o=r.length;o>a;++a)n=r[a],t=n.split("="),i[decodeURIComponent(t[0])]=decodeURIComponent(t[1]);return i}function l(e){var t,n,i,r,a=e.split(/\r?\n/),o={};a.pop();for(var s=0,l=a.length;l>s;++s)n=a[s],t=n.indexOf(":"),i=n.slice(0,t).toLowerCase(),r=x(n.slice(t+1)),o[i]=r;return o}function u(e){return/[\/+]json\b/.test(e)}function c(e){return e.split(/ *; */).shift()}function p(e){return g(e.split(/ *; */),function(e,t){var n=t.split(/ *= */),i=n.shift(),r=n.shift();return i&&r&&(e[i]=r),e},{})}function h(e,t){t=t||{},this.req=e,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method&&(""===this.xhr.responseType||"text"===this.xhr.responseType)||"undefined"==typeof this.xhr.responseType?this.xhr.responseText:null,this.statusText=this.req.xhr.statusText,this.setStatusProperties(this.xhr.status),this.header=this.headers=l(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text?this.text:this.xhr.response):null}function f(e,t){var n=this;this._query=this._query||[],this.method=e,this.url=t,this.header={},this._header={},this.on("end",function(){var e=null,t=null;try{t=new h(n)}catch(i){return e=new Error("Parser is unable to parse the response"),e.parse=!0,e.original=i,e.rawResponse=n.xhr&&n.xhr.responseText?n.xhr.responseText:null,e.statusCode=n.xhr&&n.xhr.status?n.xhr.status:null,n.callback(e)}if(n.emit("response",t),e)return n.callback(e,t);if(t.status>=200&&t.status<300)return n.callback(e,t);var r=new Error(t.statusText||"Unsuccessful HTTP response");r.original=e,r.response=t,r.status=t.status,n.callback(r,t)})}function d(e,t){var n=w("DELETE",e);return t&&n.end(t),n}var m,y=e("emitter"),g=e("reduce"),v=e("./request-base"),b=e("./is-object");m="undefined"!=typeof window?window:"undefined"!=typeof self?self:this;var w=t.exports=e("./request").bind(null,f);w.getXHR=function(){if(!(!m.XMLHttpRequest||m.location&&"file:"==m.location.protocol&&m.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}return!1};var x="".trim?function(e){return e.trim()}:function(e){return e.replace(/(^\s*|\s*$)/g,"")};w.serializeObject=a,w.parseString=s,w.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},w.serialize={"application/x-www-form-urlencoded":a,"application/json":JSON.stringify},w.parse={"application/x-www-form-urlencoded":s,"application/json":JSON.parse},h.prototype.get=function(e){return this.header[e.toLowerCase()]},h.prototype.setHeaderProperties=function(e){var t=this.header["content-type"]||"";this.type=c(t);var n=p(t);for(var i in n)this[i]=n[i]},h.prototype.parseBody=function(e){var t=w.parse[this.type];return!t&&u(this.type)&&(t=w.parse["application/json"]),t&&e&&(e.length||e instanceof Object)?t(e):null},h.prototype.setStatusProperties=function(e){1223===e&&(e=204);var t=e/100|0;this.status=this.statusCode=e,this.statusType=t,this.info=1==t,this.ok=2==t,this.clientError=4==t,this.serverError=5==t,this.error=4==t||5==t?this.toError():!1,this.accepted=202==e,this.noContent=204==e,this.badRequest=400==e,this.unauthorized=401==e,this.notAcceptable=406==e,this.notFound=404==e,this.forbidden=403==e},h.prototype.toError=function(){var e=this.req,t=e.method,n=e.url,i="cannot "+t+" "+n+" ("+this.status+")",r=new Error(i);return r.status=this.status,r.method=t,r.url=n,r},w.Response=h,y(f.prototype);for(var A in v)f.prototype[A]=v[A];f.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},f.prototype.type=function(e){return this.set("Content-Type",w.types[e]||e),this},f.prototype.responseType=function(e){return this._responseType=e,this},f.prototype.accept=function(e){return this.set("Accept",w.types[e]||e),this},f.prototype.auth=function(e,t,n){switch(n||(n={type:"basic"}),n.type){case"basic":var i=btoa(e+":"+t);this.set("Authorization","Basic "+i);break;case"auto":this.username=e,this.password=t}return this},f.prototype.query=function(e){return"string"!=typeof e&&(e=a(e)),e&&this._query.push(e),this},f.prototype.attach=function(e,t,n){return this._getFormData().append(e,t,n||t.name),this},f.prototype._getFormData=function(){return this._formData||(this._formData=new m.FormData),this._formData},f.prototype.send=function(e){var t=b(e),n=this._header["content-type"];if(t&&b(this._data))for(var i in e)this._data[i]=e[i];else"string"==typeof e?(n||this.type("form"),n=this._header["content-type"],"application/x-www-form-urlencoded"==n?this._data=this._data?this._data+"&"+e:e:this._data=(this._data||"")+e):this._data=e;return!t||r(e)?this:(n||this.type("json"),this)},h.prototype.parse=function(e){return m.console&&console.warn("Client-side parse() method has been renamed to serialize(). This method is not compatible with superagent v2.0"),this.serialize(e),this},h.prototype.serialize=function(e){return this._parser=e,this},f.prototype.callback=function(e,t){var n=this._callback;this.clearTimeout(),n(e,t)},f.prototype.crossDomainError=function(){var e=new Error("Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.");e.crossDomain=!0,e.status=this.status,e.method=this.method,e.url=this.url,this.callback(e)},f.prototype.timeoutError=function(){var e=this._timeout,t=new Error("timeout of "+e+"ms exceeded");t.timeout=e,this.callback(t)},f.prototype.withCredentials=function(){return this._withCredentials=!0,this},f.prototype.end=function(e){var t=this,n=this.xhr=w.getXHR(),a=this._query.join("&"),o=this._timeout,s=this._formData||this._data;this._callback=e||i,n.onreadystatechange=function(){if(4==n.readyState){var e;try{e=n.status}catch(i){e=0}if(0==e){if(t.timedout)return t.timeoutError();if(t.aborted)return;return t.crossDomainError()}t.emit("end")}};var l=function(e){e.total>0&&(e.percent=e.loaded/e.total*100),e.direction="download",t.emit("progress",e)};this.hasListeners("progress")&&(n.onprogress=l);try{n.upload&&this.hasListeners("progress")&&(n.upload.onprogress=l)}catch(c){}if(o&&!this._timer&&(this._timer=setTimeout(function(){t.timedout=!0,t.abort()},o)),a&&(a=w.serializeObject(a),this.url+=~this.url.indexOf("?")?"&"+a:"?"+a),this.username&&this.password?n.open(this.method,this.url,!0,this.username,this.password):n.open(this.method,this.url,!0),this._withCredentials&&(n.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof s&&!r(s)){var p=this._header["content-type"],h=this._parser||w.serialize[p?p.split(";")[0]:""];!h&&u(p)&&(h=w.serialize["application/json"]),h&&(s=h(s))}for(var f in this.header)null!=this.header[f]&&n.setRequestHeader(f,this.header[f]);return this._responseType&&(n.responseType=this._responseType),this.emit("request",this),n.send("undefined"!=typeof s?s:null),this},w.Request=f,w.get=function(e,t,n){var i=w("GET",e);return"function"==typeof t&&(n=t,t=null),t&&i.query(t),n&&i.end(n),i},w.head=function(e,t,n){var i=w("HEAD",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.del=d,w["delete"]=d,w.patch=function(e,t,n){var i=w("PATCH",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.post=function(e,t,n){var i=w("POST",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.put=function(e,t,n){var i=w("PUT",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i}},{"./is-object":159,"./request":161,"./request-base":160,emitter:162,reduce:163}],159:[function(e,t,n){function i(e){return null!=e&&"object"==typeof e}t.exports=i},{}],160:[function(e,t,n){var i=e("./is-object");n.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},n.parse=function(e){return this._parser=e,this},n.timeout=function(e){return this._timeout=e,this},n.then=function(e,t){return this.end(function(n,i){n?t(n):e(i)})},n.use=function(e){return e(this),this},n.get=function(e){return this._header[e.toLowerCase()]},n.getHeader=n.get,n.set=function(e,t){if(i(e)){for(var n in e)this.set(n,e[n]);return this}return this._header[e.toLowerCase()]=t,this.header[e]=t,this},n.unset=function(e){return delete this._header[e.toLowerCase()],delete this.header[e],this},n.field=function(e,t){return this._getFormData().append(e,t),this}},{"./is-object":159}],161:[function(e,t,n){function i(e,t,n){return"function"==typeof n?new e("GET",t).end(n):2==arguments.length?new e("GET",t):new e(t,n)}t.exports=i},{}],162:[function(e,t,n){function i(e){return e?r(e):void 0}function r(e){for(var t in i.prototype)e[t]=i.prototype[t];return e}"undefined"!=typeof t&&(t.exports=i),i.prototype.on=i.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},i.prototype.once=function(e,t){function n(){this.off(e,n),t.apply(this,arguments)}return n.fn=t,this.on(e,n),this},i.prototype.off=i.prototype.removeListener=i.prototype.removeAllListeners=i.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+e];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var i,r=0;r<n.length;r++)if(i=n[r],i===t||i.fn===t){n.splice(r,1);break}return this},i.prototype.emit=function(e){this._callbacks=this._callbacks||{};var t=[].slice.call(arguments,1),n=this._callbacks["$"+e];if(n){n=n.slice(0);for(var i=0,r=n.length;r>i;++i)n[i].apply(this,t)}return this},i.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},i.prototype.hasListeners=function(e){return!!this.listeners(e).length}},{}],163:[function(e,t,n){t.exports=function(e,t,n){for(var i=0,r=e.length,a=3==arguments.length?n:e[i++];r>i;)a=t.call(null,a,e[i],++i,e);return a}},{}]},{},[1])(1)}),window.SwaggerUi=Backbone.Router.extend({dom_id:"swagger_ui",options:null,api:null,headerView:null,mainView:null,initialize:function(e){e=e||{},"model"!==e.defaultModelRendering&&(e.defaultModelRendering="schema"),e.highlightSizeThreshold||(e.highlightSizeThreshold=1e5),e.dom_id&&(this.dom_id=e.dom_id,delete e.dom_id),e.supportedSubmitMethods||(e.supportedSubmitMethods=["get","put","post","delete","head","options","patch"]),"string"==typeof e.oauth2RedirectUrl&&(window.oAuthRedirectUrl=e.redirectUrl),$("#"+this.dom_id).length||$("body").append('<div id="'+this.dom_id+'"></div>'),this.options=e,marked.setOptions({gfm:!0});var t=this;this.options.success=function(){return t.render()},this.options.progress=function(e){return t.showMessage(e)},this.options.failure=function(e){return t.onLoadFailure(e)},this.headerView=new SwaggerUi.Views.HeaderView({el:$("#header")}),this.headerView.on("update-swagger-ui",function(e){return t.updateSwaggerUi(e)}),JSONEditor.defaults.iconlibs.swagger=JSONEditor.AbstractIconLib.extend({mapping:{collapse:"collapse",expand:"expand"},icon_prefix:"swagger-"})},setOption:function(e,t){this.options[e]=t},getOption:function(e){return this.options[e]},updateSwaggerUi:function(e){this.options.url=e.url,this.load()},load:function(){this.mainView&&this.mainView.clear(),this.authView&&this.authView.remove();var e=this.options.url;e&&0!==e.indexOf("http")&&(e=this.buildUrl(window.location.href.toString(),e)),this.api&&(this.options.authorizations=this.api.clientAuthorizations.authz),this.options.url=e,this.headerView.update(e),this.api=new SwaggerClient(this.options)},collapseAll:function(){Docs.collapseEndpointListForResource("")},listAll:function(){Docs.collapseOperationsForResource("")},expandAll:function(){Docs.expandOperationsForResource("")},render:function(){var e;switch(this.showMessage("Finished Loading Resource Information. Rendering Swagger UI..."),this.mainView=new SwaggerUi.Views.MainView({model:this.api,el:$("#"+this.dom_id),swaggerOptions:this.options,router:this}).render(),_.isEmpty(this.api.securityDefinitions)||(e=_.map(this.api.securityDefinitions,function(e,t){var n={};return n[t]=e,n}),this.authView=new SwaggerUi.Views.AuthButtonView({data:SwaggerUi.utils.parseSecurityDefinitions(e),router:this}),$("#auth_container").append(this.authView.render().el)),this.showMessage(),this.options.docExpansion){case"full":this.expandAll();break;case"list":this.listAll()}this.renderGFM(),this.options.onComplete&&this.options.onComplete(this.api,this),setTimeout(Docs.shebang.bind(this),100)},buildUrl:function(e,t){if(0===t.indexOf("/")){var n=e.split("/");return e=n[0]+"//"+n[2],e+t}var i=e.length;return e.indexOf("?")>-1&&(i=Math.min(i,e.indexOf("?"))),e.indexOf("#")>-1&&(i=Math.min(i,e.indexOf("#"))),e=e.substring(0,i),-1!==e.indexOf("/",e.length-1)?e+t:e+"/"+t},showMessage:function(e){void 0===e&&(e="");var t=$("#message-bar");t.removeClass("message-fail"),t.addClass("message-success"),t.text(e),window.SwaggerTranslator&&window.SwaggerTranslator.translate(t)},onLoadFailure:function(e){void 0===e&&(e=""),$("#message-bar").removeClass("message-success"),$("#message-bar").addClass("message-fail");var t=$("#message-bar").text(e);return this.options.onFailure&&this.options.onFailure(e),t},renderGFM:function(){$(".markdown").each(function(){$(this).html(marked($(this).html()))}),$(".propDesc",".model-signature .description").each(function(){$(this).html(marked($(this).html())).addClass("markdown")})}}),window.SwaggerUi.Views={},window.SwaggerUi.Models={},window.SwaggerUi.Collections={},window.SwaggerUi.partials={},window.SwaggerUi.utils={},function(){function e(e){"console"in window&&"function"==typeof window.console.warn&&console.warn(e)}window.authorizations={add:function(){if(e("Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add()."),"undefined"==typeof window.swaggerUi)throw new TypeError("window.swaggerUi is not defined");window.swaggerUi instanceof SwaggerUi&&window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations,arguments)}},window.ApiKeyAuthorization=function(){e("window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization."),SwaggerClient.ApiKeyAuthorization.apply(window,arguments)},window.PasswordAuthorization=function(){e("window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization."),SwaggerClient.PasswordAuthorization.apply(window,arguments)}}(),function(e,t){"function"==typeof define&&define.amd?define(["b"],function(n){return e.SwaggerUi=t(n)}):"object"==typeof exports?module.exports=t(require("b")):e.SwaggerUi=t(e.b)}(this,function(){return SwaggerUi}),window.SwaggerUi.utils={parseSecurityDefinitions:function(e){var t=Object.assign({},window.swaggerUi.api.authSchemes||window.swaggerUi.api.securityDefinitions),n=[],i=[],r=[],a=window.SwaggerUi.utils;return Array.isArray(e)?(e.forEach(function(e){var o={},s={};for(var l in e)if(Array.isArray(e[l])){if(!t[l])continue;if(t[l]=t[l]||{},"oauth2"===t[l].type){s[l]=Object.assign({},t[l]),s[l].scopes=Object.assign({},t[l].scopes);for(var u in s[l].scopes)e[l].indexOf(u)<0&&delete s[l].scopes[u];s[l].scopes=a.parseOauth2Scopes(s[l].scopes),r=_.merge(r,s[l].scopes)}else o[l]=Object.assign({},t[l])}else"oauth2"===e[l].type?(s[l]=Object.assign({},e[l]),s[l].scopes=a.parseOauth2Scopes(s[l].scopes),r=_.merge(r,s[l].scopes)):o[l]=e[l];_.isEmpty(o)||i.push(o),_.isEmpty(s)||n.push(s)}),{auths:i,oauth2:n,scopes:r}):null},parseOauth2Scopes:function(e){var t,n=Object.assign({},e),i=[];for(t in n)i.push({scope:t,description:n[t]});return i}},SwaggerUi.Models.ApiKeyAuthModel=Backbone.Model.extend({defaults:{"in":"",name:"",title:"",value:""},initialize:function(){this.on("change",this.validate)},validate:function(){var e=!!this.get("value");return this.set("valid",e),e}}),SwaggerUi.Views.ApiKeyAuthView=Backbone.View.extend({events:{"change .input_apiKey_entry":"apiKeyChange"},selectors:{apikeyInput:".input_apiKey_entry"},template:Handlebars.templates.apikey_auth,initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){return this.$el.html(this.template(this.model.toJSON())),this},apiKeyChange:function(e){var t=$(e.target).val();t&&this.$(this.selectors.apikeyInput).removeClass("error"),this.model.set("value",t)},isValid:function(){return this.model.validate()},highlightInvalid:function(){this.isValid()||this.$(this.selectors.apikeyInput).addClass("error")}}),SwaggerUi.Views.AuthButtonView=Backbone.View.extend({events:{"click .authorize__btn":"authorizeBtnClick"},tpls:{popup:Handlebars.templates.popup,authBtn:Handlebars.templates.auth_button,authBtnOperation:Handlebars.templates.auth_button_operation},initialize:function(e){this.options=e||{},this.options.data=this.options.data||{},this.isOperation=this.options.isOperation,this.model=this.model||{},this.router=this.options.router,this.auths=this.options.data.oauth2.concat(this.options.data.auths)},render:function(){var e=this.isOperation?"authBtnOperation":"authBtn";return this.$authEl=this.renderAuths(this.auths),this.$el.html(this.tpls[e](this.model)),this},authorizeBtnClick:function(e){var t;e.preventDefault(),t={title:"Available authorizations",content:this.$authEl},this.render(),this.popup=new SwaggerUi.Views.PopupView({model:t}),this.popup.render()},renderAuths:function(e){var t=$("<div>"),n=!1;return e.forEach(function(e){var i=new SwaggerUi.Views.AuthView({data:e,router:this.router}),r=i.render().el;t.append(r),i.isLogout&&(n=!0)},this),this.model.isLogout=n,t}}),SwaggerUi.Collections.AuthsCollection=Backbone.Collection.extend({constructor:function(){var e=Array.prototype.slice.call(arguments);e[0]=this.parse(e[0]),Backbone.Collection.apply(this,e)},add:function(e){var t=Array.prototype.slice.call(arguments);Array.isArray(e)?t[0]=_.map(e,function(e){return this.handleOne(e)},this):t[0]=this.handleOne(e),Backbone.Collection.prototype.add.apply(this,t)},handleOne:function(e){var t=e;if(!(e instanceof Backbone.Model))switch(e.type){case"oauth2":t=new SwaggerUi.Models.Oauth2Model(e);break;case"basic":t=new SwaggerUi.Models.BasicAuthModel(e);break;case"apiKey":t=new SwaggerUi.Models.ApiKeyAuthModel(e);break;default:t=new Backbone.Model(e)}return t},isValid:function(){var e=!0;return this.models.forEach(function(t){t.validate()||(e=!1)}),e},isAuthorized:function(){return this.length===this.where({isLogout:!0}).length},isPartiallyAuthorized:function(){return this.where({isLogout:!0}).length>0},parse:function(e){var t=Object.assign({},window.swaggerUi.api.clientAuthorizations.authz);return _.map(e,function(e,n){var i=t[n]&&"basic"===e.type&&t[n].username&&t[n].password;return _.extend(e,{title:n}),(t[n]||i)&&_.extend(e,{isLogout:!0,value:i?void 0:t[n].value,username:i?t[n].username:void 0,password:i?t[n].password:void 0,valid:!0}),e})}}),SwaggerUi.Views.AuthsCollectionView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.options.data=this.options.data||{},this.router=this.options.router,this.collection=new SwaggerUi.Collections.AuthsCollection(e.data),this.$innerEl=$("<div>"),this.authViews=[]},render:function(){return this.collection.each(function(e){this.renderOneAuth(e)},this),this.$el.html(this.$innerEl.html()?this.$innerEl:""),this},renderOneAuth:function(e){var t,n,i,r=e.get("type");"apiKey"===r?i="ApiKeyAuthView":"basic"===r&&0===this.$innerEl.find(".basic_auth_container").length?i="BasicAuthView":"oauth2"===r&&(i="Oauth2View"),i&&(n=new SwaggerUi.Views[i]({model:e,router:this.router}),t=n.render().el,this.authViews.push(n)),this.$innerEl.append(t)},highlightInvalid:function(){this.authViews.forEach(function(e){e.highlightInvalid()},this)}}),SwaggerUi.Views.AuthView=Backbone.View.extend({events:{"click .auth_submit__button":"authorizeClick","click .auth_logout__button":"logoutClick"},tpls:{main:Handlebars.templates.auth_view},selectors:{innerEl:".auth_inner",authBtn:".auth_submit__button"},initialize:function(e){this.options=e||{},e.data=e.data||{},this.router=this.options.router,this.authsCollectionView=new SwaggerUi.Views.AuthsCollectionView({data:e.data}),this.$el.html(this.tpls.main({isLogout:this.authsCollectionView.collection.isAuthorized(),isAuthorized:this.authsCollectionView.collection.isPartiallyAuthorized()})),this.$innerEl=this.$(this.selectors.innerEl),this.isLogout=this.authsCollectionView.collection.isPartiallyAuthorized()},render:function(){return this.$innerEl.html(this.authsCollectionView.render().el),this},authorizeClick:function(e){e.preventDefault(),e.stopPropagation(),this.authsCollectionView.collection.isValid()?this.authorize():this.authsCollectionView.highlightInvalid()},authorize:function(){this.authsCollectionView.collection.forEach(function(e){var t,n,i=e.get("type");"apiKey"===i?(t=new SwaggerClient.ApiKeyAuthorization(e.get("name"),e.get("value"),e.get("in")),this.router.api.clientAuthorizations.add(e.get("title"),t)):"basic"===i?(n=new SwaggerClient.PasswordAuthorization(e.get("username"),e.get("password")),this.router.api.clientAuthorizations.add(e.get("title"),n)):"oauth2"===i&&this.handleOauth2Login(e)},this),this.router.load()},logoutClick:function(e){e.preventDefault(),this.authsCollectionView.collection.forEach(function(e){window.swaggerUi.api.clientAuthorizations.remove(e.get("title"))}),this.router.load()},handleOauth2Login:function(e){var t,n,i,r=window.location,a=location.pathname.substring(0,location.pathname.lastIndexOf("/")),o=r.protocol+"//"+r.host+a+"/o2c.html",s=window.oAuthRedirectUrl||o,l=null,u=_.map(e.get("scopes"),function(e){return e.scope});window.OAuthSchemeKey=e.get("title"),window.enabledScopes=u;var c=e.get("flow");if("oauth2"!==e.get("type")||!c||"implicit"!==c&&"accessCode"!==c){if("oauth2"===e.get("type")&&c&&"application"===c)return n=e.attributes,window.swaggerUi.tokenName=n.tokenName||"access_token",void this.clientCredentialsFlow(u,n.tokenUrl,window.OAuthSchemeKey);if(e.get("grantTypes")){var p=e.get("grantTypes");for(var h in p)p.hasOwnProperty(h)&&"implicit"===h?(n=p[h],i=n.loginEndpoint.url,l=n.loginEndpoint.url+"?response_type=token",window.swaggerUi.tokenName=n.tokenName):p.hasOwnProperty(h)&&"accessCode"===h&&(n=p[h],i=n.tokenRequestEndpoint.url,l=n.tokenRequestEndpoint.url+"?response_type=code",window.swaggerUi.tokenName=n.tokenName)}}else n=e.attributes,l=n.authorizationUrl+"?response_type="+("implicit"===c?"token":"code"),window.swaggerUi.tokenName=n.tokenName||"access_token",window.swaggerUi.tokenUrl="accessCode"===c?n.tokenUrl:null,t=window.OAuthSchemeKey;l+="&redirect_uri="+encodeURIComponent(s),l+="&realm="+encodeURIComponent(realm),l+="&client_id="+encodeURIComponent(clientId),l+="&scope="+encodeURIComponent(u.join(scopeSeparator)),l+="&state="+encodeURIComponent(t);for(var f in additionalQueryStringParams)l+="&"+f+"="+encodeURIComponent(additionalQueryStringParams[f]);window.open(l)},clientCredentialsFlow:function(e,t,n){var i={client_id:clientId,client_secret:clientSecret,scope:e.join(" "),grant_type:"client_credentials"};$.ajax({url:t,type:"POST",data:i,success:function(e){onOAuthComplete(e,n)},error:function(){onOAuthComplete("")}})}}),SwaggerUi.Models.BasicAuthModel=Backbone.Model.extend({defaults:{username:"",password:"",title:"basic"},initialize:function(){this.on("change",this.validate)},validate:function(){var e=!!this.get("password")&&!!this.get("username");return this.set("valid",e),e}}),SwaggerUi.Views.BasicAuthView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},events:{"change .auth_input":"inputChange"},selectors:{usernameInput:".basic_auth__username",passwordInput:".basic_auth__password"},cls:{error:"error"},template:Handlebars.templates.basic_auth,render:function(){return $(this.el).html(this.template(this.model.toJSON())),this},inputChange:function(e){var t=$(e.target),n=t.val(),i=t.prop("name");n&&t.removeClass(this.cls.error),this.model.set(i,n)},isValid:function(){return this.model.validate()},highlightInvalid:function(){this.model.get("username")||this.$(this.selectors.usernameInput).addClass(this.cls.error)}}),SwaggerUi.Views.ContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.contentTypeId="ct"+Math.random(),$(this.el).html(Handlebars.templates.content_type(this.model)),this}}),SwaggerUi.Views.HeaderView=Backbone.View.extend({events:{"click #show-pet-store-icon":"showPetStore","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"},initialize:function(){},showPetStore:function(){this.trigger("update-swagger-ui",{url:"http://petstore.swagger.io/v2/swagger.json"})},showCustomOnKeyup:function(e){13===e.keyCode&&this.showCustom()},showCustom:function(e){e&&e.preventDefault(),this.trigger("update-swagger-ui",{url:$("#input_baseUrl").val()})},update:function(e,t,n){void 0===n&&(n=!1),$("#input_baseUrl").val(e),n&&this.trigger("update-swagger-ui",{url:e})}}),SwaggerUi.Views.MainView=Backbone.View.extend({apisSorter:{alpha:function(e,t){return e.name.localeCompare(t.name)}},operationsSorters:{alpha:function(e,t){return e.path.localeCompare(t.path)},method:function(e,t){return e.method.localeCompare(t.method)}},initialize:function(e){var t,n,i,r;if(e=e||{},this.router=e.router,e.swaggerOptions.apisSorter&&(t=e.swaggerOptions.apisSorter,n=_.isFunction(t)?t:this.apisSorter[t],_.isFunction(n)&&this.model.apisArray.sort(n)),e.swaggerOptions.operationsSorter&&(t=e.swaggerOptions.operationsSorter,n=_.isFunction(t)?t:this.operationsSorters[t],_.isFunction(n)))for(i in this.model.apisArray)this.model.apisArray[i].operationsArray.sort(n);this.model.auths=[];for(i in this.model.securityDefinitions)r=this.model.securityDefinitions[i],this.model.auths.push({name:i,type:r.type,value:r});"validatorUrl"in e.swaggerOptions?this.model.validatorUrl=e.swaggerOptions.validatorUrl:this.model.url.indexOf("localhost")>0||this.model.url.indexOf("127.0.0.1")>0?this.model.validatorUrl=null:"https:"===window.location.protocol?this.model.validatorUrl="https://online.swagger.io/validator":this.model.validatorUrl="http://online.swagger.io/validator";var a;for(a in this.model.definitions)this.model.definitions[a].type||(this.model.definitions[a].type="object")},render:function(){$(this.el).html(Handlebars.templates.main(this.model)),this.info=this.$(".info")[0],this.info&&this.info.addEventListener("click",this.onLinkClick,!0),this.model.securityDefinitions=this.model.securityDefinitions||{};for(var e={},t=0,n=0;n<this.model.apisArray.length;n++){for(var i=this.model.apisArray[n],r=i.name;"undefined"!=typeof e[r];)r=r+"_"+t,t+=1;i.id=r,e[r]=i,this.addResource(i,this.model.auths)}return $(".propWrap").hover(function(){$(".optionsWrapper",$(this)).show()},function(){$(".optionsWrapper",$(this)).hide()}),this},addResource:function(e,t){e.id=e.id.replace(/\s/g,"_"),e.definitions=this.model.definitions;var n=new SwaggerUi.Views.ResourceView({model:e,router:this.router,tagName:"li",id:"resource_"+e.id,className:"resource",auths:t,swaggerOptions:this.options.swaggerOptions});$("#resources",this.el).append(n.render().el)},clear:function(){$(this.el).html("")},onLinkClick:function(e){var t=e.target;"A"===t.tagName&&t.href&&!t.target&&(e.preventDefault(),window.open(t.href,"_blank"))}}),SwaggerUi.Models.Oauth2Model=Backbone.Model.extend({defaults:{scopes:{}},initialize:function(){this.on("change",this.validate)},setScopes:function(e,t){var n=_.extend({},this.attributes),i=_.findIndex(n.scopes,function(t){return t.scope===e});n.scopes[i].checked=t,this.set(n),this.validate()},validate:function(){var e=!1,t=this.get("scopes"),n=_.findIndex(t,function(e){return e.checked===!0});return t.length>0&&n>=0&&(e=!0),0===t.length&&(e=!0),this.set("valid",e),e}}),SwaggerUi.Views.Oauth2View=Backbone.View.extend({events:{"change .oauth-scope":"scopeChange"},template:Handlebars.templates.oauth2,render:function(){return this.$el.html(this.template(this.model.toJSON())),this},scopeChange:function(e){var t=$(e.target).prop("checked"),n=$(e.target).data("scope");this.model.setScopes(n,t)}}),SwaggerUi.Views.OperationView=Backbone.View.extend({invocationUrl:null,events:{"submit .sandbox":"submitOperation",
+"click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent","mouseenter .api-ic":"mouseEnter","dblclick .curl":"selectText","change [name=responseContentType]":"showSnippet"},initialize:function(e){return e=e||{},this.router=e.router,this.auths=e.auths,this.parentId=this.model.parentId,this.nickname=this.model.nickname,this.model.encodedParentId=encodeURIComponent(this.parentId),e.swaggerOptions&&(this.model.defaultRendering=e.swaggerOptions.defaultModelRendering,e.swaggerOptions.showRequestHeaders&&(this.model.showRequestHeaders=!0)),this},selectText:function(e){var t,n,i=document,r=e.target.firstChild;i.body.createTextRange?(t=document.body.createTextRange(),t.moveToElementText(r),t.select()):window.getSelection&&(n=window.getSelection(),t=document.createRange(),t.selectNodeContents(r),n.removeAllRanges(),n.addRange(t))},mouseEnter:function(e){var t=$(this.el).find(".content"),n=e.pageX,i=e.pageY,r=$(window).scrollLeft(),a=$(window).scrollTop(),o=r+$(window).width(),s=a+$(window).height(),l=t.width(),u=t.height();n+l>o&&(n=o-l),r>n&&(n=r),i+u>s&&(i=s-u),a>i&&(i=a);var c={};c.top=i,c.left=n,t.css(c)},render:function(){var e,t,n,i,r,a,o,s,l,u,c,p,h,f,d,m,y,g,v,b,w,x,A,j,O,S,k,C,E,I,T,M,U,P,L,D,R,N,F,B,q;if(a=jQuery.inArray(this.model.method,this.model.supportedSubmitMethods())>=0,a||(this.model.isReadOnly=!0),this.model.description=this.model.description||this.model.notes,this.model.oauth=null,m=this.model.authorizations||this.model.security)if(Array.isArray(m))for(l=0,u=m.length;u>l;l++){n=m[l];for(s in n)for(e in this.auths)if(t=this.auths[e],s===t.name&&"oauth2"===t.type){this.model.oauth={},this.model.oauth.scopes=[],A=t.value.scopes;for(o in A)R=A[o],M=n[s].indexOf(o),M>=0&&(g={scope:o,description:R},this.model.oauth.scopes.push(g))}}else for(o in m)if(R=m[o],"oauth2"===o)for(null===this.model.oauth&&(this.model.oauth={}),void 0===this.model.oauth.scopes&&(this.model.oauth.scopes=[]),d=0,c=R.length;c>d;d++)g=R[d],this.model.oauth.scopes.push(g);if("undefined"!=typeof this.model.responses){this.model.responseMessages=[],j=this.model.responses;for(i in j)N=j[i],I=null,T=this.model.responses[i].schema,T&&T.$ref&&(I=T.$ref,-1!==I.indexOf("#/definitions/")&&(I=I.replace(/^.*#\/definitions\//,""))),this.model.responseMessages.push({code:i,message:N.description,responseModel:I,headers:N.headers,schema:T})}if("undefined"==typeof this.model.responseMessages&&(this.model.responseMessages=[]),U=null,F=this.model.produces,B=this.contains(F,"xml"),q=B?this.contains(F,"json"):!0,this.model.successResponse){L=this.model.successResponse;for(s in L)N=L[s],this.model.successCode=s,"object"==typeof N&&"function"==typeof N.createJSONSample?(this.model.successDescription=N.description,this.model.headers=this.parseResponseHeaders(N.headers),U={sampleJSON:q?JSON.stringify(SwaggerUi.partials.signature.createJSONSample(N),void 0,2):!1,isParam:!1,sampleXML:B?SwaggerUi.partials.signature.createXMLSample(N.name,N.definition,N.models):!1,signature:SwaggerUi.partials.signature.getModelSignature(N.name,N.definition,N.models,N.modelPropertyMacro)}):U={signature:SwaggerUi.partials.signature.getPrimitiveSignature(N)}}else this.model.responseClassSignature&&"string"!==this.model.responseClassSignature&&(U={sampleJSON:this.model.responseSampleJSON,isParam:!1,signature:this.model.responseClassSignature});for($(this.el).html(Handlebars.templates.operation(this.model)),U?(U.defaultRendering=this.model.defaultRendering,E=new SwaggerUi.Views.SignatureView({model:U,router:this.router,tagName:"div"}),$(".model-signature",$(this.el)).append(E.render().el)):(this.model.responseClassSignature="string",$(".model-signature",$(this.el)).html(this.model.type)),r={isParam:!1},r.consumes=this.model.consumes,r.produces=this.model.produces,O=this.model.parameters,y=0,p=O.length;p>y;y++)b=O[y],D=b.type||b.dataType||"","undefined"==typeof D&&(I=b.schema,I&&I.$ref&&(x=I.$ref,D=0===x.indexOf("#/definitions/")?x.substring("#/definitions/".length):x)),D&&"file"===D.toLowerCase()&&(r.consumes||(r.consumes="multipart/form-data")),b.type=D;for(C=new SwaggerUi.Views.ResponseContentTypeView({model:r,router:this.router}),$(".response-content-type",$(this.el)).append(C.render().el),S=this.model.parameters,v=0,h=S.length;h>v;v++)b=S[v],this.addParameter(b,r.consumes);for(k=this.model.responseMessages,w=0,f=k.length;f>w;w++)P=k[w],P.isXML=B,P.isJSON=q,_.isUndefined(P.headers)||(P.headers=this.parseHeadersType(P.headers)),this.addStatusCode(P);if(Array.isArray(this.model.security)){var V=SwaggerUi.utils.parseSecurityDefinitions(this.model.security);V.isLogout=!_.isEmpty(window.swaggerUi.api.clientAuthorizations.authz),this.authView=new SwaggerUi.Views.AuthButtonView({data:V,router:this.router,isOperation:!0,model:{scopes:V.scopes}}),this.$(".authorize-wrapper").append(this.authView.render().el)}return this.showSnippet(),this},parseHeadersType:function(e){var t={string:{"date-time":"dateTime",date:"date"}};return _.forEach(e,function(e){var n;e=e||{},n=t[e.type]&&t[e.type][e.format],_.isUndefined(n)||(e.type=n)}),e},contains:function(e,t){return e.filter(function(e){return e.indexOf(t)>-1?!0:void 0}).length},parseResponseHeaders:function(e){var t="; ",n=_.clone(e);return _.forEach(n,function(e){var n=[];_.forEach(e,function(e,t){var i=["type","description"];-1===i.indexOf(t.toLowerCase())&&n.push(t+": "+e)}),n.join(t),e.other=n}),n},addParameter:function(e,t){e.consumes=t,e.defaultRendering=this.model.defaultRendering,e.schema&&($.extend(!0,e.schema,this.model.definitions[e.type]),e.schema.definitions=this.model.definitions,e.schema.type||(e.schema.type="object"),e.schema.title||(e.schema.title=" "));var n=new SwaggerUi.Views.ParameterView({model:e,tagName:"tr",readOnly:this.model.isReadOnly,swaggerOptions:this.options.swaggerOptions});$(".operation-params",$(this.el)).append(n.render().el)},addStatusCode:function(e){e.defaultRendering=this.model.defaultRendering;var t=new SwaggerUi.Views.StatusCodeView({model:e,tagName:"tr",router:this.router});$(".operation-status",$(this.el)).append(t.render().el)},submitOperation:function(e){var t,n,i,r,a;if(null!==e&&e.preventDefault(),n=$(".sandbox",$(this.el)),t=!0,n.find("input.required").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){$(e).focus()}}(this)}),t=!1)}),n.find("textarea.required:visible").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){return $(e).focus()}}(this)}),t=!1)}),n.find("select.required").each(function(){$(this).removeClass("error"),-1===this.selectedIndex&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){$(e).focus()}}(this)}),t=!1)}),t){if(r=this.getInputMap(n),i=this.isFileUpload(n),a={parent:this},this.options.swaggerOptions)for(var o in this.options.swaggerOptions)a[o]=this.options.swaggerOptions[o];var s;for(s=0;s<this.model.parameters.length;s++){var l=this.model.parameters[s];if(l.jsonEditor&&l.jsonEditor.isEnabled()){var u=l.jsonEditor.getValue();r[l.name]=JSON.stringify(u)}}return a.responseContentType=$("div select[name=responseContentType]",$(this.el)).val(),a.requestContentType=$("div select[name=parameterContentType]",$(this.el)).val(),$(".response_throbber",$(this.el)).show(),i?($(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(this.invocationUrl),a.useJQuery=!0,r.parameterContentType="multipart/form-data",this.map=r,this.model.execute(r,a,this.showCompleteStatus,this.showErrorStatus,this)):(this.map=r,this.model.execute(r,a,this.showCompleteStatus,this.showErrorStatus,this))}},getInputMap:function(e){var t,n,i,r,a,o,s,l,u,c,p,h;for(t={},n=e.find("input"),i=0,r=n.length;r>i;i++)a=n[i],null!==a.value&&jQuery.trim(a.value).length>0&&(t[a.name]=a.value),"file"===a.type&&(t[a.name]=a.files[0]);for(o=e.find("textarea"),s=0,l=o.length;l>s;s++)a=o[s],u=this.getTextAreaValue(a),null!==u&&jQuery.trim(u).length>0&&(t[a.name]=u);for(c=e.find("select"),p=0,h=c.length;h>p;p++)a=c[p],u=this.getSelectedValue(a),null!==u&&jQuery.trim(u).length>0&&(t[a.name]=u);return t},isFileUpload:function(e){var t,n,i,r,a=!1;for(t=e.find("input"),n=0,i=t.length;i>n;n++)r=t[n],"file"===r.type&&(a=!0);return a},success:function(e,t){t.showCompleteStatus(e)},wrap:function(e){var t,n,i,r,a,o,s;for(i={},n=e.getAllResponseHeaders().split("\r"),a=0,o=n.length;o>a;a++)r=n[a],t=r.match(/^([^:]*?):(.*)$/),t||(t=[]),t.shift(),void 0!==t[0]&&void 0!==t[1]&&(i[t[0].trim()]=t[1].trim());return s={},s.content={},s.content.data=e.responseText,s.headers=i,s.request={},s.request.url=this.invocationUrl,s.status=e.status,s},getSelectedValue:function(e){if(e.multiple){for(var t=[],n=0,i=e.options.length;i>n;n++){var r=e.options[n];r.selected&&t.push(r.value)}return t.length>0?t:null}return e.value},hideResponse:function(e){e&&e.preventDefault(),$(".response",$(this.el)).slideUp(),$(".response_hider",$(this.el)).fadeOut()},showResponse:function(e){var t=JSON.stringify(e,null,"	").replace(/\n/g,"<br>");$(".response_body",$(this.el)).html(_.escape(t))},showErrorStatus:function(e,t){t.showStatus(e)},showCompleteStatus:function(e,t){t.showStatus(e)},formatXml:function(e){var t,n,i,r,a,o,s,l,u,c,p,h,f;for(p=/(>)(<)(\/*)/g,f=/[ ]*(.*)[ ]+\n/g,t=/(<.+>)(.+\n)/g,e=e.replace(/\r\n/g,"\n").replace(p,"$1\n$2$3").replace(f,"$1\n").replace(t,"$1\n$2"),c=0,i="",l=e.split("\n"),r=0,o="other",h={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0},n=function(e){var t,n,a,s,l,u,c;u={single:Boolean(e.match(/<.+\/>/)),closing:Boolean(e.match(/<\/.+>/)),opening:Boolean(e.match(/<[^!?].*>/))},l=function(){var e;e=[];for(a in u)c=u[a],c&&e.push(a);return e}()[0],l=void 0===l?"other":l,t=o+"->"+l,o=l,s="",r+=h[t],s=function(){var e,t,i;for(i=[],n=e=0,t=r;t>=0?t>e:e>t;n=t>=0?++e:--e)i.push("  ");return i}().join(""),"opening->closing"===t?i=i.substr(0,i.length-1)+e+"\n":i+=s+e+"\n"},a=0,s=l.length;s>a;a++)u=l[a],n(u);return i},showStatus:function(e){var t,n;void 0===e.content?(n=e.data,t=e.url):(n=e.content.data,t=e.request.url);var i=e.headers;n=jQuery.trim(n);var r=null;i&&(r=i["Content-Type"]||i["content-type"],r&&(r=r.split(";")[0].trim())),$(".response_body",$(this.el)).removeClass("json"),$(".response_body",$(this.el)).removeClass("xml");var a,o,s=function(e){var t=document.createElement("audio");return!(!t.canPlayType||!t.canPlayType(e).replace(/no/,""))};if(n)if("application/json"===r||/\+json$/.test(r)){var l=null;try{l=JSON.stringify(JSON.parse(n),null,"  ")}catch(u){l="can't parse JSON.  Raw result:\n\n"+n}o=$("<code />").text(l),a=$('<pre class="json" />').append(o)}else if("application/xml"===r||/\+xml$/.test(r))o=$("<code />").text(this.formatXml(n)),a=$('<pre class="xml" />').append(o);else if("text/html"===r)o=$("<code />").html(_.escape(n)),a=$('<pre class="xml" />').append(o);else if(/text\/plain/.test(r))o=$("<code />").text(n),a=$('<pre class="plain" />').append(o);else if(/^image\//.test(r))a=$("<img>").attr("src",t);else if(/^audio\//.test(r)&&s(r))a=$("<audio controls>").append($("<source>").attr("src",t).attr("type",r));else if(i["Content-Disposition"]&&/attachment/.test(i["Content-Disposition"])||i["content-disposition"]&&/attachment/.test(i["content-disposition"])||i["Content-Description"]&&/File Transfer/.test(i["Content-Description"])||i["content-description"]&&/File Transfer/.test(i["content-description"]))if("Blob"in window){var c=r||"text/html",p=new Blob([n],{type:c}),h=document.createElement("a"),f=window.URL.createObjectURL(p),d=e.url.substr(e.url.lastIndexOf("/")+1),m=[c,d,f].join(":"),y=i["content-disposition"]||i["Content-Disposition"];if("undefined"!=typeof y){var g=/filename=([^;]*);?/.exec(y);null!==g&&g.length>1&&(m=g[1])}h.setAttribute("href",f),h.setAttribute("download",m),h.innerText="Download "+d,a=$("<div/>").append(h)}else a=$('<pre class="json" />').append("Download headers detected but your browser does not support downloading binary via XHR (Blob).");else i.location||i.Location?window.location=e.url:(o=$("<code />").text(n),a=$('<pre class="json" />').append(o));else o=$("<code />").text("no content"),a=$('<pre class="json" />').append(o);var v=a;$(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(t),$(".response_code",$(this.el)).html("<pre>"+e.status+"</pre>"),$(".response_body",$(this.el)).html(v),$(".response_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(e.headers,null,"  ")).replace(/\n/g,"<br>")+"</pre>"),$(".response",$(this.el)).slideDown(),$(".response_hider",$(this.el)).show(),$(".response_throbber",$(this.el)).hide();var b=this.model.asCurl(this.map,{responseContentType:r});b=b.replace("!","&#33;"),$("div.curl",$(this.el)).html("<pre>"+_.escape(b)+"</pre>");var w=this.options.swaggerOptions;if(w.showRequestHeaders){var x=$(".sandbox",$(this.el)),A=this.getInputMap(x),j=this.model.getHeaderParams(A);delete j["Content-Type"],$(".request_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(j,null,"  ")).replace(/\n/g,"<br>")+"</pre>")}var O=$(".response_body",$(this.el))[0];return w.highlightSizeThreshold&&"undefined"!=typeof e.data&&e.data.length>w.highlightSizeThreshold?O:hljs.highlightBlock(O)},toggleOperationContent:function(e){var t=$("#"+Docs.escapeResourceName(this.parentId+"_"+this.nickname+"_content"));t.is(":visible")?($.bbq.pushState("#/",2),e.preventDefault(),Docs.collapseOperation(t)):Docs.expandOperation(t)},getTextAreaValue:function(e){var t,n,i,r;if(null===e.value||0===jQuery.trim(e.value).length)return null;if(t=this.getParamByName(e.name),t&&t.type&&"array"===t.type.toLowerCase()){for(n=e.value.split("\n"),i=[],r=0;r<n.length;r++)null!==n[r]&&jQuery.trim(n[r]).length>0&&i.push(n[r]);return i.length>0?i:null}return e.value},showSnippet:function(){var e,t=this.$("[name=responseContentType]"),n=this.$(".operation-status .snippet_xml, .response-class .snippet_xml"),i=this.$(".operation-status .snippet_json, .response-class .snippet_json");t.length&&(e=t.val(),e.indexOf("xml")>-1?(n.show(),i.hide()):(i.show(),n.hide()))},getParamByName:function(e){var t;if(this.model.parameters)for(t=0;t<this.model.parameters.length;t++)if(this.model.parameters[t].name===e)return this.model.parameters[t];return null}}),SwaggerUi.Views.ParameterContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.parameterContentTypeId="pct"+Math.random(),$(this.el).html(Handlebars.templates.parameter_content_type(this.model)),this}}),SwaggerUi.Views.ParameterView=Backbone.View.extend({events:{"change [name=parameterContentType]":"toggleParameterSnippet"},initialize:function(){Handlebars.registerHelper("isArray",function(e,t){var n=e.type&&e.type.toLowerCase();return"array"===n||e.allowMultiple?t.fn(this):t.inverse(this)})},render:function(){var e,t,n=this.model.type||this.model.dataType,i=this.model.modelSignature.type,r=this.model.modelSignature.definitions,a=this.model.schema||{},o=this.model.consumes||[];if("undefined"==typeof n&&a.$ref){var s=a.$ref;n=0===s.indexOf("#/definitions/")?s.substring("#/definitions/".length):s}this.model.type=n,this.model.paramType=this.model["in"]||this.model.paramType,this.model.isBody="body"===this.model.paramType||"body"===this.model["in"],this.model.isFile=n&&"file"===n.toLowerCase(),"undefined"==typeof this.model["default"]&&(this.model["default"]=this.model.defaultValue),this.model.hasDefault="undefined"!=typeof this.model["default"],this.model.valueId="m"+this.model.name+Math.random(),this.model.allowableValues&&(this.model.isList=!0);var l=this.contains(o,"xml"),u=l?this.contains(o,"json"):!0;e=SwaggerUi.partials.signature.createParameterJSONSample(i,r);var c=this.template();$(this.el).html(c(this.model));var p={sampleJSON:u?e:!1,sampleXML:e&&l?SwaggerUi.partials.signature.createXMLSample("",a,r,!0):!1,isParam:!0,signature:SwaggerUi.partials.signature.getParameterModelSignature(i,r),defaultRendering:this.model.defaultRendering};e?(t=new SwaggerUi.Views.SignatureView({model:p,tagName:"div"}),$(".model-signature",$(this.el)).append(t.render().el)):$(".model-signature",$(this.el)).html(this.model.signature);var h=!1;if(this.options.swaggerOptions.jsonEditor&&this.model.isBody&&this.model.schema){var f=$(this.el);this.model.jsonEditor=new JSONEditor($(".editor_holder",f)[0],{schema:this.model.schema,startval:this.model["default"],ajax:!0,disable_properties:!0,disable_edit_json:!0,iconlib:"swagger"}),p.jsonEditor=this.model.jsonEditor,$(".body-textarea",f).hide(),$(".editor_holder",f).show(),$(".parameter-content-type",f).change(function(e){"application/xml"===e.target.value?($(".body-textarea",f).show(),$(".editor_holder",f).hide(),this.model.jsonEditor.disable()):($(".body-textarea",f).hide(),$(".editor_holder",f).show(),this.model.jsonEditor.enable())})}this.model.isBody&&(h=!0);var d={isParam:h};if(d.consumes=this.model.consumes,h){var m=new SwaggerUi.Views.ParameterContentTypeView({model:d});$(".parameter-content-type",$(this.el)).append(m.render().el),this.toggleParameterSnippet()}else{var y=new SwaggerUi.Views.ResponseContentTypeView({model:d});$(".response-content-type",$(this.el)).append(y.render().el),this.toggleResponseSnippet()}return this},contains:function(e,t){return e.filter(function(e){return e.indexOf(t)>-1?!0:void 0}).length},toggleParameterSnippet:function(){var e=this.$("[name=parameterContentType]").val();this.toggleSnippet(e)},toggleResponseSnippet:function(){var e=this.$("[name=responseContentType]");e.length&&this.toggleSnippet(e.val())},toggleSnippet:function(e){e=e||"",e.indexOf("xml")>-1?(this.$(".snippet_xml").show(),this.$(".snippet_json").hide()):(this.$(".snippet_json").show(),this.$(".snippet_xml").hide())},template:function(){return this.model.isList?Handlebars.templates.param_list:this.options.readOnly?this.model.required?Handlebars.templates.param_readonly_required:Handlebars.templates.param_readonly:this.model.required?Handlebars.templates.param_required:Handlebars.templates.param}}),SwaggerUi.partials.signature=function(){function e(e){var t,r=e.name,a=e.definition,o=e.config,s=e.models,l=e.config.isParam,u=[],c=a.properties,p=a.additionalProperties,h=a.xml,f=b(h);return f&&u.push(f),c||p?(c=c||{},t=_.map(c,function(e,t){var n,r;return l&&e.readOnly?"":(n=e.xml||{},r=i(t,e,s,o),n.attribute?(u.push(r),""):r)}).join(""),p&&(t+="<!-- additional elements allowed -->"),g(r,t,u)):n()}function t(e){return"<!-- Infinite loop $ref:"+e+" -->"}function n(e){return e=e?": "+e:"","<!-- invalid XML"+e+" -->"}function i(i,r,s,l){var u,c,p=_.isObject(r)?r.$ref:null;l=l||{},l.modelsToIgnore=l.modelsToIgnore||[];var h=_.isString(p)?a(p,i,s,l):o(i,r,s,l);if(!h)return n();switch(h.type){case"array":u=w(h);break;case"object":u=e(h);break;case"loop":u=t(h.name);break;default:u=A(h)}return p&&(c=l.modelsToIgnore.indexOf(p),c>-1&&l.modelsToIgnore.splice(c,1)),u}function r(e,t,n,i,r){if(arguments.length<4)throw new Error;this.config=r||{},this.config.modelsToIgnore=this.config.modelsToIgnore||[],this.name=v(e,n.xml),this.definition=n,this.models=i,this.type=t}function a(e,t,n,i){var a=u(e),o=n[a]||{},s=o.definition&&o.definition.type?o.definition.type:"object";return t=t||o.name,i.modelsToIgnore.indexOf(e)>-1?(s="loop",t=a):i.modelsToIgnore.push(e),o.definition?new r(t,s,o.definition,n,i):null}function o(e,t,n,i){var a=t.type||"object";return t?new r(e,a,t,n,i):null}function s(e,t,n,r){var a='<?xml version="1.0"?>';return p(a+i(e,t,n,{isParam:r}))}var l=function(e){return _.isPlainObject(e.schema)&&(e=l(e.schema)),e},u=function(e){return"undefined"==typeof e?null:0===e.indexOf("#/definitions/")?e.substring("#/definitions/".length):e},c=function(e){if(/^Inline Model \d+$/.test(e)&&this.inlineModels){var t=parseInt(e.substr("Inline Model".length).trim(),10),n=this.inlineModels[t];return n}return null},p=function(e){var t,n,i,r,a,o,s,l,u,c,p,h,f;for(p=/(>)(<)(\/*)/g,f=/[ ]*(.*)[ ]+\n/g,t=/(<.+>)(.+\n)/g,e=e.replace(p,"$1\n$2$3").replace(f,"$1\n").replace(t,"$1\n$2"),c=0,i="",l=e.split("\n"),r=0,o="other",h={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0},n=function(e){var t,n,a,s,l,u,c;u={single:Boolean(e.match(/<.+\/>/)),closing:Boolean(e.match(/<\/.+>/)),opening:Boolean(e.match(/<[^!?].*>/))},l=function(){var e;e=[];for(a in u)c=u[a],c&&e.push(a);return e}()[0],l=void 0===l?"other":l,t=o+"->"+l,o=l,s="",r+=h[t],s=function(){var e,t,i;for(i=[],n=e=0,t=r;t>=0?t>e:e>t;n=t>=0?++e:--e)i.push("  ");return i}().join(""),"opening->closing"===t?i=i.substr(0,i.length-1)+e+"\n":i+=s+e+"\n"},a=0,s=l.length;s>a;a++)u=l[a],n(u);return i},h=function(e,t,n,i){function r(e,t,i){var r,a=t;return e.$ref?(a=e.title||u(e.$ref),r=n[u(e.$ref)]):_.isUndefined(t)&&(a=e.title||"Inline Model "+ ++m,r={definition:e}),i!==!0&&(f[a]=_.isUndefined(r)?{}:r.definition),a}function a(e){var t='<span class="propType">',n=e.type||"object";return e.$ref?t+=r(e,u(e.$ref)):"object"===n?t+=_.isUndefined(e.properties)?"object":r(e):"array"===n?(t+="Array[",_.isArray(e.items)?t+=_.map(e.items,r).join(","):_.isPlainObject(e.items)?t+=_.isUndefined(e.items.$ref)?_.isUndefined(e.items.type)||-1!==_.indexOf(["array","object"],e.items.type)?r(e.items):e.items.type:r(e.items,u(e.items.$ref)):(console.log("Array type's 'items' schema is not an array or an object, cannot process"),t+="object"),t+="]"):t+=e.type,t+="</span>"}function o(e,t){var n="",i=e.type||"object",r="array"===i;switch(_.isUndefined(e.description)||(t+=': <span class="propDesc">'+e.description+"</span>"),e["enum"]&&(t+=' = <span class="propVals">[\''+e["enum"].join("', '")+"']</span>"),r&&(i=_.isPlainObject(e.items)&&!_.isUndefined(e.items.type)?e.items.type:"object"),_.isUndefined(e["default"])||(n+=h("Default",e["default"])),i){case"string":e.minLength&&(n+=h("Min. Length",e.minLength)),e.maxLength&&(n+=h("Max. Length",e.maxLength)),e.pattern&&(n+=h("Reg. Exp.",e.pattern));break;case"integer":case"number":e.minimum&&(n+=h("Min. Value",e.minimum)),e.exclusiveMinimum&&(n+=h("Exclusive Min.","true")),e.maximum&&(n+=h("Max. Value",e.maximum)),e.exclusiveMaximum&&(n+=h("Exclusive Max.","true")),e.multipleOf&&(n+=h("Multiple Of",e.multipleOf))}if(r&&(e.minItems&&(n+=h("Min. Items",e.minItems)),e.maxItems&&(n+=h("Max. Items",e.maxItems)),e.uniqueItems&&(n+=h("Unique Items","true")),e.collectionFormat&&(n+=h("Coll. Format",e.collectionFormat))),_.isUndefined(e.items)&&_.isArray(e["enum"])){var a;a="number"===i||"integer"===i?e["enum"].join(", "):'"'+e["enum"].join('", "')+'"',n+=h("Enum",a)}return n.length>0&&(t='<span class="propWrap">'+t+'<table class="optionsWrapper"><tr><th colspan="2">'+i+"</th></tr>"+n+"</table></span>"),t}function s(e,t){var s,h=e.type||"object",f="array"===e.type,m=c+t+" "+(f?"[":"{")+p;return t&&d.push(t),f?_.isArray(e.items)?m+="<div>"+_.map(e.items,function(e){var t=e.type||"object";return _.isUndefined(e.$ref)?_.indexOf(["array","object"],t)>-1?"object"===t&&_.isUndefined(e.properties)?"object":r(e):o(e,t):r(e,u(e.$ref))}).join(",</div><div>"):_.isPlainObject(e.items)?m+=_.isUndefined(e.items.$ref)?_.indexOf(["array","object"],e.items.type||"object")>-1?(_.isUndefined(e.items.type)||"object"===e.items.type)&&_.isUndefined(e.items.properties)?"<div>object</div>":"<div>"+r(e.items)+"</div>":"<div>"+o(e.items,e.items.type)+"</div>":"<div>"+r(e.items,u(e.items.$ref))+"</div>":(console.log("Array type's 'items' property is not an array or an object, cannot process"),m+="<div>object</div>"):e.$ref?m+="<div>"+r(e,t)+"</div>":"object"===h?(_.isPlainObject(e.properties)&&(s=_.map(e.properties,function(t,r){var s,c=_.indexOf(e.required,r)>=0,p=_.cloneDeep(t),h=c?"required":"",f='<span class="propName '+h+'">'+r+"</span> (";return p["default"]=i(p),p=l(p),_.isUndefined(p.$ref)||(s=n[u(p.$ref)],_.isUndefined(s)||-1!==_.indexOf([void 0,"array","object"],s.definition.type)||(p=l(s.definition))),f+=a(p),c||(f+=', <span class="propOptKey">optional</span>'),t.readOnly&&(f+=', <span class="propReadOnly">read only</span>'),f+=")","<div"+(t.readOnly?' class="readOnly"':"")+">"+o(p,f)}).join(",</div>")),s&&(m+=s+"</div>")):m+="<div>"+o(e,h)+"</div>",m+c+(f?"]":"}")+p}var c='<span class="strong">',p="</span>",h=function(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"};if(_.isObject(arguments[0])&&(e=void 0,t=arguments[0],n=arguments[1],i=arguments[2]),n=n||{},t=l(t),_.isEmpty(t))return c+"Empty"+p;if("string"==typeof t.$ref&&(e=u(t.$ref),t=n[e],"undefined"==typeof t))return c+e+" is not defined!"+p;"string"!=typeof e&&(e=t.title||"Inline Model"),t.definition&&(t=t.definition),"function"!=typeof i&&(i=function(e){return(e||{})["default"]});for(var f={},d=[],m=0,y=s(t,e);_.keys(f).length>0;)_.forEach(f,function(e,t){var n=_.indexOf(d,t)>-1;delete f[t],n||(d.push(t),y+="<br />"+s(e,t))});return y},f=function(e,t,n,i){e=l(e),"function"!=typeof i&&(i=function(e){return(e||{})["default"]}),n=n||{};var r,a,o=e.type||"object",s=e.format;return _.isUndefined(e.example)?_.isUndefined(e.items)&&_.isArray(e["enum"])&&(a=e["enum"][0]):a=e.example,_.isUndefined(a)&&(e.$ref?(r=t[u(e.$ref)],_.isUndefined(r)||(_.isUndefined(n[r.name])?(n[r.name]=r,a=f(r.definition,t,n,i),delete n[r.name]):a="array"===r.type?[]:{})):_.isUndefined(e["default"])?"string"===o?a="date-time"===s?(new Date).toISOString():"date"===s?(new Date).toISOString().split("T")[0]:"string":"integer"===o?a=0:"number"===o?a=0:"boolean"===o?a=!0:"object"===o?(a={},_.forEach(e.properties,function(e,r){var o=_.cloneDeep(e);o["default"]=i(e),a[r]=f(o,t,n,i)})):"array"===o&&(a=[],_.isArray(e.items)?_.forEach(e.items,function(e){a.push(f(e,t,n,i))}):_.isPlainObject(e.items)?a.push(f(e.items,t,n,i)):_.isUndefined(e.items)?a.push({}):console.log("Array type's 'items' property is not an array or an object, cannot process")):a=e["default"]),a},d=function(e,t){return t=t||{},t[e.name]=e,e.examples&&_.isPlainObject(e.examples)&&e.examples["application/json"]?(e.definition.example=e.examples["application/json"],_.isString(e.definition.example)&&(e.definition.example=jsyaml.safeLoad(e.definition.example))):e.definition.example||(e.definition.example=e.examples),f(e.definition,e.models,t,e.modelPropertyMacro)},m=function(e,t){var n,i;return e instanceof Array&&(i=!0,e=e[0]),"undefined"==typeof e?(e="undefined",n=!0):t[e]?(e=t[e],n=!1):c(e)?(e=c(e),n=!1):n=!0,n?i?"Array["+e+"]":e.toString():i?"Array["+h(e.name,e.definition,e.models,e.modelPropertyMacro)+"]":h(e.name,e.definition,e.models,e.modelPropertyMacro)},y=function(e,t){var n,i,r;if(t=t||{},n=e instanceof Array,r=n?e[0]:e,t[r]?i=d(t[r]):c(r)&&(i=d(c(r))),i){if(i=n?[i]:i,"string"==typeof i)return i;if(_.isObject(i)){var a=i;if(i instanceof Array&&i.length>0&&(a=i[0]),a.nodeName&&"Node"==typeof a){var o=(new XMLSerializer).serializeToString(a);return p(o)}return JSON.stringify(i,null,2)}return i}},g=function(e,t,i){var r,a;return i=i||[],a=i.map(function(e){return" "+e.name+'="'+e.value+'"'}).join(""),e?(r=["<",e,a,">",t,"</",e,">"],r.join("")):n("Node name is not provided")},v=function(e,t){var n=e||"";return t=t||{},t.name&&(n=t.name),t.prefix&&(n=t.prefix+":"+n),n},b=function(e){var t="",n="xmlns";return e=e||{},e.namespace?(t=e.namespace,e.prefix&&(n+=":"+e.prefix),{name:n,value:t}):t},w=function(e){var t,r=e.name,a=e.config,o=e.definition,s=e.models,l=o.items,u=o.xml||{},c=b(u),p=[];return l?(t=i(r,l,s,a),c&&p.push(c),u.wrapped&&(t=g(r,t,p)),t):n()},x=function(e){var t,n;switch(e=e||{},n=e.items||{},t=e.type||""){case"object":return"Object is not a primitive";case"array":return"Array["+(n.format||n.type)+"]";default:return e.format||t}},A=function(e){var t,i=e.name,r=e.definition,a={string:{date:new Date(1).toISOString().split("T")[0],"date-time":new Date(1).toISOString(),"default":"string"},integer:{"default":1},number:{"default":1.1},"boolean":{"default":!0}},o=r.type,s=r.format,l=r.xml||{},u=b(l),c=[];return _.keys(a).indexOf(o)<0?n():(t=_.isArray(r["enum"])?r["enum"][0]:r.example||a[o][s]||a[o]["default"],l.attribute?{name:i,value:t}:(u&&c.push(u),g(i,t,c)))};return{getModelSignature:h,createJSONSample:d,getParameterModelSignature:m,createParameterJSONSample:y,createSchemaXML:i,createXMLSample:s,getPrimitiveSignature:x}}(),SwaggerUi.Views.PopupView=Backbone.View.extend({events:{"click .api-popup-cancel":"cancelClick"},template:Handlebars.templates.popup,className:"api-popup-dialog",selectors:{content:".api-popup-content",main:"#swagger-ui-container"},initialize:function(){this.$el.html(this.template(this.model))},render:function(){return this.$(this.selectors.content).append(this.model.content),$(this.selectors.main).first().append(this.el),this.showPopup(),this},showPopup:function(){this.$el.show()},cancelClick:function(){this.remove()}}),SwaggerUi.Views.ResourceView=Backbone.View.extend({initialize:function(e){e=e||{},this.router=e.router,this.auths=e.auths,""===this.model.description&&(this.model.description=null),this.model.description&&(this.model.summary=this.model.description)},render:function(){var e={};$(this.el).html(Handlebars.templates.resource(this.model));for(var t=0;t<this.model.operationsArray.length;t++){for(var n=this.model.operationsArray[t],i=0,r=n.nickname;"undefined"!=typeof e[r];)r=r+"_"+i,i+=1;e[r]=n,n.nickname=r,n.parentId=this.model.id,n.definitions=this.model.definitions,this.addOperation(n)}return $(".toggleEndpointList",this.el).click(this.callDocs.bind(this,"toggleEndpointListForResource")),$(".collapseResource",this.el).click(this.callDocs.bind(this,"collapseOperationsForResource")),$(".expandResource",this.el).click(this.callDocs.bind(this,"expandOperationsForResource")),this},addOperation:function(e){e.number=this.number;var t=new SwaggerUi.Views.OperationView({model:e,router:this.router,tagName:"li",className:"endpoint",swaggerOptions:this.options.swaggerOptions,auths:this.auths});$(".endpoints",$(this.el)).append(t.render().el),this.number++},callDocs:function(e,t){t.preventDefault(),Docs[e](t.currentTarget.getAttribute("data-id"))}}),SwaggerUi.Views.ResponseContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.responseContentTypeId="rct"+Math.random(),$(this.el).html(Handlebars.templates.response_content_type(this.model)),this}}),SwaggerUi.Views.SignatureView=Backbone.View.extend({events:{"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet_json":"jsonSnippetMouseDown","mousedown .snippet_xml":"xmlSnippetMouseDown"},initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.signature(this.model)),"model"===this.model.defaultRendering?this.switchToDescription():this.switchToSnippet(),this},switchToDescription:function(e){e&&e.preventDefault(),$(".snippet",$(this.el)).hide(),$(".description",$(this.el)).show(),$(".description-link",$(this.el)).addClass("selected"),$(".snippet-link",$(this.el)).removeClass("selected")},switchToSnippet:function(e){e&&e.preventDefault(),$(".snippet",$(this.el)).show(),$(".description",$(this.el)).hide(),$(".snippet-link",$(this.el)).addClass("selected"),$(".description-link",$(this.el)).removeClass("selected")},snippetToTextArea:function(e){var t=$("textarea",$(this.el.parentNode.parentNode.parentNode));""!==$.trim(t.val())&&t.prop("placeholder")!==t.val()||(t.val(e),this.model.jsonEditor&&this.model.jsonEditor.isEnabled()&&this.model.jsonEditor.setValue(JSON.parse(this.model.sampleJSON)));
+},jsonSnippetMouseDown:function(e){this.model.isParam&&(e&&e.preventDefault(),this.snippetToTextArea(this.model.sampleJSON))},xmlSnippetMouseDown:function(e){this.model.isParam&&(e&&e.preventDefault(),this.snippetToTextArea(this.model.sampleXML))}}),SwaggerUi.Views.StatusCodeView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){var e,t,n=this.router.api.models[this.model.responseModel];return $(this.el).html(Handlebars.templates.status_code(this.model)),e=this.router.api.models.hasOwnProperty(this.model.responseModel)?{sampleJSON:JSON.stringify(SwaggerUi.partials.signature.createJSONSample(n),void 0,2),sampleXML:this.model.isXML?SwaggerUi.partials.signature.createXMLSample("",this.model.schema,this.router.api.models):!1,isParam:!1,signature:SwaggerUi.partials.signature.getModelSignature(this.model.responseModel,n,this.router.api.models),defaultRendering:this.model.defaultRendering}:{signature:SwaggerUi.partials.signature.getPrimitiveSignature(this.model.schema)},t=new SwaggerUi.Views.SignatureView({model:e,tagName:"div"}),$(".model-signature",this.$el).append(t.render().el),this}})}).call(this);
\ No newline at end of file
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
index 31b1729..8672065 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
@@ -36,7 +36,12 @@ import org.killbill.billing.client.model.PaymentMethod;
 import org.killbill.billing.client.model.PaymentMethodPluginDetail;
 import org.killbill.billing.client.model.PluginProperty;
 import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.Tags;
+import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
+import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.tag.ControlTagType;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 
 public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedDB {
@@ -111,8 +116,22 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         return killBillClient.getAccount(input.getExternalKey());
     }
 
+    protected Account createAccountWithExternalPaymentMethod() throws Exception {
+        final Account input = createAccount();
+
+        final PaymentMethodPluginDetail info = new PaymentMethodPluginDetail();
+        final PaymentMethod paymentMethodJson = new PaymentMethod(null, UUIDs.randomUUID().toString(), input.getAccountId(),
+                                                                  true, ExternalPaymentProviderPlugin.PLUGIN_NAME, info);
+        killBillClient.createPaymentMethod(paymentMethodJson, requestOptions);
+        return killBillClient.getAccount(input.getExternalKey(), requestOptions);
+    }
+
     protected Account createAccount() throws Exception {
-        final Account input = getAccount();
+        return createAccount(null);
+    }
+
+    protected Account createAccount(final UUID parentAccountId) throws Exception {
+        final Account input = getAccount(parentAccountId);
         return killBillClient.createAccount(input, createdBy, reason, comment);
     }
 
@@ -126,7 +145,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         input.setBillingPeriod(billingPeriod);
         input.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-        return killBillClient.createSubscription(input, waitCompletion ? DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC : -1, createdBy, reason, comment);
+        return killBillClient.createSubscription(input, null, waitCompletion ? DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC : -1, basicRequestOptions());
     }
 
     protected Account createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice() throws Exception {
@@ -143,6 +162,37 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         return accountJson;
     }
 
+    protected Account createAccountWithExternalPMBundleAndSubscriptionAndManualPayTagAndWaitForFirstInvoice() throws Exception {
+        final Account accountJson = createAccountWithExternalPaymentMethod();
+        assertNotNull(accountJson);
+
+        final Tags accountTag = killBillClient.createAccountTag(accountJson.getAccountId(), ControlTagType.MANUAL_PAY.getId(), requestOptions);
+        assertNotNull(accountTag);
+        assertEquals(accountTag.get(0).getTagDefinitionId(), ControlTagType.MANUAL_PAY.getId());
+
+        // Add a bundle, subscription and move the clock to get the first invoice
+        final Subscription subscriptionJson = createEntitlement(accountJson.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                                                                ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        assertNotNull(subscriptionJson);
+        clock.addDays(32);
+        crappyWaitForLackOfProperSynchonization();
+
+        return accountJson;
+    }
+
+    protected Account createAccountNoPMBundleAndSubscription() throws Exception {
+        // Create an account with no payment method
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        // Add a bundle, subscription and move the clock to get the first invoice
+        final Subscription subscriptionJson = createEntitlement(accountJson.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                                                                ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        assertNotNull(subscriptionJson);
+
+        return accountJson;
+    }
+
     protected Account createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice() throws Exception {
         // Create an account with no payment method
         final Account accountJson = createAccount();
@@ -161,10 +211,18 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
     }
 
     protected Account getAccount() {
-        return getAccount(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString().substring(0, 5) + '@' + UUID.randomUUID().toString().substring(0, 5));
+        return getAccount(null);
+    }
+
+    protected Account getAccount(final UUID parentAccountId) {
+        return getAccount(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString().substring(0, 5) + '@' + UUID.randomUUID().toString().substring(0, 5), parentAccountId);
     }
 
     public Account getAccount(final String name, final String externalKey, final String email) {
+        return getAccount(name, externalKey, email, null);
+    }
+
+    public Account getAccount(final String name, final String externalKey, final String email, final UUID parentAccountId) {
         final UUID accountId = UUID.randomUUID();
         final int length = 4;
         final String currency = DEFAULT_CURRENCY;
@@ -178,10 +236,12 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
         final String country = "France";
         final String locale = "fr";
         final String phone = "81 53 26 56";
+        final String notes = "notes";
+        final boolean isPaymentDelegatedToParent = parentAccountId != null;
 
         // Note: the accountId payload is ignored on account creation
-        return new Account(accountId, name, length, externalKey, email, null, currency, null, timeZone,
-                           address1, address2, postalCode, company, city, state, country, locale, phone, false, false, null, null);
+        return new Account(accountId, name, length, externalKey, email, null, currency, parentAccountId, isPaymentDelegatedToParent, null, timeZone,
+                           address1, address2, postalCode, company, city, state, country, locale, phone, notes, false, false, null, null);
     }
 
     /**
@@ -189,7 +249,12 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
      * but until we have a strong need for it, this is in the TODO list...
      */
     protected void crappyWaitForLackOfProperSynchonization() throws Exception {
-        Thread.sleep(5000);
+        crappyWaitForLackOfProperSynchonization(5000);
+    }
+
+
+    protected void crappyWaitForLackOfProperSynchonization(int sleepValueMSec) throws Exception {
+        Thread.sleep(sleepValueMSec);
     }
 
     /**
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
index 5972d5f..d714352 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccount.java
@@ -116,8 +116,9 @@ public class TestAccount extends TestJaxrsBase {
         // Update Account
         final Account newInput = new Account(input.getAccountId(),
                                              "zozo", 4, input.getExternalKey(), "rr@google.com", 18,
-                                             "USD", null, "UTC", "bl1", "bh2", "", "", "ca", "San Francisco", "usa", "en", "415-255-2991",
-                                             false, false, null, null);
+                                             "USD", null, false, null, "UTC",
+                                             "bl1", "bh2", "", "", "ca", "San Francisco", "usa", "en", "415-255-2991",
+                                             "notes", false, false, null, null);
         final Account updatedAccount = killBillClient.updateAccount(newInput, createdBy, reason, comment);
         Assert.assertTrue(updatedAccount.equals(newInput));
 
@@ -185,7 +186,7 @@ public class TestAccount extends TestJaxrsBase {
         //
         // DELETE NON DEFAULT PM
         //
-        killBillClient.deletePaymentMethod(paymentMethodCC.getPaymentMethodId(), false, createdBy, reason, comment);
+        killBillClient.deletePaymentMethod(paymentMethodCC.getPaymentMethodId(), false, false, createdBy, reason, comment);
 
         //
         // FETCH ALL PAYMENT METHODS
@@ -197,7 +198,7 @@ public class TestAccount extends TestJaxrsBase {
         // DELETE DEFAULT PAYMENT METHOD (without special flag first)
         //
         try {
-            killBillClient.deletePaymentMethod(paymentMethodPP.getPaymentMethodId(), false, createdBy, reason, comment);
+            killBillClient.deletePaymentMethod(paymentMethodPP.getPaymentMethodId(), false, false, createdBy, reason, comment);
             fail();
         } catch (final KillBillClientException e) {
         }
@@ -205,7 +206,7 @@ public class TestAccount extends TestJaxrsBase {
         //
         // RETRY TO DELETE DEFAULT PAYMENT METHOD (with special flag this time)
         //
-        killBillClient.deletePaymentMethod(paymentMethodPP.getPaymentMethodId(), true, createdBy, reason, comment);
+        killBillClient.deletePaymentMethod(paymentMethodPP.getPaymentMethodId(), true, false, createdBy, reason, comment);
 
         // CHECK ACCOUNT IS NOW AUTO_PAY_OFF
         final List<Tag> tagsJson = killBillClient.getAccountTags(accountJson.getAccountId());
@@ -375,4 +376,65 @@ public class TestAccount extends TestJaxrsBase {
             Assert.assertEquals(accountsByKey.get(0), output);
         }
     }
+
+    @Test(groups = "slow", description = "Can create and retrieve parent/children accounts")
+    public void testParentAccountOk() throws Exception {
+
+        final Account parentAccount = createAccount();
+
+        final Account childInput = getAccount();
+        childInput.setParentAccountId(parentAccount.getAccountId());
+        childInput.setIsPaymentDelegatedToParent(true);
+        final Account childAccount = killBillClient.createAccount(childInput, createdBy, reason, comment);
+
+        // Retrieves child account by external key
+        final Account retrievedAccount = killBillClient.getAccount(childAccount.getExternalKey());
+        Assert.assertTrue(retrievedAccount.equals(childAccount));
+        Assert.assertEquals(retrievedAccount.getParentAccountId(), parentAccount.getAccountId());
+        Assert.assertTrue(retrievedAccount.getIsPaymentDelegatedToParent());
+    }
+
+    @Test(groups = "slow", description = "retrieve children accounts by parent account id")
+    public void testGetChildrenAccounts() throws Exception {
+
+        final Account parentAccount = createAccount();
+
+        final Account childInput = getAccount();
+        childInput.setParentAccountId(parentAccount.getAccountId());
+        childInput.setIsPaymentDelegatedToParent(true);
+        Account childAccount = killBillClient.createAccount(childInput, createdBy, reason, comment);
+        childAccount = killBillClient.getAccount(childAccount.getAccountId(), true, true, basicRequestOptions());
+
+        final Account childInput2 = getAccount();
+        childInput2.setParentAccountId(parentAccount.getAccountId());
+        childInput2.setIsPaymentDelegatedToParent(true);
+        Account childAccount2 = killBillClient.createAccount(childInput2, createdBy, reason, comment);
+        childAccount2 = killBillClient.getAccount(childAccount2.getAccountId(), true, true, basicRequestOptions());
+
+        // Retrieves children accounts by parent account id
+        final Accounts childrenAccounts = killBillClient.getChildrenAccounts(parentAccount.getAccountId(), true, true, requestOptions);
+        Assert.assertEquals(childrenAccounts.size(), 2);
+
+        Assert.assertTrue(childrenAccounts.get(0).equals(childAccount));
+        Assert.assertTrue(childrenAccounts.get(1).equals(childAccount2));
+    }
+
+    @Test(groups = "slow", description = "retrieve an empty children accounts list by a non parent account id")
+    public void testEmptyGetChildrenAccounts() throws Exception {
+
+        // Retrieves children accounts by parent account id
+        final Accounts childrenAccounts = killBillClient.getChildrenAccounts(UUID.randomUUID(), false, false, requestOptions);
+        Assert.assertEquals(childrenAccounts.size(), 0);
+
+    }
+
+    @Test(groups = "slow", description = "retrieve an empty children accounts list by a null id")
+    public void testGetChildrenAccountsByNullId() throws Exception {
+
+        // Retrieves children accounts by parent account id
+        final Accounts childrenAccounts = killBillClient.getChildrenAccounts(null, true, true, requestOptions);
+        Assert.assertEquals(childrenAccounts.size(), 0);
+
+    }
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
index 62dd8b9..b0a42f3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -26,6 +26,8 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
+import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AccountTimeline;
 import org.killbill.billing.client.model.AuditLog;
@@ -42,9 +44,9 @@ import org.killbill.billing.util.audit.ChangeType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.HashMultimap;
 
-import static org.testng.Assert.assertEquals;
+import static org.killbill.billing.jaxrs.resources.JaxrsResource.QUERY_PARALLEL;
 
 public class TestAccountTimeline extends TestJaxrsBase {
 
@@ -57,7 +59,7 @@ public class TestAccountTimeline extends TestJaxrsBase {
 
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
-        final AccountTimeline timeline = killBillClient.getAccountTimeline(accountJson.getAccountId());
+        final AccountTimeline timeline = getAccountTimeline(accountJson.getAccountId(), AuditLevel.NONE);
         Assert.assertEquals(timeline.getPayments().size(), 1);
         Assert.assertEquals(timeline.getInvoices().size(), 2);
         Assert.assertEquals(timeline.getBundles().size(), 1);
@@ -83,9 +85,8 @@ public class TestAccountTimeline extends TestJaxrsBase {
         final BigDecimal creditAmount = BigDecimal.ONE;
         final Credit credit = new Credit();
         credit.setAccountId(accountJson.getAccountId());
-        credit.setInvoiceId(invoice.getInvoiceId());
         credit.setCreditAmount(creditAmount);
-        killBillClient.createCredit(credit, createdBy, reason, comment);
+        killBillClient.createCredit(credit, true, createdBy, reason, comment);
 
         // Add refund
         final Payment postedPayment = killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0);
@@ -118,7 +119,7 @@ public class TestAccountTimeline extends TestJaxrsBase {
     private void verifyPayments(final UUID accountId, final DateTime startTime, final DateTime endTime,
                                 final BigDecimal refundAmount, final BigDecimal chargebackAmount) throws Exception {
         for (final AuditLevel auditLevel : AuditLevel.values()) {
-            final AccountTimeline timeline = killBillClient.getAccountTimeline(accountId, auditLevel);
+            final AccountTimeline timeline = getAccountTimeline(accountId, auditLevel);
 
             Assert.assertEquals(timeline.getPayments().size(), 1);
             final InvoicePayment payment = timeline.getPayments().get(0);
@@ -188,29 +189,33 @@ public class TestAccountTimeline extends TestJaxrsBase {
 
     private void verifyInvoices(final UUID accountId, final DateTime startTime, final DateTime endTime) throws Exception {
         for (final AuditLevel auditLevel : AuditLevel.values()) {
-            final AccountTimeline timeline = killBillClient.getAccountTimeline(accountId, auditLevel);
+            final AccountTimeline timeline = getAccountTimeline(accountId, auditLevel);
 
             // Verify invoices
-            Assert.assertEquals(timeline.getInvoices().size(), 2);
+            Assert.assertEquals(timeline.getInvoices().size(), 3);
 
             // Verify audits
             final List<AuditLog> firstInvoiceAuditLogs = timeline.getInvoices().get(0).getAuditLogs();
             final List<AuditLog> secondInvoiceAuditLogs = timeline.getInvoices().get(1).getAuditLogs();
+            final List<AuditLog> thirdInvoiceAuditLogs = timeline.getInvoices().get(2).getAuditLogs();
             if (AuditLevel.NONE.equals(auditLevel)) {
                 Assert.assertEquals(firstInvoiceAuditLogs.size(), 0);
                 Assert.assertEquals(secondInvoiceAuditLogs.size(), 0);
+                Assert.assertEquals(thirdInvoiceAuditLogs.size(), 0);
             } else {
                 Assert.assertEquals(firstInvoiceAuditLogs.size(), 1);
                 verifyAuditLog(firstInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
                 Assert.assertEquals(secondInvoiceAuditLogs.size(), 1);
                 verifyAuditLog(secondInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
+                Assert.assertEquals(thirdInvoiceAuditLogs.size(), 1);
+                verifyAuditLog(thirdInvoiceAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
             }
         }
     }
 
     private void verifyCredits(final UUID accountId, final DateTime startTime, final DateTime endTime, final BigDecimal creditAmount) throws Exception {
         for (final AuditLevel auditLevel : AuditLevel.values()) {
-            final AccountTimeline timeline = killBillClient.getAccountTimeline(accountId, auditLevel);
+            final AccountTimeline timeline = getAccountTimeline(accountId, auditLevel);
 
             // Verify credits
             final List<Credit> credits = timeline.getInvoices().get(1).getCredits();
@@ -230,7 +235,7 @@ public class TestAccountTimeline extends TestJaxrsBase {
 
     private void verifyBundles(final UUID accountId, final DateTime startTime, final DateTime endTime) throws Exception {
         for (final AuditLevel auditLevel : AuditLevel.values()) {
-            final AccountTimeline timeline = killBillClient.getAccountTimeline(accountId, auditLevel);
+            final AccountTimeline timeline = getAccountTimeline(accountId, auditLevel);
 
             // Verify bundles
             Assert.assertEquals(timeline.getBundles().size(), 1);
@@ -299,4 +304,17 @@ public class TestAccountTimeline extends TestJaxrsBase {
         Assert.assertEquals(auditLogJson.getComments(), comments);
         Assert.assertEquals(auditLogJson.getChangedBy(), changedBy);
     }
+
+    private AccountTimeline getAccountTimeline(final UUID accountId, final AuditLevel auditLevel) throws KillBillClientException {
+        final AccountTimeline accountTimeline = killBillClient.getAccountTimeline(accountId, auditLevel, RequestOptions.empty());
+
+        // Verify also the parallel path
+        final HashMultimap<String, String> queryParams = HashMultimap.<String, String>create();
+        queryParams.put(QUERY_PARALLEL, "true");
+        final RequestOptions requestOptions = RequestOptions.builder().withQueryParams(queryParams).build();
+        final AccountTimeline accountTimelineInParallel = killBillClient.getAccountTimeline(accountId, auditLevel, requestOptions);
+        Assert.assertEquals(accountTimelineInParallel, accountTimeline);
+
+        return accountTimeline;
+    }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBuildResponse.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBuildResponse.java
index 123265d..1b54361 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBuildResponse.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBuildResponse.java
@@ -19,7 +19,7 @@ package org.killbill.billing.jaxrs;
 import org.killbill.billing.jaxrs.resources.AccountResource;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.server.log.ServerTestSuiteNoDB;
-import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.billing.util.config.definition.JaxrsConfig;
 import org.testng.annotations.Test;
 
 import javax.ws.rs.core.Response;
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
index fbc8a31..4c158af 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
@@ -25,19 +25,26 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.BlockingState;
+import org.killbill.billing.client.model.BlockingStates;
 import org.killbill.billing.client.model.Bundle;
 import org.killbill.billing.client.model.Bundles;
 import org.killbill.billing.client.model.Subscription;
 import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
 
 public class TestBundle extends TestJaxrsBase {
 
@@ -74,9 +81,17 @@ public class TestBundle extends TestJaxrsBase {
     public void testBundleNonExistent() throws Exception {
         final Account accountJson = createAccount();
 
-        Assert.assertNull(killBillClient.getBundle(UUID.randomUUID()));
-        Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), "98374982743892").isEmpty());
-        Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId()).isEmpty());
+        // ID
+        Assert.assertNull(killBillClient.getBundle(UUID.randomUUID(), requestOptions));
+
+        // External Key
+        Assert.assertNull(killBillClient.getBundle(UUID.randomUUID().toString(), requestOptions));
+        Assert.assertTrue(killBillClient.getAllBundlesForExternalKey(UUID.randomUUID().toString(), requestOptions).isEmpty());
+
+        // Account Id
+        Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), "98374982743892", requestOptions).isEmpty());
+        Assert.assertTrue(killBillClient.getAccountBundles(accountJson.getAccountId(), requestOptions).isEmpty());
+
     }
 
     @Test(groups = "slow", description = "Can handle non existent account")
@@ -98,7 +113,7 @@ public class TestBundle extends TestJaxrsBase {
         final Subscription entitlementJsonNoEvents = createEntitlement(accountJson.getAccountId(), bundleExternalKey, productName,
                                                                        ProductCategory.BASE, term, true);
 
-        final Bundle originalBundle = killBillClient.getBundle(bundleExternalKey);
+        final Bundle originalBundle = killBillClient.getBundle(bundleExternalKey, requestOptions);
         assertEquals(originalBundle.getAccountId(), accountJson.getAccountId());
         assertEquals(originalBundle.getExternalKey(), bundleExternalKey);
 
@@ -109,12 +124,29 @@ public class TestBundle extends TestJaxrsBase {
         bundle.setBundleId(entitlementJsonNoEvents.getBundleId());
         assertEquals(killBillClient.transferBundle(bundle, createdBy, reason, comment).getAccountId(), newAccount.getAccountId());
 
-        final Bundle newBundle = killBillClient.getBundle(bundleExternalKey);
+        final Bundle newBundle = killBillClient.getBundle(bundleExternalKey, requestOptions);
         assertNotEquals(newBundle.getBundleId(), originalBundle.getBundleId());
         assertEquals(newBundle.getExternalKey(), originalBundle.getExternalKey());
         assertEquals(newBundle.getAccountId(), newAccount.getAccountId());
+
+
+        final Bundles bundles = killBillClient.getAllBundlesForExternalKey(bundleExternalKey, requestOptions);
+        assertEquals(bundles.size(), 2);
+        assertSubscriptionState(bundles, originalBundle.getBundleId(), EntitlementState.CANCELLED);
+        assertSubscriptionState(bundles, newBundle.getBundleId(), EntitlementState.ACTIVE);
     }
 
+    private void assertSubscriptionState(final Bundles bundles, final UUID bundleId, final EntitlementState expectedState) {
+        final Bundle bundle = Iterables.tryFind(bundles, new Predicate<Bundle>() {
+            @Override
+            public boolean apply(final Bundle input) {
+                return input.getBundleId().equals(bundleId);
+            }
+        }).orNull();
+
+        assertNotNull(bundle);
+        assertEquals(bundle.getSubscriptions().get(0).getState(), expectedState);
+    }
 
     @Test(groups = "slow", description = "Block a bundle")
     public void testBlockBundle() throws Exception {
@@ -128,28 +160,35 @@ public class TestBundle extends TestJaxrsBase {
         final String bundleExternalKey = "93199";
 
         final Subscription entitlement = createEntitlement(accountJson.getAccountId(), bundleExternalKey, productName,
-                                                                       ProductCategory.BASE, term, true);
+                                                           ProductCategory.BASE, term, true);
 
         final Bundle bundle = killBillClient.getBundle(bundleExternalKey);
         assertEquals(bundle.getAccountId(), accountJson.getAccountId());
         assertEquals(bundle.getExternalKey(), bundleExternalKey);
 
-        final BlockingState blockingState = new BlockingState(bundle.getBundleId(), "block", "service", false, true, true, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), BlockingStateType.SUBSCRIPTION_BUNDLE, null);
-        killBillClient.setBlockingState(bundle.getBundleId(), blockingState, createdBy, reason, comment);
+        final BlockingState blockingState = new BlockingState(bundle.getBundleId(), "block", "service", false, true, true, null, BlockingStateType.SUBSCRIPTION_BUNDLE, null);
+        killBillClient.setBlockingState(bundle.getBundleId(), blockingState, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), ImmutableMap.<String, String>of(), createdBy, reason, comment);
 
         final Subscription subscription = killBillClient.getSubscription(entitlement.getSubscriptionId());
         assertEquals(subscription.getState(), EntitlementState.BLOCKED);
 
         clock.addDays(1);
 
-        final BlockingState unblockingState = new BlockingState(bundle.getBundleId(), "unblock", "service", false, false, false, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), BlockingStateType.SUBSCRIPTION_BUNDLE, null);
-        killBillClient.setBlockingState(bundle.getBundleId(), unblockingState, createdBy, reason, comment);
+        final BlockingState unblockingState = new BlockingState(bundle.getBundleId(), "unblock", "service", false, false, false, null, BlockingStateType.SUBSCRIPTION_BUNDLE, null);
+        killBillClient.setBlockingState(bundle.getBundleId(), unblockingState, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), ImmutableMap.<String, String>of(), createdBy, reason, comment);
 
         final Subscription subscription2 = killBillClient.getSubscription(entitlement.getSubscriptionId());
         assertEquals(subscription2.getState(), EntitlementState.ACTIVE);
-    }
 
+        final BlockingStates blockingStates = killBillClient.getBlockingStates(accountJson.getAccountId(), null, ImmutableList.<String>of("service"), AuditLevel.FULL, basicRequestOptions());
+        Assert.assertEquals(blockingStates.size(), 2);
+
+        final BlockingStates blockingStates2 = killBillClient.getBlockingStates(accountJson.getAccountId(), ImmutableList.<BlockingStateType>of(BlockingStateType.SUBSCRIPTION_BUNDLE), null, AuditLevel.FULL, basicRequestOptions());
+        Assert.assertEquals(blockingStates2.size(), 2);
 
+        final BlockingStates blockingStates3 = killBillClient.getBlockingStates(accountJson.getAccountId(), null, null, AuditLevel.FULL, basicRequestOptions());
+        Assert.assertEquals(blockingStates3.size(), 3);
+    }
 
     @Test(groups = "slow", description = "Can paginate and search through all bundles")
     public void testBundlesPagination() throws Exception {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCache.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCache.java
new file mode 100644
index 0000000..23325ad
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCache.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.RequestOptions;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.PaymentMethod;
+import org.killbill.billing.client.model.PaymentMethodPluginDetail;
+import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.Tenant;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.io.Resources;
+import net.sf.ehcache.Ehcache;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestCache extends TestJaxrsBase {
+
+    @Test(groups = "slow", description = "Can Invalidate (clear) a Cache by name")
+    public void testInvalidateCacheByName() throws Exception {
+        // get Ehcache item with name "record-id"
+        final Ehcache cache = cacheManager.getEhcache(CacheType.RECORD_ID.getCacheName());
+        // verify that it is not null and has one stored key (the default tenant created for all integration tests)
+        assertNotNull(cache);
+        Assert.assertEquals(cache.getSize(), 1);
+
+        // invalidate the specified cache
+        killBillClient.invalidateCacheByName(cache.getName(), requestOptions);
+
+        // verify that now the cache is empty and has no keys stored
+        Assert.assertEquals(cache.getSize(), 0);
+    }
+
+    @Test(groups = "slow", description = "Can Invalidate (clear) all available Caches")
+    public void testInvalidateAllCaches() throws Exception {
+        // get Ehcache item with name "record-id"
+        final Ehcache cache = cacheManager.getEhcache(CacheType.RECORD_ID.getCacheName());
+        // verify that it is not null and has one stored key (the default tenant created for all integration tests)
+        assertNotNull(cache);
+        Assert.assertEquals(cache.getSize(), 1);
+
+        // invalidate all caches
+        killBillClient.invalidateAllCaches(requestOptions);
+
+        // verify that now the cache is empty and has no keys stored
+        Assert.assertEquals(cache.getSize(), 0);
+    }
+
+    @Test(groups = "slow", description = "Can Invalidate (clear) all Account Caches by accountId")
+    public void testInvalidateCacheByAccount() throws Exception {
+        final Account input = createAccountNoPMBundleAndSubscription();
+
+        // get all caches per account level
+        final Ehcache accountRecordIdCache = cacheManager.getEhcache(CacheType.ACCOUNT_RECORD_ID.getCacheName());
+        final Ehcache accountImmutableCache = cacheManager.getEhcache(CacheType.ACCOUNT_IMMUTABLE.getCacheName());
+        final Ehcache accountBcdCache = cacheManager.getEhcache(CacheType.ACCOUNT_BCD.getCacheName());
+
+        // verify that they are not null and have the accountId stored as a key (the account created before)
+        assertNotNull(accountRecordIdCache);
+        assertNotNull(accountRecordIdCache.get(input.getAccountId().toString()));
+        assertNotNull(accountImmutableCache);
+        assertNotNull(accountImmutableCache.get(input.getAccountId()));
+        assertNotNull(accountBcdCache);
+        assertNotNull(accountBcdCache.get(input.getAccountId()));
+
+        // invalidate caches per account level by accountId
+        killBillClient.invalidateCacheByAccount(input.getAccountId().toString(), requestOptions);
+
+        // verify that now the caches don't have the accountId key stored
+        Assert.assertNull(accountRecordIdCache.get(input.getAccountId().toString()));
+        Assert.assertNull(accountImmutableCache.get(input.getAccountId()));
+        Assert.assertNull(accountBcdCache.get(input.getAccountId()));
+    }
+
+    @Test(groups = "slow", description = "Can Invalidate (clear) all Tenant Caches for current Tenant")
+    public void testInvalidateCacheByTenant() throws Exception {
+        // creating a new Tenant for this test
+        final String testApiKey = "testApiKey";
+        final String testApiSecret = "testApiSecret";
+        final Tenant tenant = new Tenant();
+        tenant.setApiKey(testApiKey);
+        tenant.setApiSecret(testApiSecret);
+        loginTenant(testApiKey, testApiSecret);
+        Tenant currentTenant = killBillClient.createTenant(tenant, false, requestOptions);
+
+        // using custom RequestOptions with the new Tenant created before
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment)
+                                                    .withTenantApiKey(currentTenant.getApiKey())
+                                                    .withTenantApiSecret(currentTenant.getApiSecret())
+                                                    .build();
+
+        // Uploading the test catalog using the new Tenant created before
+        killBillClient.uploadXMLCatalog(Resources.getResource("SpyCarAdvanced.xml").getPath(), inputOptions);
+
+        // creating an Account with PaymentMethod and a Subscription
+        createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoiceWithInputOptions(inputOptions);
+
+        // get all caches per tenant level
+        final Ehcache tenantRecordIdCache = cacheManager.getEhcache(CacheType.TENANT_RECORD_ID.getCacheName());
+        final Ehcache tenantPaymentStateMachineConfigCache = cacheManager.getEhcache(CacheType.TENANT_PAYMENT_STATE_MACHINE_CONFIG.getCacheName());
+        final Ehcache tenantCache = cacheManager.getEhcache(CacheType.TENANT.getCacheName());
+        final Ehcache tenantKvCache = cacheManager.getEhcache(CacheType.TENANT_KV.getCacheName());
+        final Ehcache tenantConfigCache = cacheManager.getEhcache(CacheType.TENANT_CONFIG.getCacheName());
+        final Ehcache tenantOverdueConfigCache = cacheManager.getEhcache(CacheType.TENANT_OVERDUE_CONFIG.getCacheName());
+        final Ehcache tenantCatalogCache = cacheManager.getEhcache(CacheType.TENANT_CATALOG.getCacheName());
+
+        // getting current Tenant's record Id from the specific Cache
+        Long tenantRecordId = (Long) tenantRecordIdCache.get(currentTenant.getTenantId().toString()).getObjectValue();
+
+        // verify that they are not null and have the expected tenant information
+        assertNotNull(tenantRecordIdCache);
+        assertNotNull(tenantRecordIdCache.get(currentTenant.getTenantId().toString()));
+        assertNotNull(tenantPaymentStateMachineConfigCache);
+        assertTrue(hasKeysByTenantRecordId(tenantPaymentStateMachineConfigCache, tenantRecordId.toString()));
+        assertNotNull(tenantCache);
+        assertNotNull(tenantCache.get(testApiKey));
+        assertNotNull(tenantKvCache);
+        assertTrue(hasKeysByTenantRecordId(tenantKvCache, tenantRecordId.toString()));
+        assertNotNull(tenantConfigCache);
+        assertNotNull(tenantConfigCache.get(tenantRecordId));
+        assertNotNull(tenantOverdueConfigCache);
+        assertNotNull(tenantOverdueConfigCache.get(tenantRecordId));
+        assertNotNull(tenantCatalogCache);
+        assertNotNull(tenantCatalogCache.get(tenantRecordId));
+
+        // invalidate caches per tenant level
+        killBillClient.invalidateCacheByTenant(inputOptions);
+
+        // verify that now the caches don't have the previous values
+        Assert.assertNull(tenantRecordIdCache.get(currentTenant.getTenantId().toString()));
+        assertFalse(hasKeysByTenantRecordId(tenantPaymentStateMachineConfigCache, tenantRecordId.toString()));
+        Assert.assertNull(tenantCache.get(testApiKey));
+        assertFalse(hasKeysByTenantRecordId(tenantKvCache, tenantRecordId.toString()));
+        Assert.assertNull(tenantConfigCache.get(tenantRecordId));
+        Assert.assertNull(tenantOverdueConfigCache.get(tenantRecordId));
+        Assert.assertNull(tenantCatalogCache.get(tenantRecordId));
+    }
+
+    private boolean hasKeysByTenantRecordId(final Ehcache tenantCache, final String tenantRecordId) {
+        for (String key : (List<String>) tenantCache.getKeys()) {
+            if (key.endsWith("::" + tenantRecordId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoiceWithInputOptions(final RequestOptions inputOptions) throws Exception {
+        Account account = killBillClient.createAccount(getAccount(), inputOptions);
+
+        final PaymentMethodPluginDetail info = new PaymentMethodPluginDetail();
+        info.setProperties(null);
+        final PaymentMethod paymentMethodJson = new PaymentMethod(null, UUID.randomUUID().toString(), account.getAccountId(), true, PLUGIN_NAME, info);
+        killBillClient.createPaymentMethod(paymentMethodJson, inputOptions);
+
+        final Subscription subscription = new Subscription();
+        subscription.setAccountId(account.getAccountId());
+        subscription.setExternalKey(UUID.randomUUID().toString());
+        subscription.setProductName("Sports");
+        subscription.setProductCategory(ProductCategory.BASE);
+        subscription.setBillingPeriod(BillingPeriod.MONTHLY);
+        subscription.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        clock.resetDeltaFromReality();
+        clock.setDay(new LocalDate(2013, 3, 1));
+        final Subscription subscriptionJson = killBillClient.createSubscription(subscription, clock.getUTCToday(), DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, inputOptions);
+
+        assertNotNull(subscriptionJson);
+        clock.addDays(32);
+        crappyWaitForLackOfProperSynchonization();
+    }
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index 7a24e1c..84f3d50 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -18,46 +18,63 @@
 
 package org.killbill.billing.jaxrs;
 
+import java.math.BigDecimal;
 import java.sql.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Catalog;
 import org.killbill.billing.client.model.Plan;
 import org.killbill.billing.client.model.PlanDetail;
 import org.killbill.billing.client.model.Product;
+import org.killbill.billing.client.model.SimplePlan;
+import org.killbill.billing.client.model.Tenant;
+import org.killbill.billing.client.model.Usage;
+import org.killbill.xmlloader.XMLLoader;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.Resources;
 
 public class TestCatalog extends TestJaxrsBase {
 
     @Test(groups = "slow", description = "Upload and retrieve a per tenant catalog")
     public void testMultiTenantCatalog() throws Exception {
-        final String catalogPath = Resources.getResource("SpyCarBasic.xml").getPath();
-        killBillClient.uploadXMLCatalog(catalogPath, createdBy, reason, comment);
-
-        final String catalog = killBillClient.getXMLCatalog();
+        final String versionPath1 = Resources.getResource("SpyCarBasic.xml").getPath();
+        killBillClient.uploadXMLCatalog(versionPath1, createdBy, reason, comment);
+        String catalog = killBillClient.getXMLCatalog();
         Assert.assertNotNull(catalog);
+        //
+        // We can't deserialize the VersionedCatalog using our JAXB models because it contains several
+        // Standalone catalog and ids (JAXB name) are not unique across the various catalogs so deserialization would fail
+        //
     }
 
     @Test(groups = "slow", description = "Can retrieve a json version of the catalog")
     public void testCatalog() throws Exception {
         final Set<String> allBasePlans = new HashSet<String>();
 
-        final Catalog catalogJson = killBillClient.getJSONCatalog();
+        final List<Catalog> catalogsJson = killBillClient.getJSONCatalog();
 
-        Assert.assertEquals(catalogJson.getName(), "Firearms");
-        Assert.assertEquals(catalogJson.getEffectiveDate(), Date.valueOf("2011-01-01"));
-        Assert.assertEquals(catalogJson.getCurrencies().size(), 3);
-        Assert.assertEquals(catalogJson.getProducts().size(), 11);
-        Assert.assertEquals(catalogJson.getPriceLists().size(), 4);
+        Assert.assertEquals(catalogsJson.get(0).getName(), "Firearms");
+        Assert.assertEquals(catalogsJson.get(0).getEffectiveDate(), Date.valueOf("2011-01-01"));
+        Assert.assertEquals(catalogsJson.get(0).getCurrencies().size(), 3);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().size(), 11);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().size(), 4);
 
-        for (final Product productJson : catalogJson.getProducts()) {
+        for (final Product productJson : catalogsJson.get(0).getProducts()) {
             if (!"BASE".equals(productJson.getType())) {
                 Assert.assertEquals(productJson.getIncluded().size(), 0);
                 Assert.assertEquals(productJson.getAvailable().size(), 0);
@@ -69,6 +86,18 @@ public class TestCatalog extends TestJaxrsBase {
                 allBasePlans.add(planJson.getName());
             }
 
+            // Verify Usage info in json
+            if (productJson.getName().equals("Bullets")) {
+                Assert.assertEquals(productJson.getPlans().get(0).getName(), "bullets-usage-in-arrear");
+                Assert.assertEquals(productJson.getPlans().get(0).getPhases().get(0).getType(), "EVERGREEN");
+                List<Usage> usages = productJson.getPlans().get(0).getPhases().get(0).getUsages();
+                Assert.assertEquals(usages.get(0).getBillingPeriod(), "MONTHLY");
+                Assert.assertEquals(usages.get(0).getTiers().get(0).getBlocks().get(0).getUnit(), "bullets");
+                Assert.assertEquals(usages.get(0).getTiers().get(0).getBlocks().get(0).getSize(), "100.0");
+                Assert.assertEquals(usages.get(0).getTiers().get(0).getBlocks().get(0).getPrices().get(0).getCurrency(), "USD");
+                Assert.assertEquals(usages.get(0).getTiers().get(0).getBlocks().get(0).getPrices().get(0).getValue(), 2.95);
+            }
+
             // Retrieve available products (addons) for that base product
             final List<PlanDetail> availableAddons = killBillClient.getAvailableAddons(productJson.getName());
             final Set<String> availableAddonsNames = new HashSet<String>();
@@ -91,8 +120,73 @@ public class TestCatalog extends TestJaxrsBase {
             expectedExceptions = KillBillClientException.class,
             expectedExceptionsMessageRegExp = "There is no catalog version that applies for the given date.*")
     public void testCatalogInvalidDate() throws Exception {
-        final Catalog catalogJson = killBillClient.getJSONCatalog(DateTime.parse("2008-01-01"));
+        final List<Catalog> catalogsJson = killBillClient.getJSONCatalog(DateTime.parse("2008-01-01"));
         Assert.fail();
     }
 
+    @Test(groups = "slow", description = "Can create a simple Plan into a per-tenant catalog")
+    public void testAddSimplePlan() throws Exception {
+
+        killBillClient.addSimplePan(new SimplePlan("foo-monthly", "Foo", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()), requestOptions);
+        List<Catalog> catalogsJson = killBillClient.getJSONCatalog(requestOptions);
+        Assert.assertEquals(catalogsJson.size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().size(), 1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().get(0), "foo-monthly");
+
+
+        killBillClient.addSimplePan(new SimplePlan("foo-annual", "Foo", ProductCategory.BASE, Currency.USD, new BigDecimal("100.00"), BillingPeriod.ANNUAL, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()), requestOptions);
+
+        catalogsJson = killBillClient.getJSONCatalog(requestOptions);
+        Assert.assertEquals(catalogsJson.size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().size(), 2);
+
+    }
+
+    @Test(groups = "slow", description = "Upload and retrieve a per plugin payment state machine config")
+    public void testAddSimplePlanWithoutKBDefault() throws Exception {
+        // Create another tenant initialized with no default catalog,...
+        final Tenant otherTenantNoKBDefault = new Tenant();
+        otherTenantNoKBDefault.setApiKey(UUID.randomUUID().toString());
+        otherTenantNoKBDefault.setApiSecret(UUID.randomUUID().toString());
+
+        killBillClient.createTenant(otherTenantNoKBDefault, false, requestOptions);
+
+        final RequestOptions requestOptionsOtherTenant = requestOptions.extend()
+                                                                       .withTenantApiKey(otherTenantNoKBDefault.getApiKey())
+                                                                       .withTenantApiSecret(otherTenantNoKBDefault.getApiSecret())
+                                                                       .build();
+        // Verify the template catalog is not returned
+        List<Catalog> catalogsJson = killBillClient.getJSONCatalog(requestOptionsOtherTenant);
+        Assert.assertEquals(catalogsJson.size(), 0);
+
+        killBillClient.addSimplePan(new SimplePlan("foo-monthly", "Foo", ProductCategory.BASE, Currency.USD, BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()), requestOptionsOtherTenant);
+        catalogsJson = killBillClient.getJSONCatalog(requestOptionsOtherTenant);
+        Assert.assertEquals(catalogsJson.size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().size(), 1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().get(0), "foo-monthly");
+
+
+        killBillClient.addSimplePan(new SimplePlan("foo-annual", "Foo", ProductCategory.BASE, Currency.USD, new BigDecimal("100.00"), BillingPeriod.ANNUAL, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of()), requestOptionsOtherTenant);
+
+        catalogsJson = killBillClient.getJSONCatalog(requestOptionsOtherTenant);
+        Assert.assertEquals(catalogsJson.size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getProducts().get(0).getName(),"Foo");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().size(),1);
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getName(), "DEFAULT");
+        Assert.assertEquals(catalogsJson.get(0).getPriceLists().get(0).getPlans().size(), 2);
+    }
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
index a8eb3bd..405c415 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestChargeback.java
@@ -94,7 +94,7 @@ public class TestChargeback extends TestJaxrsBase {
         assertEquals(killBillClient.getPaymentMethod(payment.getPaymentMethodId()).getAccountId(), payment.getAccountId());
 
         // Delete the payment method
-        killBillClient.deletePaymentMethod(payment.getPaymentMethodId(), true, createdBy, reason, comment);
+        killBillClient.deletePaymentMethod(payment.getPaymentMethodId(), true, false, createdBy, reason, comment);
 
         // Check the payment method was deleted
         assertNull(killBillClient.getAccount(payment.getAccountId()).getPaymentMethodId());
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
index 48c3748..e323ac2 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
@@ -44,21 +44,40 @@ public class TestCredit extends TestJaxrsBase {
 
     @Test(groups = "slow", description = "Can add a credit to an existing invoice")
     public void testAddCreditToInvoice() throws Exception {
-        final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
+        //final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
 
         final DateTime effectiveDate = clock.getUTCNow();
         final BigDecimal creditAmount = BigDecimal.ONE;
         final Credit credit = new Credit();
         credit.setAccountId(accountJson.getAccountId());
-        credit.setInvoiceId(invoice.getInvoiceId());
         credit.setCreditAmount(creditAmount);
-        final Credit objFromJson = killBillClient.createCredit(credit, createdBy, reason, comment);
+        credit.setDescription("description");
+        Credit objFromJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+
+        final UUID invoiceId = objFromJson.getInvoiceId();
+        credit.setInvoiceId(invoiceId);
+        objFromJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
 
         // We can't just compare the object via .equals() due e.g. to the invoice id
         assertEquals(objFromJson.getAccountId(), accountJson.getAccountId());
-        assertEquals(objFromJson.getInvoiceId(), invoice.getInvoiceId());
+        assertEquals(objFromJson.getInvoiceId(), invoiceId);
         assertEquals(objFromJson.getCreditAmount().compareTo(creditAmount), 0);
         assertEquals(objFromJson.getEffectiveDate().compareTo(effectiveDate.toLocalDate()), 0);
+        assertEquals(objFromJson.getDescription().compareTo("description"), 0);
+    }
+
+    @Test(groups = "slow", description = "Can add a credit to an existing account",
+            expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = ".*it is already in COMMITTED status")
+    public void testAddCreditToCommittedInvoice() throws Exception {
+        final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
+
+        final DateTime effectiveDate = clock.getUTCNow();
+        final BigDecimal creditAmount = BigDecimal.ONE;
+        final Credit credit = new Credit();
+        credit.setAccountId(accountJson.getAccountId());
+        credit.setInvoiceId(invoice.getInvoiceId());
+        credit.setCreditAmount(creditAmount);
+        final Credit objFromJson = killBillClient.createCredit(credit, true, createdBy, reason, comment);
     }
 
     @Test(groups = "slow", description = "Cannot add a credit if the account doesn't exist")
@@ -68,7 +87,7 @@ public class TestCredit extends TestJaxrsBase {
         credit.setCreditAmount(BigDecimal.TEN);
 
         // Try to create the credit
-        assertNull(killBillClient.createCredit(credit, createdBy, reason, comment));
+        assertNull(killBillClient.createCredit(credit, true, createdBy, reason, comment));
     }
 
     @Test(groups = "slow", description = "Cannot credit a badly formatted credit")
@@ -79,7 +98,7 @@ public class TestCredit extends TestJaxrsBase {
 
         // Try to create the credit
         try {
-            killBillClient.createCredit(credit, createdBy, reason, comment);
+            killBillClient.createCredit(credit, true, createdBy, reason, comment);
             fail();
         } catch (final KillBillClientException e) {
         }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
index 0c826dd..a298dc3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestEntitlement.java
@@ -26,22 +26,26 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.DefaultPriceList;
+import org.killbill.billing.catalog.DefaultPriceListSet;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.Bundle;
 import org.killbill.billing.client.model.Invoice;
 import org.killbill.billing.client.model.PhasePriceOverride;
 import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.client.model.Tags;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
 import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.ning.http.client.Response;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
@@ -222,9 +226,9 @@ public class TestEntitlement extends TestJaxrsBase {
         overrides.add(new PhasePriceOverride(null, PhaseType.TRIAL.toString(), BigDecimal.TEN, null));
         input.setPriceOverrides(overrides);
 
-        final Subscription subscription = killBillClient.createSubscription(input, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, createdBy, reason, comment);
+        final Subscription subscription = killBillClient.createSubscription(input, null, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, basicRequestOptions());
 
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
         assertEquals(invoices.size(), 1);
         assertEquals(invoices.get(0).getAmount().compareTo(BigDecimal.TEN), 0);
     }
@@ -264,12 +268,12 @@ public class TestEntitlement extends TestJaxrsBase {
         subscriptions.add(base);
         subscriptions.add(addOn1);
         subscriptions.add(addOn2);
-        final Bundle bundle = killBillClient.createSubscriptionWithAddOns(subscriptions, initialDate, 10, "createdBy", "", "");
+        final Bundle bundle = killBillClient.createSubscriptionWithAddOns(subscriptions, initialDate.toLocalDate(), 10, "createdBy", "", "");
         assertNotNull(bundle);
         assertEquals(bundle.getExternalKey(), "base");
         assertEquals(bundle.getSubscriptions().size(), 3);
 
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
         assertEquals(invoices.size(), 1);
     }
 
@@ -286,7 +290,7 @@ public class TestEntitlement extends TestJaxrsBase {
         input.setProductCategory(ProductCategory.BASE);
         input.setBillingPeriod(BillingPeriod.MONTHLY);
         input.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
-        final Subscription entitlementJson = killBillClient.createSubscription(input, initialDate.plusMonths(1), -1, createdBy, reason, comment);
+        final Subscription entitlementJson = killBillClient.createSubscription(input, initialDate.toLocalDate().plusMonths(1), -1, createdBy, reason, comment);
 
         Assert.assertEquals(entitlementJson.getProductName(), input.getProductName());
         Assert.assertEquals(entitlementJson.getProductCategory(), input.getProductCategory());
@@ -297,4 +301,103 @@ public class TestEntitlement extends TestJaxrsBase {
         final Subscription objFromJson = killBillClient.getSubscription(entitlementJson.getSubscriptionId());
         Assert.assertTrue(objFromJson.equals(entitlementJson));
     }
+
+    @Test(groups = "slow", description = "Can create an entitlement with an account with autoPayOff")
+    public void testCreateEntitlementWithAutoPayOff() throws Exception {
+        final Account accountJson = createAccount();
+        assertNotNull(accountJson);
+
+        // assign autoPaymentOff tag to account
+        Tags tags = killBillClient.createAccountTag(accountJson.getAccountId(), new UUID(0L, 1L), requestOptions);
+        assertEquals(tags.get(0).getTagDefinitionName(), "AUTO_PAY_OFF");
+
+        // verify that number of invoices and payments for account is still 0
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions).size(), 0);
+        assertEquals(killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions).size(), 0);
+
+        // create a subscription with no trial plan
+        final Subscription input = new Subscription();
+        input.setAccountId(accountJson.getAccountId());
+        input.setProductName("Blowdart");
+        input.setProductCategory(ProductCategory.BASE);
+        input.setBillingPeriod(BillingPeriod.MONTHLY);
+        input.setPriceList("notrial");
+        final Subscription subscriptionJson = killBillClient.createSubscription(input, null, 10, requestOptions);
+        assertNotNull(subscriptionJson);
+
+        // verify that number of invoices is now 1
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions).size(), 1);
+
+        // verify that number of payments is still 0 (no attempts)
+        assertEquals(killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions).size(), 0);
+    }
+
+    @Test(groups = "slow", description = "Verify we can move the BCD associated with the subscription")
+    public void testMoveEntitlementBCD() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final Subscription entitlementJson = createEntitlement(accountJson.getAccountId(), "99999", productName,
+                                                               ProductCategory.BASE, term, true);
+
+        Assert.assertEquals(entitlementJson.getBillCycleDayLocal(), new Integer(25));
+
+        final Subscription updatedSubscription = new Subscription();
+        updatedSubscription.setSubscriptionId(entitlementJson.getSubscriptionId());
+        updatedSubscription.setBillCycleDayLocal(9);
+        killBillClient.updateSubscriptionBCD(updatedSubscription, null, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, basicRequestOptions());
+
+
+        final Subscription result = killBillClient.getSubscription(entitlementJson.getSubscriptionId());
+        // Still shows as the 4 (BCD did not take effect)
+        Assert.assertEquals(result.getBillCycleDayLocal(), new Integer(25));
+
+        // 2012, 5, 9
+        clock.addDays(14);
+        crappyWaitForLackOfProperSynchonization();
+
+        final Subscription result2 = killBillClient.getSubscription(entitlementJson.getSubscriptionId());
+        // Still shows as the 4 (BCD did not take effect)
+        Assert.assertEquals(result2.getBillCycleDayLocal(), new Integer(9));
+    }
+
+
+
+    @Test(groups = "slow", description = "Can create subscription and change plan using planName")
+    public void testEntitlementUsingPlanName() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final Subscription input = new Subscription();
+        input.setAccountId(accountJson.getAccountId());
+        input.setExternalKey("somethingSpecial");
+        input.setPlanName("shotgun-monthly");
+
+        final Subscription entitlementJson = killBillClient.createSubscription(input, null, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, basicRequestOptions());
+        Assert.assertEquals(entitlementJson.getProductName(), "Shotgun");
+        Assert.assertEquals(entitlementJson.getBillingPeriod(), BillingPeriod.MONTHLY);
+        Assert.assertEquals(entitlementJson.getPriceList(), DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        Assert.assertEquals(entitlementJson.getPlanName(), "shotgun-monthly");
+
+
+        final Subscription newInput = new Subscription();
+        newInput.setAccountId(entitlementJson.getAccountId());
+        newInput.setSubscriptionId(entitlementJson.getSubscriptionId());
+        newInput.setPlanName("pistol-monthly");
+        final Subscription newEntitlementJson = killBillClient.updateSubscription(newInput, null, null, DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC, basicRequestOptions());
+        Assert.assertEquals(newEntitlementJson.getProductName(), "Pistol");
+        Assert.assertEquals(newEntitlementJson.getBillingPeriod(), BillingPeriod.MONTHLY);
+        Assert.assertEquals(newEntitlementJson.getPriceList(), DefaultPriceListSet.DEFAULT_PRICELIST_NAME);
+        Assert.assertEquals(newEntitlementJson.getPlanName(), "pistol-monthly");
+
+    }
+
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java
new file mode 100644
index 0000000..80575fd
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestExternalRefund.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Invoice;
+import org.killbill.billing.client.model.InvoiceItem;
+import org.killbill.billing.client.model.InvoicePayment;
+import org.killbill.billing.client.model.InvoicePaymentTransaction;
+import org.killbill.billing.client.model.InvoicePayments;
+import org.killbill.billing.client.model.Invoices;
+import org.killbill.billing.client.model.Payment;
+import org.killbill.billing.client.model.PaymentMethod;
+import org.killbill.billing.client.model.PaymentMethodPluginDetail;
+import org.killbill.billing.client.model.Payments;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.payment.api.TransactionType;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestExternalRefund extends TestJaxrsBase {
+
+    @Test(groups = "slow", description = "#255 - Scenario 0 - Can refund an automatic payment. This is a test to validate the correct behaviour.")
+    public void testAutomaticPaymentAndRefund() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // regular refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 0 - Can refund an automatic payment over item adjustments. This is a test to validate the correct behaviour.")
+    public void testAutomaticPaymentAndRefundWithAdjustments() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // regular refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(itemsToBeAdjusted);
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 1 - Can refund a manual payment though an external refund")
+    public void testManualPaymentAndExternalRefund() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithExternalPMBundleAndSubscriptionAndManualPayTagAndWaitForFirstInvoice();
+
+        final Invoices invoicesForAccount = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
+        final Invoice unpaidInvoice = invoicesForAccount.get(1);
+        assertEquals(unpaidInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        assertEquals(paymentsForAccount.size(), 0);
+
+        final InvoicePayment invoicePaymentRequest = new InvoicePayment();
+        invoicePaymentRequest.setTargetInvoiceId(unpaidInvoice.getInvoiceId());
+        invoicePaymentRequest.setAccountId(accountJson.getAccountId());
+        invoicePaymentRequest.setCurrency(unpaidInvoice.getCurrency().toString());
+        invoicePaymentRequest.setPurchasedAmount(unpaidInvoice.getAmount());
+        final InvoicePayment invoicePayment = killBillClient.createInvoicePayment(invoicePaymentRequest, true, requestOptions);
+        assertEquals(invoicePayment.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePayment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setPaymentId(invoicePayment.getPaymentId());
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 1 - Can refund a manual payment though an external refund over item adjustments")
+    public void testManualPaymentAndExternalRefundWithAdjustments() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithExternalPMBundleAndSubscriptionAndManualPayTagAndWaitForFirstInvoice();
+
+        final Invoices invoicesForAccount = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
+        final Invoice unpaidInvoice = invoicesForAccount.get(1);
+        assertEquals(unpaidInvoice.getBalance().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        assertEquals(paymentsForAccount.size(), 0);
+
+        final InvoicePayment invoicePaymentRequest = new InvoicePayment();
+        invoicePaymentRequest.setTargetInvoiceId(unpaidInvoice.getInvoiceId());
+        invoicePaymentRequest.setAccountId(accountJson.getAccountId());
+        invoicePaymentRequest.setCurrency(unpaidInvoice.getCurrency().toString());
+        invoicePaymentRequest.setPurchasedAmount(unpaidInvoice.getAmount());
+        final InvoicePayment invoicePayment = killBillClient.createInvoicePayment(invoicePaymentRequest, true, requestOptions);
+        assertEquals(invoicePayment.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePayment.getRefundedAmount().compareTo(BigDecimal.ZERO), 0);
+
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setPaymentId(invoicePayment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(unpaidInvoice.getItems());
+        final InvoicePayment invoicePaymentRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, requestOptions);
+        assertNotNull(invoicePaymentRefund);
+
+        assertSingleInvoicePaymentRefund(invoicePaymentRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 2a - Can refund an automatic payment though an external refund")
+    public void testAutomaticPaymentAndExternalRefund() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        // delete PM
+        killBillClient.deletePaymentMethod(accountJson.getPaymentMethodId(), true, true, requestOptions);
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        // external refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        final InvoicePayment invoicePaymentExternalRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, null, requestOptions);
+        assertNotNull(invoicePaymentExternalRefund);
+
+        assertInvoicePaymentsExternalRefund(accountJson.getAccountId(), invoicePaymentExternalRefund);
+        assertRefundInvoiceNoAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.valueOf(249.95), BigDecimal.ZERO);
+
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 2a - Can refund an automatic payment though an external refund over item adjustments")
+    public void testAutomaticPaymentAndExternalRefundWithAdjustments() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        // delete PM
+        killBillClient.deletePaymentMethod(accountJson.getPaymentMethodId(), true, true, requestOptions);
+
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        final Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // external refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(itemsToBeAdjusted);
+        final InvoicePayment invoicePaymentExternalRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, null, requestOptions);
+        assertNotNull(invoicePaymentExternalRefund);
+
+        assertInvoicePaymentsExternalRefund(accountJson.getAccountId(), invoicePaymentExternalRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
+
+    }
+
+    @Test(groups = "slow", description = "#255 - Scenario 2b - Can refund an automatic payment though another existing payment method")
+    public void testAutomaticPaymentAndExternalRefundWithDifferentPM() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        // delete PM
+        killBillClient.deletePaymentMethod(accountJson.getPaymentMethodId(), true, true, requestOptions);
+
+        // create another PM
+        final PaymentMethodPluginDetail info = new PaymentMethodPluginDetail();
+        final PaymentMethod paymentMethodJson = new PaymentMethod(null, UUID.randomUUID().toString(), accountJson.getAccountId(), false, PLUGIN_NAME, info);
+        final PaymentMethod otherPaymentMethod = killBillClient.createPaymentMethod(paymentMethodJson, requestOptions);
+
+        final Payments paymentsForAccount = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), requestOptions);
+        final Payment payment = paymentsForAccount.get(paymentsForAccount.size() - 1);
+
+        final Invoices invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true, requestOptions);
+        final List<InvoiceItem> itemsToBeAdjusted = invoices.get(1).getItems();
+
+        // external refund
+        final InvoicePaymentTransaction invoicePaymentTransactionRequest = new InvoicePaymentTransaction();
+        invoicePaymentTransactionRequest.setAmount(BigDecimal.valueOf(249.95));
+        invoicePaymentTransactionRequest.setCurrency(accountJson.getCurrency().toString());
+        invoicePaymentTransactionRequest.setPaymentId(payment.getPaymentId());
+        invoicePaymentTransactionRequest.setIsAdjusted(true);
+        invoicePaymentTransactionRequest.setAdjustments(itemsToBeAdjusted);
+        final InvoicePayment invoicePaymentExternalRefund = killBillClient.createInvoicePaymentRefund(invoicePaymentTransactionRequest, true, otherPaymentMethod.getPaymentMethodId(), requestOptions);
+        assertNotNull(invoicePaymentExternalRefund);
+        assertEquals(invoicePaymentExternalRefund.getPaymentMethodId(), otherPaymentMethod.getPaymentMethodId());
+
+        assertInvoicePaymentsExternalRefund(accountJson.getAccountId(), invoicePaymentExternalRefund);
+        assertRefundInvoiceAdjustments(accountJson.getAccountId());
+        assertRefundAccountBalance(accountJson.getAccountId(), BigDecimal.ZERO, BigDecimal.ZERO);
+
+    }
+
+    private void assertRefundInvoiceAdjustments(final UUID accountId) throws KillBillClientException {
+        final Invoices invoices;
+        invoices = killBillClient.getInvoicesForAccount(accountId, true, true, requestOptions);
+        final Invoice invoiceWithRefund = invoices.get(1);
+        assertEquals(invoiceWithRefund.getAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoiceWithRefund.getRefundAdj().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+        assertEquals(invoiceWithRefund.getItems().size(), 2);
+        assertEquals(invoiceWithRefund.getItems().get(0).getItemType(), InvoiceItemType.RECURRING.toString());
+        assertEquals(invoiceWithRefund.getItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoiceWithRefund.getItems().get(1).getItemType(), InvoiceItemType.ITEM_ADJ.toString());
+        assertEquals(invoiceWithRefund.getItems().get(1).getAmount().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+    }
+
+    private void assertRefundInvoiceNoAdjustments(final UUID accountId) throws KillBillClientException {
+        final Invoices invoices = killBillClient.getInvoicesForAccount(accountId, true, true, requestOptions);
+        final Invoice invoiceWithRefund = invoices.get(1);
+        assertEquals(invoiceWithRefund.getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoiceWithRefund.getRefundAdj().compareTo(BigDecimal.valueOf(249.95).negate()), 0);
+        assertEquals(invoiceWithRefund.getItems().size(), 1);
+        assertEquals(invoiceWithRefund.getItems().get(0).getItemType(), InvoiceItemType.RECURRING.toString());
+        assertEquals(invoiceWithRefund.getItems().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
+    private void assertRefundAccountBalance(final UUID accountId, final BigDecimal balanceAmount, final BigDecimal cbaAmount) throws KillBillClientException {
+        final Account account = killBillClient.getAccount(accountId, true, true, requestOptions);
+        assertEquals(account.getAccountBalance().compareTo(balanceAmount), 0);
+        assertEquals(account.getAccountCBA().compareTo(cbaAmount), 0);
+    }
+
+    private void assertInvoicePaymentsExternalRefund(final UUID accountId, final InvoicePayment invoicePaymentExternalRefund) throws KillBillClientException {
+        final InvoicePayments invoicePaymentsForAccount = killBillClient.getInvoicePaymentsForAccount(accountId, requestOptions);
+        assertEquals(invoicePaymentsForAccount.size(), 2);
+
+        // INVOICE PAYMENT FOR ORIGINAL PURCHASE
+        final InvoicePayment invoicePaymentPurchase = invoicePaymentsForAccount.get(0);
+        assertEquals(invoicePaymentPurchase.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentPurchase.getCreditedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(invoicePaymentPurchase.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE.toString());
+        assertEquals(invoicePaymentPurchase.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        // INVOICE PAYMENT FOR EXTERNAL REFUND
+        final InvoicePayment creditInvoicePayment = invoicePaymentsForAccount.get(1);
+        assertTrue(creditInvoicePayment.equals(invoicePaymentExternalRefund));
+
+        assertEquals(creditInvoicePayment.getPurchasedAmount().compareTo(BigDecimal.ZERO), 0);
+        assertEquals(creditInvoicePayment.getCreditedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(creditInvoicePayment.getTransactions().size(), 1);
+        assertEquals(creditInvoicePayment.getTransactions().get(0).getTransactionType(), TransactionType.CREDIT.toString());
+        assertEquals(creditInvoicePayment.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
+    private void assertSingleInvoicePaymentRefund(final InvoicePayment invoicePaymentRefund) {
+        // ONLY ONE INVOICE PAYMENT IS GENERATED FOR BOTH, PURCHASE AND REFUND.
+        assertEquals(invoicePaymentRefund.getPurchasedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentRefund.getRefundedAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+
+        assertEquals(invoicePaymentRefund.getTransactions().size(), 2);
+        assertEquals(invoicePaymentRefund.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE.toString());
+        assertEquals(invoicePaymentRefund.getTransactions().get(0).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+        assertEquals(invoicePaymentRefund.getTransactions().get(1).getTransactionType(), TransactionType.REFUND.toString());
+        assertEquals(invoicePaymentRefund.getTransactions().get(1).getAmount().compareTo(BigDecimal.valueOf(249.95)), 0);
+    }
+
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 7606915..dfd8d8c 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -28,10 +28,12 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.AuditLog;
+import org.killbill.billing.client.model.Credit;
 import org.killbill.billing.client.model.Invoice;
 import org.killbill.billing.client.model.InvoiceDryRun;
 import org.killbill.billing.client.model.InvoiceItem;
@@ -39,17 +41,22 @@ import org.killbill.billing.client.model.InvoicePayment;
 import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Invoices;
 import org.killbill.billing.client.model.PaymentMethod;
+import org.killbill.billing.client.model.Payments;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
 import org.killbill.billing.invoice.api.DryRunType;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
 import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
 import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
@@ -62,7 +69,7 @@ public class TestInvoice extends TestJaxrsBase {
 
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, AuditLevel.FULL);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false, false, AuditLevel.FULL);
         assertEquals(invoices.size(), 2);
         for (final Invoice invoiceJson : invoices) {
             Assert.assertEquals(invoiceJson.getAuditLogs().size(), 1);
@@ -338,7 +345,7 @@ public class TestInvoice extends TestJaxrsBase {
         final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
         final Invoice invoice = invoices.get(1);
@@ -399,7 +406,7 @@ public class TestInvoice extends TestJaxrsBase {
         final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
         final Invoice invoice = invoices.get(1);
@@ -416,7 +423,7 @@ public class TestInvoice extends TestJaxrsBase {
         adjustmentInvoiceItem.setInvoiceId(invoice.getInvoiceId());
         adjustmentInvoiceItem.setInvoiceItemId(invoiceItem.getInvoiceItemId());
         adjustmentInvoiceItem.setAmount(adjustedAmount);
-        adjustmentInvoiceItem.setCurrency(invoice.getCurrency());
+        adjustmentInvoiceItem.setCurrency(invoice.getCurrency().name());
         killBillClient.adjustInvoiceItem(adjustmentInvoiceItem, createdBy, reason, comment);
 
         // Verify the new invoice balance
@@ -437,9 +444,9 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge.setCurrency(accountJson.getCurrency());
         externalCharge.setDescription(UUID.randomUUID().toString());
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
         assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -472,11 +479,11 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge2 = new InvoiceItem();
         externalCharge2.setAccountId(accountJson.getAccountId());
         externalCharge2.setAmount(chargeAmount);
-        externalCharge2.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge2.setCurrency(accountJson.getCurrency());
         externalCharge2.setDescription(UUID.randomUUID().toString());
         externalCharges.add(externalCharge2);
 
-        final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCNow(), false, createdBy, reason, comment);
+        final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCToday(), false, true, createdBy, reason, comment);
         assertEquals(createdExternalCharges.size(), 2);
         assertEquals(createdExternalCharges.get(0).getCurrency().toString(), accountJson.getCurrency());
         assertEquals(createdExternalCharges.get(1).getCurrency().toString(), accountJson.getCurrency());
@@ -485,6 +492,56 @@ public class TestInvoice extends TestJaxrsBase {
         assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
     }
 
+    @Test(groups = "slow", description = "Can create multiple external charges with same invoice and external keys")
+    public void testExternalChargesWithSameInvoiceAndExternalKeys() throws Exception {
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        // Get the invoices
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 2);
+
+        // Post an external charge
+        final BigDecimal chargeAmount = BigDecimal.TEN;
+
+        final List<InvoiceItem> externalCharges = new ArrayList<InvoiceItem>();
+
+        // Does not pass currency to test on purpose that we will default to account currency
+        final InvoiceItem externalCharge1 = new InvoiceItem();
+        externalCharge1.setAccountId(accountJson.getAccountId());
+        externalCharge1.setAmount(chargeAmount);
+        externalCharge1.setDescription(UUID.randomUUID().toString());
+        externalCharges.add(externalCharge1);
+
+        final InvoiceItem externalCharge2 = new InvoiceItem();
+        externalCharge2.setAccountId(accountJson.getAccountId());
+        externalCharge2.setAmount(chargeAmount);
+        externalCharge2.setCurrency(accountJson.getCurrency());
+        externalCharge2.setDescription(UUID.randomUUID().toString());
+        externalCharges.add(externalCharge2);
+
+        String paymentExternalKey = "anyPaymentExternalKey";
+        String transactionExternalKey = "anyTransactionExternalKey";
+
+        final List<InvoiceItem> createdExternalCharges =
+                killBillClient.createExternalCharges(externalCharges, clock.getUTCToday(), true, true,
+                                                     paymentExternalKey, transactionExternalKey, RequestOptions.builder()
+                                                                                                               .withCreatedBy(createdBy)
+                                                                                                               .withReason(reason)
+                                                                                                               .withComment(comment).build());
+        assertEquals(createdExternalCharges.size(), 2);
+        assertEquals(createdExternalCharges.get(0).getCurrency().toString(), accountJson.getCurrency());
+        assertEquals(createdExternalCharges.get(1).getCurrency().toString(), accountJson.getCurrency());
+
+        // Verify the total number of invoices
+        assertEquals(killBillClient.getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
+
+        Payments payments = killBillClient.getPaymentsForAccount(accountJson.getAccountId(), AuditLevel.NONE, RequestOptions.empty());
+        assertNotNull(payments);
+        // Verify payment with paymentExternalKey provided
+        assertEquals(payments.get(payments.size() - 1).getPaymentExternalKey(), paymentExternalKey);
+        // Verify transactions with transactionExternalKey provided
+        assertEquals(payments.get(payments.size() - 1).getTransactions().get(0).getTransactionExternalKey(), transactionExternalKey);
+    }
+
     @Test(groups = "slow", description = "Can create an external charge and trigger a payment")
     public void testExternalChargeOnNewInvoiceWithAutomaticPayment() throws Exception {
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
@@ -497,8 +554,8 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, createdBy, reason, comment);
+        externalCharge.setCurrency(accountJson.getCurrency());
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), true, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getBalance().compareTo(BigDecimal.ZERO), 0);
         assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -521,9 +578,9 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge.setCurrency(accountJson.getCurrency());
         externalCharge.setBundleId(bundleId);
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
         assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -538,7 +595,7 @@ public class TestInvoice extends TestJaxrsBase {
         final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
         final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -550,9 +607,9 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge.setCurrency(accountJson.getCurrency());
         externalCharge.setInvoiceId(invoiceId);
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
         assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -568,7 +625,7 @@ public class TestInvoice extends TestJaxrsBase {
         final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
         final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -580,9 +637,9 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge.setCurrency(accountJson.getCurrency());
         externalCharge.setInvoiceId(invoiceId);
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, createdBy, reason, comment);
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), true, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
         assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -597,7 +654,7 @@ public class TestInvoice extends TestJaxrsBase {
         final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true);
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, false);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
         final UUID invoiceId = invoices.get(1).getInvoiceId();
@@ -610,10 +667,10 @@ public class TestInvoice extends TestJaxrsBase {
         final InvoiceItem externalCharge = new InvoiceItem();
         externalCharge.setAccountId(accountJson.getAccountId());
         externalCharge.setAmount(chargeAmount);
-        externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
+        externalCharge.setCurrency(accountJson.getCurrency());
         externalCharge.setInvoiceId(invoiceId);
         externalCharge.setBundleId(bundleId);
-        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+        final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCToday(), false, true, createdBy, reason, comment);
         final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
         assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
         assertEquals(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId(), bundleId);
@@ -652,4 +709,202 @@ public class TestInvoice extends TestJaxrsBase {
         }
         Assert.assertNull(page);
     }
+
+    @Test(groups = "slow", description = "Can add a credit to a new invoice")
+    public void testCreateCreditInvoiceAndMoveStatus() throws Exception {
+
+        final Account account = createAccountWithDefaultPaymentMethod();
+
+        final DateTime effectiveDate = clock.getUTCNow();
+        final BigDecimal creditAmount = BigDecimal.TEN;
+        final Credit credit = new Credit();
+        credit.setAccountId(account.getAccountId());
+        credit.setInvoiceId(null);
+        credit.setCreditAmount(creditAmount);
+        final Credit creditJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+
+        Invoice invoice = killBillClient.getInvoice(creditJson.getInvoiceId());
+        Assert.assertEquals(invoice.getStatus(), InvoiceStatus.DRAFT.toString());
+
+        killBillClient.commitInvoice(invoice.getInvoiceId(), createdBy, reason, comment);
+
+        invoice = killBillClient.getInvoice(creditJson.getInvoiceId());
+        Assert.assertEquals(invoice.getStatus(), InvoiceStatus.COMMITTED.toString());
+    }
+
+    @Test(groups = "slow", description = "Can create a migration invoice")
+    public void testInvoiceMigration() throws Exception {
+        final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        // Get the invoices
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true);
+        assertEquals(invoices.size(), 2);
+
+        // Migrate an invoice with one external charge
+        final BigDecimal chargeAmount = BigDecimal.TEN;
+        final InvoiceItem externalCharge = new InvoiceItem();
+        externalCharge.setStartDate(new LocalDate());
+        externalCharge.setAccountId(accountJson.getAccountId());
+        externalCharge.setAmount(chargeAmount);
+        externalCharge.setItemType(InvoiceItemType.EXTERNAL_CHARGE.toString());
+        externalCharge.setCurrency(accountJson.getCurrency());
+
+        final Account accountWithBalance = killBillClient.getAccount(accountJson.getAccountId(), true, true);
+
+        final Invoice migrationInvoice = killBillClient.createMigrationInvoice(accountJson.getAccountId(), null, ImmutableList.<InvoiceItem>of(externalCharge), basicRequestOptions());
+        assertEquals(migrationInvoice.getBalance(), BigDecimal.ZERO);
+        assertEquals(migrationInvoice.getItems().size(), 1);
+        assertEquals(migrationInvoice.getItems().get(0).getAmount().compareTo(chargeAmount), 0);
+        assertEquals(migrationInvoice.getItems().get(0).getCurrency(), accountJson.getCurrency());
+
+        final List<Invoice> invoicesWithMigration = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), true, true);
+        assertEquals(invoicesWithMigration.size(), 3);
+
+        final Account accountWithBalanceAfterMigration = killBillClient.getAccount(accountJson.getAccountId(), true, true);
+        assertEquals(accountWithBalanceAfterMigration.getAccountBalance().compareTo(accountWithBalance.getAccountBalance()), 0);
+    }
+
+    @Test(groups = "slow", description = "Can transfer credit to parent account")
+    public void testInvoiceTransferCreditToParentAccount() throws Exception {
+        final Account parentAccount = createAccount();
+        final Account childAccount = createAccount(parentAccount.getAccountId());
+
+        final BigDecimal creditAmount = BigDecimal.TEN;
+        final Credit credit = new Credit();
+        credit.setAccountId(childAccount.getAccountId());
+        credit.setInvoiceId(null);
+        credit.setCreditAmount(creditAmount);
+
+        // insert credit to child account
+        final Credit creditJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+
+        Invoices childInvoices = killBillClient.getInvoicesForAccount(childAccount.getAccountId(), true, false);
+        Assert.assertEquals(childInvoices.size(), 1);
+        Assert.assertEquals(childInvoices.get(0).getCreditAdj().compareTo(BigDecimal.TEN), 0);
+
+        Invoices parentInvoices = killBillClient.getInvoicesForAccount(parentAccount.getAccountId(), true, false);
+        Assert.assertEquals(parentInvoices.size(), 0);
+
+        // transfer credit to parent account
+        killBillClient.transferChildCreditToParent(childAccount.getAccountId(), basicRequestOptions());
+
+        childInvoices = killBillClient.getInvoicesForAccount(childAccount.getAccountId(), true, false);
+        Assert.assertEquals(childInvoices.size(), 2);
+        Assert.assertEquals(childInvoices.get(1).getCreditAdj().compareTo(BigDecimal.TEN.negate()), 0);
+
+        parentInvoices = killBillClient.getInvoicesForAccount(parentAccount.getAccountId(), true, false);
+        Assert.assertEquals(parentInvoices.size(), 1);
+        Assert.assertEquals(parentInvoices.get(0).getCreditAdj().compareTo(BigDecimal.TEN), 0);
+    }
+
+    @Test(groups = "slow", description = "Fail to transfer credit from an account without parent account",
+            expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = ".* does not have a Parent Account associated")
+    public void testInvoiceTransferCreditAccountNoParent() throws Exception {
+        final Account account = createAccount();
+
+        // transfer credit to parent account
+        killBillClient.transferChildCreditToParent(account.getAccountId(), basicRequestOptions());
+
+    }
+
+    @Test(groups = "slow", description = "Fail to transfer credit from an account without parent account",
+            expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = ".* does not have credit")
+    public void testInvoiceTransferCreditAccountNoCredit() throws Exception {
+        final Account parentAccount = createAccount();
+        final Account childAccount = createAccount(parentAccount.getAccountId());
+
+        // transfer credit to parent account
+        killBillClient.transferChildCreditToParent(childAccount.getAccountId(), basicRequestOptions());
+
+    }
+
+    @Test(groups = "slow", description = "Can search and retrieve parent and children invoices with and without children items")
+    public void testParentInvoiceWithChildItems() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account parentAccount = createAccount();
+        final Account childAccount1 = createAccount(parentAccount.getAccountId());
+        final Account childAccount2 = createAccount(parentAccount.getAccountId());
+        final Account childAccount3 = createAccount(parentAccount.getAccountId());
+
+        // Add a bundle, subscription and move the clock to get the first invoice
+        createEntitlement(childAccount1.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        createEntitlement(childAccount2.getAccountId(), UUID.randomUUID().toString(), "Pistol",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+        createEntitlement(childAccount3.getAccountId(), UUID.randomUUID().toString(), "Shotgun",
+                          ProductCategory.BASE, BillingPeriod.MONTHLY, true);
+
+
+        clock.addDays(32);
+        crappyWaitForLackOfProperSynchonization();
+
+        final List<Invoice> child1Invoices = killBillClient.getInvoicesForAccount(childAccount1.getAccountId(), true, false, requestOptions);
+        final List<Invoice> child2Invoices = killBillClient.getInvoicesForAccount(childAccount2.getAccountId(), true, false, requestOptions);
+        final List<Invoice> child3Invoices = killBillClient.getInvoicesForAccount(childAccount3.getAccountId(), true, false, requestOptions);
+
+        assertEquals(child1Invoices.size(), 2);
+        final Invoice child1RecurringInvoice = child1Invoices.get(1);
+        final InvoiceItem child1RecurringInvoiceItem = child1RecurringInvoice.getItems().get(0);
+        final InvoiceItem child2RecurringInvoiceItem = child2Invoices.get(1).getItems().get(0);
+        final InvoiceItem child3RecurringInvoiceItem = child3Invoices.get(1).getItems().get(0);
+
+        final List<Invoice> parentInvoices = killBillClient.getInvoicesForAccount(parentAccount.getAccountId(), true, false, requestOptions);
+        assertEquals(parentInvoices.size(), 2);
+
+        // check parent invoice with child invoice items and no adjustments
+        // parameters: withItems = true, withChildrenItems = true
+        Invoice parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+        // add an item adjustment
+        final InvoiceItem adjustmentInvoiceItem = new InvoiceItem();
+        adjustmentInvoiceItem.setAccountId(childAccount1.getAccountId());
+        adjustmentInvoiceItem.setInvoiceId(child1RecurringInvoice.getInvoiceId());
+        adjustmentInvoiceItem.setInvoiceItemId(child1RecurringInvoiceItem.getInvoiceItemId());
+        adjustmentInvoiceItem.setAmount(BigDecimal.TEN);
+        adjustmentInvoiceItem.setCurrency(child1RecurringInvoiceItem.getCurrency());
+        final Invoice invoiceAdjustment = killBillClient.adjustInvoiceItem(adjustmentInvoiceItem, requestOptions);
+        final InvoiceItem child1AdjInvoiceItem = killBillClient.getInvoice(invoiceAdjustment.getInvoiceId(), requestOptions).getItems().get(1);
+
+        // check parent invoice with child invoice items and adjustments
+        // parameters: withItems = true, withChildrenItems = true
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+        final InvoiceItem child1InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(0);
+        final InvoiceItem child1AdjInvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(0).getChildItems().get(1);
+        final InvoiceItem child2InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(1).getChildItems().get(0);
+        final InvoiceItem child3InvoiceItemFromParent = parentInvoiceWithChildItems.getItems().get(2).getChildItems().get(0);
+
+        // check children items for each PARENT_SUMMARY item
+        assertTrue(child1InvoiceItemFromParent.equals(child1RecurringInvoiceItem));
+        assertTrue(child1AdjInvoiceItemFromParent.equals(child1AdjInvoiceItem));
+        assertTrue(child2InvoiceItemFromParent.equals(child2RecurringInvoiceItem));
+        assertTrue(child3InvoiceItemFromParent.equals(child3RecurringInvoiceItem));
+
+        // check parent invoice without child invoice items
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), true, false, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertNull(parentInvoiceWithChildItems.getItems().get(0).getChildItems());
+        assertNull(parentInvoiceWithChildItems.getItems().get(1).getChildItems());
+        assertNull(parentInvoiceWithChildItems.getItems().get(2).getChildItems());
+
+        // check parent invoice without items but with child invoice items and adjustment. Should return items anyway.
+        // parameters: withItems = false, withChildrenItems = true
+        parentInvoiceWithChildItems = killBillClient.getInvoice(parentInvoices.get(1).getInvoiceId(), false, true, requestOptions);
+        assertEquals(parentInvoiceWithChildItems.getItems().size(), 3);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(0).getChildItems().size(), 2);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(1).getChildItems().size(), 1);
+        assertEquals(parentInvoiceWithChildItems.getItems().get(2).getChildItems().size(), 1);
+
+    }
+
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
index ffb6d3b..331fbd6 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoicePayment.java
@@ -108,52 +108,6 @@ public class TestInvoicePayment extends TestJaxrsBase {
         verifyInvoice(paymentJson, expectedInvoiceBalance);
     }
 
-    @Test(groups = "slow", description = "Can create a full refund with invoice adjustment")
-    public void testFullRefundWithInvoiceAdjustment() throws Exception {
-        final InvoicePayment paymentJson = setupScenarioWithPayment();
-
-        // Issue a refund for the full amount
-        final BigDecimal refundAmount = paymentJson.getPurchasedAmount();
-        final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
-
-        final InvoicePayments invoicePayments = killBillClient.getInvoicePayment(paymentJson.getTargetInvoiceId());
-        Assert.assertEquals(invoicePayments.size(), 1);
-
-        // Post and verify the refund
-        final InvoicePaymentTransaction refund = new InvoicePaymentTransaction();
-        refund.setPaymentId(paymentJson.getPaymentId());
-        refund.setAmount(refundAmount);
-        refund.setIsAdjusted(true);
-        final Payment paymentAfterRefundJson = killBillClient.createInvoicePaymentRefund(refund, createdBy, reason, comment);
-        verifyRefund(paymentJson, paymentAfterRefundJson, refundAmount);
-
-        // Verify the invoice balance
-        verifyInvoice(paymentJson, expectedInvoiceBalance);
-
-        final InvoicePayments invoicePaymentsAfterRefund = killBillClient.getInvoicePayment(paymentJson.getTargetInvoiceId());
-        Assert.assertEquals(invoicePaymentsAfterRefund.size(), 1);
-
-    }
-
-    @Test(groups = "slow", description = "Can create a partial refund with invoice adjustment")
-    public void testPartialRefundWithInvoiceAdjustment() throws Exception {
-        final InvoicePayment paymentJson = setupScenarioWithPayment();
-
-        // Issue a refund for a fraction of the amount
-        final BigDecimal refundAmount = getFractionOfAmount(paymentJson.getPurchasedAmount());
-        final BigDecimal expectedInvoiceBalance = BigDecimal.ZERO;
-
-        // Post and verify the refund
-        final InvoicePaymentTransaction refund = new InvoicePaymentTransaction();
-        refund.setPaymentId(paymentJson.getPaymentId());
-        refund.setAmount(refundAmount);
-        refund.setIsAdjusted(true);
-        final Payment paymentAfterRefundJson = killBillClient.createInvoicePaymentRefund(refund, createdBy, reason, comment);
-        verifyRefund(paymentJson, paymentAfterRefundJson, refundAmount);
-
-        // Verify the invoice balance
-        verifyInvoice(paymentJson, expectedInvoiceBalance);
-    }
 
     @Test(groups = "slow", description = "Can create a full refund with invoice item adjustment")
     public void testRefundWithFullInvoiceItemAdjustment() throws Exception {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
index 5044598..d4ec7bf 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestJaxrsBase.java
@@ -37,6 +37,7 @@ import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import org.killbill.billing.api.TestApiListener;
 import org.killbill.billing.client.KillBillClient;
 import org.killbill.billing.client.KillBillHttpClient;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Payment;
 import org.killbill.billing.client.model.PaymentTransaction;
 import org.killbill.billing.client.model.Tenant;
@@ -53,9 +54,10 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.server.config.KillbillServerConfig;
 import org.killbill.billing.server.listeners.KillbillGuiceListener;
 import org.killbill.billing.server.modules.KillbillServerModule;
+import org.killbill.billing.tenant.api.TenantCacheInvalidation;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
-import org.killbill.billing.util.config.PaymentConfig;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.PaymentConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.jdbi.guice.DaoConfig;
 import org.skife.config.ConfigurationObjectFactory;
@@ -72,6 +74,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.inject.Module;
 import com.google.inject.util.Modules;
+import net.sf.ehcache.CacheManager;
 
 public class TestJaxrsBase extends KillbillClient {
 
@@ -101,6 +104,12 @@ public class TestJaxrsBase extends KillbillClient {
     @Inject
     protected SecurityConfig securityConfig;
 
+    @Inject
+    protected TenantCacheInvalidation tenantCacheInvalidation;
+
+    @Inject
+    protected CacheManager cacheManager;
+
     protected DaoConfig daoConfig;
     protected KillbillServerConfig serverConfig;
 
@@ -190,6 +199,10 @@ public class TestJaxrsBase extends KillbillClient {
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
+
+        // Because we truncate the tables, the database record_id auto_increment will be reset
+        tenantCacheInvalidation.setLatestRecordIdProcessed(0L);
+
         externalBus.start();
         internalBus.start();
         cacheControllerDispatcher.clearAll();
@@ -247,6 +260,7 @@ public class TestJaxrsBase extends KillbillClient {
         server.start();
     }
 
+
     protected Iterable<EventListener> getListeners() {
         return new Iterable<EventListener>() {
             @Override
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestOverdue.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestOverdue.java
index 6685955..6b13996 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestOverdue.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestOverdue.java
@@ -22,11 +22,13 @@ import java.math.BigDecimal;
 import java.util.Comparator;
 import java.util.List;
 
+import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.Invoice;
 import org.killbill.billing.client.model.InvoicePayment;
 import org.killbill.billing.client.model.Invoices;
-import org.killbill.billing.client.model.Payment;
+import org.killbill.billing.client.model.Tags;
+import org.killbill.billing.util.tag.ControlTagType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -40,42 +42,41 @@ public class TestOverdue extends TestJaxrsBase {
     @Test(groups = "slow", description = "Upload and retrieve a per tenant overdue config")
     public void testMultiTenantOverdueConfig() throws Exception {
         final String overdueConfigPath = Resources.getResource("overdue.xml").getPath();
-        killBillClient.uploadXMLOverdueConfig(overdueConfigPath, createdBy, reason, comment);
+        killBillClient.uploadXMLOverdueConfig(overdueConfigPath, requestOptions);
 
-        final String overdueConfig = killBillClient.getXMLOverdueConfig();
+        final String overdueConfig = killBillClient.getXMLOverdueConfig(requestOptions);
         Assert.assertNotNull(overdueConfig);
     }
 
-
     @Test(groups = "slow", description = "Can retrieve the account overdue status")
     public void testOverdueStatus() throws Exception {
         // Create an account without a payment method
         final Account accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
 
         // Get the invoices
-        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId());
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
         // 2 invoices but look for the non zero dollar one
         assertEquals(invoices.size(), 2);
 
         // We're still clear - see the configuration
-        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId()).getIsClearState());
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getIsClearState());
 
         clock.addDays(30);
         crappyWaitForLackOfProperSynchonization();
-        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId()).getName(), "OD1");
+        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getName(), "OD1");
 
         clock.addDays(10);
         crappyWaitForLackOfProperSynchonization();
-        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId()).getName(), "OD2");
+        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getName(), "OD2");
 
         clock.addDays(10);
         crappyWaitForLackOfProperSynchonization();
-        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId()).getName(), "OD3");
+        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getName(), "OD3");
 
         // Post external payments, paying the most recent invoice first: this is to avoid a race condition where
         // a refresh overdue notification kicks in after the first payment, which makes the account goes CLEAR and
         // triggers an AUTO_INVOICE_OFF tag removal (hence adjustment of the other invoices balance).
-        final Invoices invoicesForAccount = killBillClient.getInvoicesForAccount(accountJson.getAccountId());
+        final Invoices invoicesForAccount = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
         final List<Invoice> mostRecentInvoiceFirst = Ordering.<Invoice>from(new Comparator<Invoice>() {
             @Override
             public int compare(final Invoice invoice1, final Invoice invoice2) {
@@ -89,7 +90,7 @@ public class TestOverdue extends TestJaxrsBase {
                 invoicePayment.setPurchasedAmount(invoice.getAmount());
                 invoicePayment.setAccountId(accountJson.getAccountId());
                 invoicePayment.setTargetInvoiceId(invoice.getInvoiceId());
-                killBillClient.createInvoicePayment(invoicePayment, true, createdBy, reason, comment);
+                killBillClient.createInvoicePayment(invoicePayment, true, requestOptions);
             }
         }
 
@@ -97,6 +98,85 @@ public class TestOverdue extends TestJaxrsBase {
         crappyWaitForLackOfProperSynchonization();
 
         // Verify we're in clear state
-        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId()).getIsClearState());
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getIsClearState());
+    }
+
+    @Test(groups = "slow", description = "Allow overdue condition by control tag defined in overdue config xml file")
+    public void testControlTagOverdueConfig() throws Exception {
+        final String overdueConfigPath = Resources.getResource("overdueWithControlTag.xml").getPath();
+        killBillClient.uploadXMLOverdueConfig(overdueConfigPath, requestOptions);
+
+        // Create an account without a payment method and assign a TEST tag
+        final Account accountJson = createAccountNoPMBundleAndSubscription();
+        final Tags accountTag = killBillClient.createAccountTag(accountJson.getAccountId(), ControlTagType.TEST.getId(), requestOptions);
+        assertEquals(accountTag.get(0).getTagDefinitionId(), ControlTagType.TEST.getId());
+
+        // Create an account without a TEST tag
+        final Account accountJsonNoTag = createAccountNoPMBundleAndSubscription();
+
+        // No payment will be triggered as the account doesn't have a payment method
+        clock.addMonths(1);
+        crappyWaitForLackOfProperSynchonization();
+
+        // Get the invoices
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
+        // 2 invoices but look for the non zero dollar one
+        assertEquals(invoices.size(), 2);
+
+        final List<Invoice> invoicesNoTag = killBillClient.getInvoicesForAccount(accountJsonNoTag.getAccountId(), requestOptions);
+        // 2 invoices but look for the non zero dollar one
+        assertEquals(invoicesNoTag.size(), 2);
+
+        // We're still clear - see the configuration
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getIsClearState());
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJsonNoTag.getAccountId(), requestOptions).getIsClearState());
+
+        clock.addDays(30);
+        crappyWaitForLackOfProperSynchonization();
+
+        // This account is expected to move to OD1 state because it matches with controlTag defined
+        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getName(), "OD1");
+        // This account is not expected to move to OD1 state because it does not match with controlTag defined
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJsonNoTag.getAccountId(), requestOptions).getIsClearState());
+    }
+
+    @Test(groups = "slow", description = "Allow overdue condition by exclusion control tag defined in overdue config xml file")
+    public void testExclusionControlTagOverdueConfig() throws Exception {
+        final String overdueConfigPath = Resources.getResource("overdueWithExclusionControlTag.xml").getPath();
+        killBillClient.uploadXMLOverdueConfig(overdueConfigPath, requestOptions);
+
+        // Create an account without a payment method and assign a TEST tag
+        final Account accountJson = createAccountNoPMBundleAndSubscription();
+        final Tags accountTag = killBillClient.createAccountTag(accountJson.getAccountId(), ControlTagType.TEST.getId(), requestOptions);
+        assertEquals(accountTag.get(0).getTagDefinitionId(), ControlTagType.TEST.getId());
+
+        // Create an account without a TEST tag
+        final Account accountJsonNoTag = createAccountNoPMBundleAndSubscription();
+
+        // move a month a wait for invoicing
+        // No payment will be triggered as the account doesn't have a payment method
+        clock.addMonths(1);
+        crappyWaitForLackOfProperSynchonization();
+
+        // Get the invoices
+        final List<Invoice> invoices = killBillClient.getInvoicesForAccount(accountJson.getAccountId(), requestOptions);
+        // 2 invoices but look for the non zero dollar one
+        assertEquals(invoices.size(), 2);
+
+        final List<Invoice> invoicesNoTag = killBillClient.getInvoicesForAccount(accountJsonNoTag.getAccountId(), requestOptions);
+        // 2 invoices but look for the non zero dollar one
+        assertEquals(invoicesNoTag.size(), 2);
+
+        // We're still clear - see the configuration
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getIsClearState());
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJsonNoTag.getAccountId(), requestOptions).getIsClearState());
+
+        clock.addDays(30);
+        crappyWaitForLackOfProperSynchonization();
+
+        // This account is not expected to move to OD1 state because it does not match with exclusion controlTag defined
+        Assert.assertTrue(killBillClient.getOverdueStateForAccount(accountJson.getAccountId(), requestOptions).getIsClearState());
+        // This account is expected to move to OD1 state because it matches with exclusion controlTag defined
+        Assert.assertEquals(killBillClient.getOverdueStateForAccount(accountJsonNoTag.getAccountId(), requestOptions).getName(), "OD1");
     }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
index 5c21ce8..88cb7ea 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPayment.java
@@ -19,20 +19,25 @@ package org.killbill.billing.jaxrs;
 
 import java.math.BigDecimal;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
 import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.ComboPaymentTransaction;
+import org.killbill.billing.client.model.InvoicePayments;
 import org.killbill.billing.client.model.Payment;
 import org.killbill.billing.client.model.PaymentMethod;
 import org.killbill.billing.client.model.PaymentMethodPluginDetail;
 import org.killbill.billing.client.model.PaymentTransaction;
 import org.killbill.billing.client.model.Payments;
 import org.killbill.billing.client.model.PluginProperty;
+import org.killbill.billing.client.model.TagDefinition;
+import org.killbill.billing.client.model.Tags;
 import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
 import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
 import org.killbill.billing.osgi.api.OSGIServiceRegistration;
@@ -42,12 +47,14 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
 import org.killbill.billing.payment.provider.MockPaymentControlProviderPlugin;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
+import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
@@ -148,6 +155,129 @@ public class TestPayment extends TestJaxrsBase {
     }
 
     @Test(groups = "slow")
+    public void testWithFailedPaymentAndScheduledAttemptsGetInvoicePayment() throws Exception {
+        mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+        final Account account = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+        // Getting Invoice #2 (first after Trial period)
+        UUID failedInvoiceId = killBillClient.getInvoicesForAccount(account.getAccountId(), false, false, RequestOptions.empty()).get(1).getInvoiceId();
+
+        HashMultimap<String, String> queryParams = HashMultimap.create();
+        queryParams.put("withAttempts", "true");
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment)
+                                                    .withQueryParams(queryParams).build();
+
+        InvoicePayments invoicePayments = killBillClient.getInvoicePayment(failedInvoiceId, inputOptions);
+
+        Assert.assertEquals(invoicePayments.get(0).getTargetInvoiceId(), failedInvoiceId);
+        Assert.assertNotNull(invoicePayments.get(0).getPaymentAttempts());
+        Assert.assertEquals(invoicePayments.get(0).getPaymentAttempts().size(), 2);
+        Assert.assertEquals(invoicePayments.get(0).getPaymentAttempts().get(0).getStateName(), "RETRIED");
+        Assert.assertEquals(invoicePayments.get(0).getPaymentAttempts().get(1).getStateName(), "SCHEDULED");
+
+
+        // Remove the future notification and check SCHEDULED does not appear any longer
+        killBillClient.cancelScheduledPaymentTransaction(null, invoicePayments.get(0).getPaymentAttempts().get(1).getTransactionExternalKey(), inputOptions);
+        invoicePayments = killBillClient.getInvoicePayment(failedInvoiceId, inputOptions);
+        Assert.assertEquals(invoicePayments.get(0).getPaymentAttempts().size(), 1);
+        Assert.assertEquals(invoicePayments.get(0).getPaymentAttempts().get(0).getStateName(), "RETRIED");
+    }
+
+    @Test(groups = "slow")
+    public void testWithFailedPaymentAndScheduledAttemptsGetPaymentsForAccount() throws Exception {
+        mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+        final Account account = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        HashMultimap<String, String> queryParams = HashMultimap.create();
+        queryParams.put("withAttempts", "true");
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment)
+                                                    .withQueryParams(queryParams).build();
+
+        Payments payments = killBillClient.getPaymentsForAccount(account.getAccountId(), inputOptions);
+
+        Assert.assertNotNull(payments.get(0).getPaymentAttempts());
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(0).getStateName(), "RETRIED");
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(1).getStateName(), "SCHEDULED");
+    }
+
+    @Test(groups = "slow")
+    public void testWithFailedPaymentAndScheduledAttemptsGetPayments() throws Exception {
+        mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+        createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        HashMultimap<String, String> queryParams = HashMultimap.create();
+        queryParams.put("withAttempts", "true");
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment)
+                                                    .withQueryParams(queryParams).build();
+
+        Payments payments = killBillClient.getPayments(0L, 100L, null, new HashMap<String, String>(), AuditLevel.NONE, inputOptions);
+
+        Assert.assertNotNull(payments.get(0).getPaymentAttempts());
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(0).getStateName(), "RETRIED");
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(1).getStateName(), "SCHEDULED");
+    }
+
+    @Test(groups = "slow")
+    public void testWithFailedPaymentAndScheduledAttemptsSearchPayments() throws Exception {
+        mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+        createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        HashMultimap<String, String> queryParams = HashMultimap.create();
+        queryParams.put("withAttempts", "true");
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment)
+                                                    .withQueryParams(queryParams).build();
+
+        Payments payments = killBillClient.searchPayments("", 0L, 100L, AuditLevel.NONE, inputOptions);
+
+        Assert.assertNotNull(payments.get(0).getPaymentAttempts());
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(0).getStateName(), "RETRIED");
+        Assert.assertEquals(payments.get(0).getPaymentAttempts().get(1).getStateName(), "SCHEDULED");
+    }
+
+    @Test(groups = "slow")
+    public void testWithFailedPaymentAndScheduledAttemptsGetPaymentById() throws Exception {
+        mockPaymentProviderPlugin.makeNextPaymentFailWithError();
+        createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        Payments payments = killBillClient.searchPayments("", 0L, 100L, AuditLevel.NONE, requestOptions);
+        Assert.assertNotNull(payments.get(0));
+        Payment payment = killBillClient.getPayment(payments.get(0).getPaymentId(), false, true, ImmutableMap.<String, String>of(), AuditLevel.NONE, requestOptions);
+
+        Assert.assertNotNull(payment.getPaymentAttempts());
+        Assert.assertEquals(payment.getPaymentAttempts().get(0).getStateName(), "RETRIED");
+        Assert.assertEquals(payment.getPaymentAttempts().get(1).getStateName(), "SCHEDULED");
+    }
+
+    @Test(groups = "slow")
+    public void testDeletePaymentMethodWithAutoPayOff() throws Exception {
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final UUID paymentMethodId = account.getPaymentMethodId();
+
+        RequestOptions inputOptions = RequestOptions.builder()
+                                                    .withCreatedBy(createdBy)
+                                                    .withReason(reason)
+                                                    .withComment(comment).build();
+
+        killBillClient.deletePaymentMethod(paymentMethodId, true, false, inputOptions);
+
+        Tags accountTags = killBillClient.getAccountTags(account.getAccountId(), inputOptions);
+
+        Assert.assertNotNull(accountTags);
+        Assert.assertEquals(accountTags.get(0).getTagDefinitionName(), "AUTO_PAY_OFF");
+    }
+
+    @Test(groups = "slow")
     public void testCreateRetrievePayment() throws Exception {
         final Account account = createAccountWithDefaultPaymentMethod();
         final String externalPaymentKey = UUID.randomUUID().toString();
@@ -375,15 +505,10 @@ public class TestPayment extends TestJaxrsBase {
 
         final Payment initialPayment = createVerifyTransaction(account, paymentMethodId, paymentExternalKey, authTransactionExternalKey, transactionType, TransactionStatus.SUCCESS.name(), amount, amount, pluginProperties, 1);
 
-        // The payment was already completed
+        // The payment was already completed, it should succeed (no-op)
         final PaymentTransaction completeTransactionByPaymentId = new PaymentTransaction();
         completeTransactionByPaymentId.setPaymentId(initialPayment.getPaymentId());
-        try {
-            killBillClient.completePayment(completeTransactionByPaymentId, pluginProperties, basicRequestOptions());
-            fail("Completion should not succeed, there is no PENDING payment transaction");
-        } catch (final KillBillClientException expected) {
-            // Invalid parameter paymentId: XXXX
-        }
+        killBillClient.completePayment(completeTransactionByPaymentId, pluginProperties, basicRequestOptions());
     }
 
 
@@ -515,6 +640,50 @@ public class TestPayment extends TestJaxrsBase {
         Assert.assertNull(payment);
     }
 
+    @Test(groups = "slow")
+    public void testGetTagsForPaymentTransaction() throws Exception {
+        UUID tagDefinitionId = UUID.randomUUID();
+        String tagDefinitionName = "payment-transaction";
+        TagDefinition tagDefinition = new TagDefinition(tagDefinitionId, false, tagDefinitionName, "description", null);
+        final TagDefinition createdTagDefinition = killBillClient.createTagDefinition(tagDefinition, requestOptions);
+
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final String externalPaymentKey = UUID.randomUUID().toString();
+        final UUID paymentId = testCreateRetrievePayment(account, null, externalPaymentKey, 1);
+
+        final Payment payment = killBillClient.getPaymentByExternalKey(externalPaymentKey, requestOptions);
+        assertEquals(payment.getPaymentId(), paymentId);
+
+        UUID paymentTransactionId = payment.getTransactions().get(0).getTransactionId();
+        killBillClient.createPaymentTransactionTag(paymentTransactionId, createdTagDefinition.getId(), requestOptions);
+
+        final Tags paymentTransactionTags = killBillClient.getPaymentTransactionTags(paymentTransactionId, requestOptions);
+
+        Assert.assertNotNull(paymentTransactionTags);
+        Assert.assertEquals(paymentTransactionTags.get(0).getTagDefinitionName(), tagDefinitionName);
+    }
+
+    @Test(groups = "slow")
+    public void testCreateTagForPaymentTransaction() throws Exception {
+        UUID tagDefinitionId = UUID.randomUUID();
+        String tagDefinitionName = "payment-transaction";
+        TagDefinition tagDefinition = new TagDefinition(tagDefinitionId, false, tagDefinitionName, "description", null);
+        final TagDefinition createdTagDefinition = killBillClient.createTagDefinition(tagDefinition, requestOptions);
+
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final String externalPaymentKey = UUID.randomUUID().toString();
+        final UUID paymentId = testCreateRetrievePayment(account, null, externalPaymentKey, 1);
+
+        final Payment payment = killBillClient.getPaymentByExternalKey(externalPaymentKey, requestOptions);
+        assertEquals(payment.getPaymentId(), paymentId);
+
+        UUID paymentTransactionId = payment.getTransactions().get(0).getTransactionId();
+        final Tags paymentTransactionTag = killBillClient.createPaymentTransactionTag(paymentTransactionId, createdTagDefinition.getId(), requestOptions);
+
+        Assert.assertNotNull(paymentTransactionTag);
+        Assert.assertEquals(paymentTransactionTag.get(0).getTagDefinitionName(), tagDefinitionName);
+    }
+
     private UUID testCreateRetrievePayment(final Account account, @Nullable final UUID paymentMethodId,
                                            final String paymentExternalKey, final int paymentNb) throws Exception {
         // Authorization
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentPluginProperties.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentPluginProperties.java
new file mode 100644
index 0000000..e24c9a9
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentPluginProperties.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.KillBillHttpClient;
+import org.killbill.billing.client.RequestOptions;
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Payment;
+import org.killbill.billing.client.model.PaymentTransaction;
+import org.killbill.billing.client.model.PluginProperty;
+import org.killbill.billing.control.plugin.api.OnFailurePaymentControlResult;
+import org.killbill.billing.control.plugin.api.OnSuccessPaymentControlResult;
+import org.killbill.billing.control.plugin.api.PaymentControlApiException;
+import org.killbill.billing.control.plugin.api.PaymentControlContext;
+import org.killbill.billing.control.plugin.api.PaymentControlPluginApi;
+import org.killbill.billing.control.plugin.api.PriorPaymentControlResult;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
+import org.killbill.billing.payment.retry.DefaultFailureCallResult;
+import org.killbill.billing.payment.retry.DefaultOnSuccessPaymentControlResult;
+import org.killbill.billing.payment.retry.DefaultPriorPaymentControlResult;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.inject.Inject;
+
+public class TestPaymentPluginProperties extends TestJaxrsBase {
+
+    @Inject
+    private OSGIServiceRegistration<PaymentControlPluginApi> controlPluginRegistry;
+
+    private PluginPropertiesVerificator mockPaymentControlProviderPlugin;
+
+    public static class PluginPropertiesVerificator implements PaymentControlPluginApi {
+
+        public static final String PLUGIN_NAME = "PLUGIN_PROPERTY_VERIFICATOR";
+
+        private Iterable<org.killbill.billing.payment.api.PluginProperty> expectedProperties;
+
+        public PluginPropertiesVerificator() {
+            clearExpectPluginProperties();
+        }
+
+        @Override
+        public PriorPaymentControlResult priorCall(final PaymentControlContext paymentControlContext, final Iterable<org.killbill.billing.payment.api.PluginProperty> properties) throws PaymentControlApiException {
+            assertPluginProperties(properties);
+            return new DefaultPriorPaymentControlResult(false);
+        }
+
+        @Override
+        public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<org.killbill.billing.payment.api.PluginProperty> properties) throws PaymentControlApiException {
+            return new DefaultOnSuccessPaymentControlResult();
+        }
+
+        @Override
+        public OnFailurePaymentControlResult onFailureCall(final PaymentControlContext paymentControlContext, final Iterable<org.killbill.billing.payment.api.PluginProperty> properties) throws PaymentControlApiException {
+            return new DefaultFailureCallResult();
+        }
+
+        public void setExpectPluginProperties(final Iterable<org.killbill.billing.payment.api.PluginProperty> expectedProperties) {
+            this.expectedProperties = expectedProperties;
+        }
+
+        public void clearExpectPluginProperties() {
+            this.expectedProperties = ImmutableList.of();
+        }
+
+        private void assertPluginProperties(final Iterable<org.killbill.billing.payment.api.PluginProperty> properties) {
+            for (org.killbill.billing.payment.api.PluginProperty input : properties) {
+                boolean found  = false;
+                for (org.killbill.billing.payment.api.PluginProperty expect : expectedProperties) {
+                    if (expect.getKey().equals(input.getKey()) && expect.getValue().equals(expect.getValue())) {
+                        found = true;
+                        break;
+                    }
+                }
+                Assert.assertTrue(found);
+            }
+
+            for (org.killbill.billing.payment.api.PluginProperty expect : properties) {
+                boolean found  = false;
+                for (org.killbill.billing.payment.api.PluginProperty input : expectedProperties) {
+                    if (expect.getKey().equals(input.getKey()) && expect.getValue().equals(expect.getValue())) {
+                        found = true;
+                        break;
+                    }
+                }
+                Assert.assertTrue(found);
+            }
+        }
+    }
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+
+        mockPaymentControlProviderPlugin = new PluginPropertiesVerificator();
+        controlPluginRegistry.registerService(new OSGIServiceDescriptor() {
+            @Override
+            public String getPluginSymbolicName() {
+                return null;
+            }
+
+            @Override
+            public String getPluginName() {
+                return PluginPropertiesVerificator.PLUGIN_NAME;
+            }
+
+            @Override
+            public String getRegistrationName() {
+                return PluginPropertiesVerificator.PLUGIN_NAME;
+            }
+        }, mockPaymentControlProviderPlugin);
+    }
+
+    @AfterMethod(groups = "slow")
+    public void tearDown() throws Exception {
+        mockPaymentControlProviderPlugin.clearExpectPluginProperties();
+    }
+
+    @Test(groups = "slow")
+    public void testWithQueryPropertiesOnly() throws Exception {
+        final List<org.killbill.billing.payment.api.PluginProperty> expectProperties = new ArrayList<org.killbill.billing.payment.api.PluginProperty>();
+
+        final Map<String, String> queryProperties = new HashMap<String, String>();
+        addProperty("key1", "val1", queryProperties, expectProperties);
+        addProperty("key2", "val2", queryProperties, expectProperties);
+        addProperty("key3", "val3", queryProperties, expectProperties);
+        addProperty("key4", "val4", queryProperties, expectProperties);
+
+        final List<PluginProperty> bodyProperties = new ArrayList<PluginProperty>();
+
+        testInternal(queryProperties, bodyProperties, expectProperties);
+
+    }
+
+    @Test(groups = "slow")
+    public void testWithBodyPropertiesOnly() throws Exception {
+        final List<org.killbill.billing.payment.api.PluginProperty> expectProperties = new ArrayList<org.killbill.billing.payment.api.PluginProperty>();
+
+        final Map<String, String> queryProperties = new HashMap<String, String>();
+
+        final List<PluginProperty> bodyProperties = new ArrayList<PluginProperty>();
+        addProperty("keyXXX1", "valXXXX1", bodyProperties, expectProperties);
+        addProperty("keyXXX2", "valXXXX2", bodyProperties, expectProperties);
+        addProperty("keyXXX3", "valXXXX3", bodyProperties, expectProperties);
+
+        testInternal(queryProperties, bodyProperties, expectProperties);
+
+    }
+
+    @Test(groups = "slow")
+    public void testWithBodyAndQueryProperties() throws Exception {
+        final List<org.killbill.billing.payment.api.PluginProperty> expectProperties = new ArrayList<org.killbill.billing.payment.api.PluginProperty>();
+
+        final Map<String, String> queryProperties = new HashMap<String, String>();
+        addProperty("key1", "val1", queryProperties, expectProperties);
+        addProperty("key2", "val2", queryProperties, expectProperties);
+        addProperty("key3", "val3", queryProperties, expectProperties);
+        addProperty("key4", "val4", queryProperties, expectProperties);
+
+        final List<PluginProperty> bodyProperties = new ArrayList<PluginProperty>();
+        addProperty("keyXXX1", "valXXXX1", bodyProperties, expectProperties);
+        addProperty("keyXXX2", "valXXXX2", bodyProperties, expectProperties);
+        addProperty("keyXXX3", "valXXXX3", bodyProperties, expectProperties);
+
+        testInternal(queryProperties, bodyProperties, expectProperties);
+    }
+
+    private void testInternal(final Map<String, String> queryProperties, final List<PluginProperty> bodyProperties, final List<org.killbill.billing.payment.api.PluginProperty> expectProperties) throws Exception {
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final UUID paymentMethodId = account.getPaymentMethodId();
+        final BigDecimal amount = BigDecimal.TEN;
+
+        final String pending = PaymentPluginStatus.PENDING.toString();
+        final ImmutableMap<String, String> pluginProperties = ImmutableMap.<String, String>of(MockPaymentProviderPlugin.PLUGIN_PROPERTY_PAYMENT_PLUGIN_STATUS_OVERRIDE, pending);
+
+        TransactionType transactionType = TransactionType.AUTHORIZE;
+        final String paymentExternalKey = UUID.randomUUID().toString();
+        final String authTransactionExternalKey = UUID.randomUUID().toString();
+
+        final Payment initialPayment = createVerifyTransaction(account, paymentMethodId, paymentExternalKey, authTransactionExternalKey, transactionType, amount, pluginProperties);
+
+        mockPaymentControlProviderPlugin.setExpectPluginProperties(expectProperties);
+
+        // Complete operation: first, only specify the payment id
+        final PaymentTransaction completeTransactionByPaymentId = new PaymentTransaction();
+        completeTransactionByPaymentId.setPaymentId(initialPayment.getPaymentId());
+        completeTransactionByPaymentId.setProperties(bodyProperties);
+
+        final RequestOptions basicRequestOptions = basicRequestOptions();
+        final Multimap<String, String> params = LinkedListMultimap.create(basicRequestOptions.getQueryParams());
+        params.putAll(KillBillHttpClient.CONTROL_PLUGIN_NAME, ImmutableList.<String>of(PluginPropertiesVerificator.PLUGIN_NAME));
+
+        final RequestOptions requestOptionsWithParams = basicRequestOptions.extend()
+                                                          .withQueryParams(params).build();
+
+        killBillClient.completePayment(completeTransactionByPaymentId, queryProperties, requestOptionsWithParams);
+    }
+
+    private Payment createVerifyTransaction(final Account account,
+                                            @Nullable final UUID paymentMethodId,
+                                            final String paymentExternalKey,
+                                            final String transactionExternalKey,
+                                            final TransactionType transactionType,
+                                            final BigDecimal transactionAmount,
+                                            final Map<String, String> pluginProperties) throws KillBillClientException {
+        final PaymentTransaction authTransaction = new PaymentTransaction();
+        authTransaction.setAmount(transactionAmount);
+        authTransaction.setCurrency(account.getCurrency());
+        authTransaction.setPaymentExternalKey(paymentExternalKey);
+        authTransaction.setTransactionExternalKey(transactionExternalKey);
+        authTransaction.setTransactionType(transactionType.toString());
+        final Payment payment = killBillClient.createPayment(account.getAccountId(), paymentMethodId, authTransaction, pluginProperties, basicRequestOptions());
+        return payment;
+    }
+
+    private void addProperty(final String key, final String value, final Map<String, String> dest, final List<org.killbill.billing.payment.api.PluginProperty> expectProperties) {
+        dest.put(key, value);
+        expectProperties.add(new org.killbill.billing.payment.api.PluginProperty(key, value, false));
+    }
+
+    private void addProperty(final String key, final String value, List<PluginProperty> bodyProperties, final List<org.killbill.billing.payment.api.PluginProperty> expectProperties) {
+        bodyProperties.add(new PluginProperty(key, value, false));
+        expectProperties.add(new org.killbill.billing.payment.api.PluginProperty(key, value, false));
+    }
+}
\ No newline at end of file
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java
new file mode 100644
index 0000000..a180f70
--- /dev/null
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPerTenantConfig.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.jaxrs;
+
+import java.util.HashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.Payments;
+import org.killbill.billing.client.model.Tenant;
+import org.killbill.billing.client.model.TenantKey;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.TransactionStatus;
+import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
+import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
+import org.killbill.billing.util.jackson.ObjectMapper;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+import com.jayway.awaitility.Awaitility;
+import com.jayway.awaitility.Duration;
+
+public class TestPerTenantConfig extends TestJaxrsBase {
+
+    @Inject
+    protected OSGIServiceRegistration<PaymentPluginApi> registry;
+
+    private MockPaymentProviderPlugin mockPaymentProviderPlugin;
+
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        mockPaymentProviderPlugin = (MockPaymentProviderPlugin) registry.getServiceForName(PLUGIN_NAME);
+    }
+
+    @AfterMethod(groups = "slow")
+    public void tearDown() throws Exception {
+        mockPaymentProviderPlugin.clear();
+    }
+
+    @Test(groups = "slow")
+    public void testFailedPaymentWithPerTenantRetryConfig() throws Exception {
+        // Create the tenant
+        final String apiKeyTenant1 = "tenantSuperTuned";
+        final String apiSecretTenant1 = "2367$$ffr79";
+        loginTenant(apiKeyTenant1, apiSecretTenant1);
+        final Tenant tenant1 = new Tenant();
+        tenant1.setApiKey(apiKeyTenant1);
+        tenant1.setApiSecret(apiSecretTenant1);
+        killBillClient.createTenant(tenant1, createdBy, reason, comment);
+
+        // Configure our plugin to fail
+        mockPaymentProviderPlugin.makeAllInvoicesFailWithError(true);
+
+        // Upload the config
+        final ObjectMapper mapper = new ObjectMapper();
+        final HashMap<String, String> perTenantProperties = new HashMap<String, String>();
+        perTenantProperties.put("org.killbill.payment.retry.days", "1,1,1");
+        final String perTenantConfig = mapper.writeValueAsString(perTenantProperties);
+
+        final TenantKey tenantKey = killBillClient.postConfigurationPropertiesForTenant(perTenantConfig, basicRequestOptions());
+
+        final Account accountJson = createAccountWithPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+        final Payments payments = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
+        Assert.assertEquals(payments.size(), 1);
+        Assert.assertEquals(payments.get(0).getTransactions().size(), 1);
+
+        //
+        // Because we have specified a retry interval of one day we should see the new attempt after moving clock 1 day (and not 8 days which is default)
+        //
+
+        //
+        // Now unregister special per tenant config and we the first retry occurs one day after (and still fails), it now sets a retry date of 8 days
+        //
+        killBillClient.unregisterConfigurationForTenant(basicRequestOptions());
+        // org.killbill.tenant.broadcast.rate has been set to 1s
+        crappyWaitForLackOfProperSynchonization(2000);
+
+        clock.addDays(1);
+
+        Awaitility.await()
+                  .atMost(4, TimeUnit.SECONDS)
+                  .pollInterval(Duration.ONE_SECOND)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+
+                          return killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0).getTransactions().size() == 2;
+                      }
+                  });
+        final Payments payments2 = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
+        Assert.assertEquals(payments2.size(), 1);
+        Assert.assertEquals(payments2.get(0).getTransactions().size(), 2);
+        Assert.assertEquals(payments2.get(0).getTransactions().get(0).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
+        Assert.assertEquals(payments2.get(0).getTransactions().get(1).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
+
+        clock.addDays(1);
+        crappyWaitForLackOfProperSynchonization(3000);
+
+        // No retry with default config
+        final Payments payments3 = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
+        Assert.assertEquals(payments3.size(), 1);
+        Assert.assertEquals(payments3.get(0).getTransactions().size(), 2);
+
+        mockPaymentProviderPlugin.makeAllInvoicesFailWithError(false);
+        clock.addDays(7);
+
+        Awaitility.await()
+                  .atMost(4, TimeUnit.SECONDS)
+                  .pollInterval(Duration.ONE_SECOND)
+                  .until(new Callable<Boolean>() {
+                      @Override
+                      public Boolean call() throws Exception {
+                          return killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0).getTransactions().size() == 3;
+                      }
+                  });
+        final Payments payments4 = killBillClient.getPaymentsForAccount(accountJson.getAccountId());
+        Assert.assertEquals(payments4.size(), 1);
+        Assert.assertEquals(payments4.get(0).getTransactions().size(), 3);
+        Assert.assertEquals(payments4.get(0).getTransactions().get(0).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
+        Assert.assertEquals(payments4.get(0).getTransactions().get(1).getStatus(), TransactionStatus.PAYMENT_FAILURE.name());
+        Assert.assertEquals(payments4.get(0).getTransactions().get(2).getStatus(), TransactionStatus.SUCCESS.name());
+    }
+}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
index 390e4ed..1c6b542 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPushNotification.java
@@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.TenantKey;
 import org.killbill.billing.jaxrs.json.NotificationJson;
 import org.killbill.billing.server.notifications.PushNotificationListener;
@@ -54,15 +55,18 @@ public class TestPushNotification extends TestJaxrsBase {
 
     private volatile boolean callbackCompleted;
     private volatile boolean callbackCompletedWithError;
+    private volatile int expectedNbCalls = 1;
+    private volatile boolean forceToFail = false;
+    private volatile int failedResponseStatus = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
 
     @Override
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
         callbackServer = new CallbackServer(this, SERVER_PORT, CALLBACK_ENDPOINT);
-        callbackCompleted = false;
-        callbackCompletedWithError = false;
+        resetCallbackStatusProperties();
         callbackServer.startServer();
+        this.expectedNbCalls = 1;
     }
 
     @AfterMethod(groups = "slow")
@@ -85,7 +89,7 @@ public class TestPushNotification extends TestJaxrsBase {
     public void retrieveAccountWithAsserts(final String accountId) {
         try {
             // Just check we can retrieve the account with the id from the callback
-            killBillClient.getAccount(UUID.fromString(accountId));
+            killBillClient.getAccount(UUID.fromString(accountId), requestOptions);
         } catch (final Exception e) {
             Assert.fail(e.getMessage());
         }
@@ -93,12 +97,12 @@ public class TestPushNotification extends TestJaxrsBase {
 
     @Test(groups = "slow")
     public void testPushNotification() throws Exception {
-        // Register tenant for callback
-        final String callback = "http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPOINT;
-        final TenantKey result0 = killBillClient.registerCallbackNotificationForTenant(callback, createdBy, reason, comment);
-        Assert.assertEquals(result0.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
-        Assert.assertEquals(result0.getValues().size(), 1);
-        Assert.assertEquals(result0.getValues().get(0), callback);
+        final String callback = registerTenantForCallback();
+
+        // set expected number of calls
+        // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
+        // 2nd: is "eventType":"ACCOUNT_CREATION"
+        this.expectedNbCalls = 2;
 
         // Create account to trigger a push notification
         createAccount();
@@ -112,17 +116,161 @@ public class TestPushNotification extends TestJaxrsBase {
             Assert.fail("Assertion during callback failed...");
         }
 
-        final TenantKey result = killBillClient.getCallbackNotificationForTenant();
+        unregisterTenantForCallback(callback);
+    }
+
+    private void unregisterTenantForCallback(final String callback) throws KillBillClientException {
+        final TenantKey result = killBillClient.getCallbackNotificationForTenant(requestOptions);
         Assert.assertEquals(result.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
         Assert.assertEquals(result.getValues().size(), 1);
         Assert.assertEquals(result.getValues().get(0), callback);
 
-        killBillClient.unregisterCallbackNotificationForTenant(createdBy, reason, comment);
-        final TenantKey result2 = killBillClient.getCallbackNotificationForTenant();
+        killBillClient.unregisterCallbackNotificationForTenant(requestOptions);
+        final TenantKey result2 = killBillClient.getCallbackNotificationForTenant(requestOptions);
         Assert.assertEquals(result2.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
         Assert.assertEquals(result2.getValues().size(), 0);
     }
 
+    private String registerTenantForCallback() throws KillBillClientException, InterruptedException {// Register tenant for callback
+        final String callback = "http://127.0.0.1:" + SERVER_PORT + CALLBACK_ENDPOINT;
+        final TenantKey result0 = killBillClient.registerCallbackNotificationForTenant(callback, requestOptions);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError); // expected true because is not an ACCOUNT_CREATION event
+
+        Assert.assertEquals(result0.getKey(), TenantKV.TenantKey.PUSH_NOTIFICATION_CB.toString());
+        Assert.assertEquals(result0.getValues().size(), 1);
+        Assert.assertEquals(result0.getValues().get(0), callback);
+
+        // reset values
+        resetCallbackStatusProperties();
+        return callback;
+    }
+
+    @Test(groups = "slow")
+    public void testPushNotificationRetries() throws Exception {
+        final String callback = registerTenantForCallback();
+
+        // force server to fail
+        // Notifications retries are set to:
+        // org.killbill.billing.server.notifications.retries=15m,1h,1d,2d
+        this.forceToFail = true;
+
+        // set expected number of calls
+        // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
+        // 2nd: is original "eventType":"ACCOUNT_CREATION" call [force error]
+        // 3rd: is 1st notification retry (+ 15m) [force error]
+        // 4th: is 1st notification retry (+ 1h) [force error]
+        // 5th: is 1st notification retry (+ 1d) [success]
+        this.expectedNbCalls = 5;
+
+        // Create account to trigger a push notification
+        createAccount();
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock 15 minutes and get 1st retry
+        clock.addDeltaFromReality(900000);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock an hour and get 2nd retry
+        clock.addDeltaFromReality(3600000);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // make call success
+        this.forceToFail = false;
+
+        // move clock a day, get 3rd retry and wait for a success push notification
+        clock.addDays(1);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertFalse(callbackCompletedWithError);
+
+        unregisterTenantForCallback(callback);
+    }
+
+    @Test(groups = "slow")
+    public void testPushNotificationRetriesMaxAttemptNumber() throws Exception {
+        final String callback = registerTenantForCallback();
+
+        // force server to fail
+        // Notifications retries are set to:
+        // org.killbill.billing.server.notifications.retries=15m,1h,1d,2d
+        this.forceToFail = true;
+
+        // set expected number of calls
+        // 1st: was "eventType":"TENANT_CONFIG_CHANGE"
+        // 2nd: is original "eventType":"ACCOUNT_CREATION" call [force error]
+        // 3rd: is 1st notification retry (+ 15m) [force error]
+        // 4th: is 2nd notification retry (+ 1h) [force error]
+        // 5th: is 3rd notification retry (+ 1d) [force error]
+        // 6th: is 4th notification retry (+ 2d) [force error]
+        this.expectedNbCalls = 6;
+
+        // Create account to trigger a push notification
+        createAccount();
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock 15 minutes and get 1st retry
+        clock.addDeltaFromReality(900000);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock an hour and get 2nd retry
+        clock.addDeltaFromReality(3600000);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock a day and get 3rd retry
+        clock.addDays(1);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+
+        resetCallbackStatusProperties();
+
+        // move clock a day and get 4rd retry
+        clock.addDays(2);
+
+        Assert.assertTrue(waitForCallbacksToComplete());
+        Assert.assertTrue(callbackCompletedWithError);
+        resetCallbackStatusProperties();
+
+        clock.addDays(4);
+
+        Assert.assertFalse(waitForCallbacksToComplete());
+        Assert.assertFalse(callbackCompletedWithError);
+
+        unregisterTenantForCallback(callback);
+    }
+
+    private void resetCallbackStatusProperties() {
+        // reset values
+        this.callbackCompleted = false;
+        this.callbackCompletedWithError = false;
+    }
+
     public void setCompleted(final boolean withError) {
         callbackCompleted = true;
         callbackCompletedWithError = withError;
@@ -144,7 +292,7 @@ public class TestPushNotification extends TestJaxrsBase {
             final ServletContextHandler context = new ServletContextHandler();
             context.setContextPath("/");
             server.setHandler(context);
-            context.addServlet(new ServletHolder(new CallmebackServlet(test, 1)), callbackEndpoint);
+            context.addServlet(new ServletHolder(new CallmebackServlet(test)), callbackEndpoint);
             server.start();
         }
 
@@ -159,15 +307,13 @@ public class TestPushNotification extends TestJaxrsBase {
 
         private static final Logger log = LoggerFactory.getLogger(CallmebackServlet.class);
 
-        private final int expectedNbCalls;
         private final AtomicInteger receivedCalls;
         private final TestPushNotification test;
         private final ObjectMapper objectMapper = new ObjectMapper();
 
         private boolean withError;
 
-        public CallmebackServlet(final TestPushNotification test, final int expectedNbCalls) {
-            this.expectedNbCalls = expectedNbCalls;
+        public CallmebackServlet(final TestPushNotification test) {
             this.test = test;
             this.receivedCalls = new AtomicInteger(0);
             this.withError = false;
@@ -176,8 +322,18 @@ public class TestPushNotification extends TestJaxrsBase {
         @Override
         protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
             final int current = receivedCalls.incrementAndGet();
-
             final String body = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8"));
+            withError = false;
+
+            log.info("CallmebackServlet received {} calls , current = {} at {}", current, body, getClock().getUTCNow());
+
+            if (test.forceToFail) {
+                response.setStatus(test.failedResponseStatus);
+                log.info("CallmebackServlet is force to fail for testing purposes");
+                test.setCompleted(true);
+                return;
+            }
+
             response.setStatus(HttpServletResponse.SC_OK);
 
             log.info("Got body {}", body);
@@ -197,15 +353,15 @@ public class TestPushNotification extends TestJaxrsBase {
                 withError = true;
             }
 
-            log.info("CallmebackServlet received {} calls , current = {}", current, body);
             stopServerWhenComplete(current, withError);
         }
 
         private void stopServerWhenComplete(final int current, final boolean withError) {
-            if (current == expectedNbCalls) {
+            if (current == test.expectedNbCalls) {
                 log.info("Excellent, we are done!");
                 test.setCompleted(withError);
             }
         }
+
     }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestSecurity.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestSecurity.java
index fe40922..47e92ac 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestSecurity.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestSecurity.java
@@ -22,11 +22,13 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.UUID;
 
 import javax.annotation.Nullable;
 import javax.ws.rs.core.Response.Status;
 
 import org.killbill.billing.client.KillBillClientException;
+import org.killbill.billing.client.RequestOptions;
 import org.killbill.billing.client.model.Permissions;
 import org.killbill.billing.client.model.RoleDefinition;
 import org.killbill.billing.client.model.UserRoles;
@@ -85,6 +87,17 @@ public class TestSecurity extends TestJaxrsBase {
     }
 
     @Test(groups = "slow")
+    public void testDynamicUserRolesNoPermissions() throws Exception {
+        final String username = UUID.randomUUID().toString();
+        final String password = UUID.randomUUID().toString();
+        final String role = UUID.randomUUID().toString();
+        testDynamicUserRolesInternal(username, password, role, ImmutableList.of(""), false);
+
+        final Permissions permissions = killBillClient.getPermissions(RequestOptions.builder().withUser(username).withPassword(password).build());
+        Assert.assertEquals(permissions.size(), 0);
+    }
+
+    @Test(groups = "slow")
     public void testUserPermission() throws KillBillClientException {
 
         final String roleDefinition = "notEnoughToAddUserAndRoles";
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTenantKV.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTenantKV.java
index f2539d8..5232201 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTenantKV.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestTenantKV.java
@@ -72,7 +72,7 @@ public class TestTenantKV extends TestJaxrsBase {
         final Tenant otherTenantWithDifferentStateMachine = new Tenant();
         otherTenantWithDifferentStateMachine.setApiKey(UUID.randomUUID().toString());
         otherTenantWithDifferentStateMachine.setApiSecret(UUID.randomUUID().toString());
-        killBillClient.createTenant(otherTenantWithDifferentStateMachine, requestOptions);
+        killBillClient.createTenant(otherTenantWithDifferentStateMachine, true, requestOptions);
         final RequestOptions requestOptionsOtherTenant = requestOptions.extend()
                                                                        .withTenantApiKey(otherTenantWithDifferentStateMachine.getApiKey())
                                                                        .withTenantApiSecret(otherTenantWithDifferentStateMachine.getApiSecret())
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
index 2e9dd6a..dfc49e3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestUsage.java
@@ -19,9 +19,11 @@ package org.killbill.billing.jaxrs;
 
 import java.util.UUID;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
 import org.killbill.billing.client.model.Bundle;
 import org.killbill.billing.client.model.RolledUpUsage;
@@ -115,4 +117,62 @@ public class TestUsage extends TestJaxrsBase {
         Assert.assertEquals(retrievedUsage4.getRolledUpUnits().get(0).getUnitType(), "bullets");
         Assert.assertEquals((long) retrievedUsage4.getRolledUpUnits().get(0).getAmount(), 5);
     }
+
+    @Test(groups = "slow", description = "Test tracking ID already exists")
+    public void testRecordUsageTrackingIdExists() throws Exception {
+
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final Subscription base = new Subscription();
+        base.setAccountId(accountJson.getAccountId());
+        base.setProductName("Pistol");
+        base.setProductCategory(ProductCategory.BASE);
+        base.setBillingPeriod(BillingPeriod.MONTHLY);
+        base.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        final Subscription addOn = new Subscription();
+        addOn.setAccountId(accountJson.getAccountId());
+        addOn.setProductName("Bullets");
+        addOn.setProductCategory(ProductCategory.ADD_ON);
+        addOn.setBillingPeriod(BillingPeriod.NO_BILLING_PERIOD);
+        addOn.setPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+        final Bundle bundle = killBillClient.createSubscriptionWithAddOns(ImmutableList.<Subscription>of(base, addOn),
+                                                                          null,
+                                                                          DEFAULT_WAIT_COMPLETION_TIMEOUT_SEC,
+                                                                          createdBy,
+                                                                          reason,
+                                                                          comment);
+        final UUID addOnSubscriptionId = Iterables.<Subscription>find(bundle.getSubscriptions(),
+                                                                      new Predicate<Subscription>() {
+                                                                          @Override
+                                                                          public boolean apply(final Subscription input) {
+                                                                              return ProductCategory.ADD_ON.equals(input.getProductCategory());
+                                                                          }
+                                                                      }).getSubscriptionId();
+
+        final UsageRecord usageRecord1 = new UsageRecord();
+        usageRecord1.setAmount(10L);
+        usageRecord1.setRecordDate(clock.getUTCToday().minusDays(1));
+
+        final UnitUsageRecord unitUsageRecord = new UnitUsageRecord();
+        unitUsageRecord.setUnitType("bullets");
+        unitUsageRecord.setUsageRecords(ImmutableList.<UsageRecord>of(usageRecord1));
+
+        final SubscriptionUsageRecord usage = new SubscriptionUsageRecord();
+        usage.setSubscriptionId(addOnSubscriptionId);
+        usage.setTrackingId(UUID.randomUUID().toString());
+        usage.setUnitUsageRecords(ImmutableList.<UnitUsageRecord>of(unitUsageRecord));
+
+        killBillClient.createSubscriptionUsageRecord(usage, createdBy, reason, comment);
+
+        try {
+            killBillClient.createSubscriptionUsageRecord(usage, createdBy, reason, comment);
+            Assert.fail();
+        }
+        catch (final KillBillClientException e) {
+            Assert.assertEquals(e.getBillingException().getCode(), (Integer) ErrorCode.USAGE_RECORD_TRACKING_ID_ALREADY_EXISTS.getCode());
+        }
+
+    }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
index fb6d67e..51a41c7 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestConfigMagicObfuscator.java
@@ -31,19 +31,19 @@ public class TestConfigMagicObfuscator extends ServerTestSuiteNoDB {
     @Test(groups = "fast")
     public void testKey() throws Exception {
         verify("Assigning value [pass2b78b7cef] for [org.killbill.billing.plugin.avatax.licenseKey] on [org.killbill.billing.plugins.avatax#getLicenseKey()]",
-               "Assigning value [***MASKED****] for [org.killbill.billing.plugin.avatax.licenseKey] on [org.killbill.billing.plugins.avatax#getLicenseKey()]");
+               "Assigning value [*************] for [org.killbill.billing.plugin.avatax.licenseKey] on [org.killbill.billing.plugins.avatax#getLicenseKey()]");
 
         verify("Assigning value [pass2b78b7cef] for [org.killbill.billing.plugin.avatax.apiKey] on [org.killbill.billing.plugins.avatax#getApiKey()]",
-               "Assigning value [***MASKED****] for [org.killbill.billing.plugin.avatax.apiKey] on [org.killbill.billing.plugins.avatax#getApiKey()]");
+               "Assigning value [*************] for [org.killbill.billing.plugin.avatax.apiKey] on [org.killbill.billing.plugins.avatax#getApiKey()]");
     }
 
     @Test(groups = "fast")
     public void testPassword() throws Exception {
         verify("Assigning value [pass2b78b7ce] for [org.killbill.dao.pass] on [org.killbill.commons.jdbi.guice.DaoConfig#getPass()]",
-               "Assigning value [***MASKED***] for [org.killbill.dao.pass] on [org.killbill.commons.jdbi.guice.DaoConfig#getPass()]");
+               "Assigning value [************] for [org.killbill.dao.pass] on [org.killbill.commons.jdbi.guice.DaoConfig#getPass()]");
 
         verify("Assigning value [pass2b78b7ce] for [org.killbill.dao.password] on [org.killbill.commons.jdbi.guice.DaoConfig#getPassword()]",
-               "Assigning value [***MASKED***] for [org.killbill.dao.password] on [org.killbill.commons.jdbi.guice.DaoConfig#getPassword()]");
+               "Assigning value [************] for [org.killbill.dao.password] on [org.killbill.commons.jdbi.guice.DaoConfig#getPassword()]");
     }
 
     private void verify(final String input, final String output) {
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
index 794a96d..29d51ef 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestLuhnMaskingObfuscator.java
@@ -50,17 +50,17 @@ public class TestLuhnMaskingObfuscator extends ServerTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testConvert() {
-        verify("try 5137 0049 8639 6404 and 5137 0049 8639 6403", "try 5137 0049 8639 6404 and ****MASKED*****6403");
+        verify("try 5137 0049 8639 6404 and 5137 0049 8639 6403", "try 5137 0049 8639 6404 and 513700********6403");
     }
 
     @Test(groups = "fast")
     public void testConvertCcNumberAtStartNonCcNumberAtEnd() {
-        verify("5137 0049 8639 6403 and 5137 0049 8639 6404", "****MASKED*****6403 and 5137 0049 8639 6404");
+        verify("5137 0049 8639 6403 and 5137 0049 8639 6404", "513700********6403 and 5137 0049 8639 6404");
     }
 
     @Test(groups = "fast")
     public void testConvertMultiple() {
-        verify("try 5137 0049 8639 6403 multiple 5137 0049 8639 6404 possible 4111-1111-1111 1111 card 4111111111111112 numbers", "try ****MASKED*****6403 multiple 5137 0049 8639 6404 possible ****MASKED*****1111 card 4111111111111112 numbers");
+        verify("try 5137 0049 8639 6403 multiple 5137 0049 8639 6404 possible 4111-1111-1111 1111 card 4111111111111112 numbers", "try 513700********6403 multiple 5137 0049 8639 6404 possible 411111********1111 card 4111111111111112 numbers");
     }
 
     @Test(groups = "fast")
@@ -102,41 +102,41 @@ public class TestLuhnMaskingObfuscator extends ServerTestSuiteNoDB {
                + "Switch/Solo (Paymentech)"
                + "6331101999990016",
                "American Express"
-               + "**MASKED***0005"
+               + "378282*****0005"
                + "American Express"
-               + "**MASKED***8431"
+               + "371449*****8431"
                + "American Express Corporate"
-               + "**MASKED***1000"
+               + "378734*****1000"
                + "Australian BankCard"
-               + "***MASKED***8250"
+               + "561059******8250"
                + "Diners Club"
-               + "**MASKED**5904"
+               + "305693****5904"
                + "Diners Club"
-               + "**MASKED**3237"
+               + "385200****3237"
                + "Discover"
-               + "***MASKED***1117"
+               + "601111******1117"
                + "Discover"
-               + "***MASKED***9424"
+               + "601100******9424"
                + "JCB"
-               + "***MASKED***0000"
+               + "353011******0000"
                + "JCB"
-               + "***MASKED***0505"
+               + "356600******0505"
                + "MasterCard"
-               + "***MASKED***4444"
+               + "555555******4444"
                + "MasterCard"
-               + "***MASKED***5100"
+               + "510510******5100"
                + "Visa"
-               + "***MASKED***1111"
+               + "411111******1111"
                + "Visa"
-               + "***MASKED***1881"
+               + "401288******1881"
                + "Visa"
-               + "*MASKED**2222"
+               + "422222***2222"
                + "Note : Even though this number has a different character count than the other test numbers, it is the correct and functional number."
                + "Processor-specific Cards"
                + "Dankort (PBS)"
-               + "***MASKED***3742"
+               + "501971******3742"
                + "Switch/Solo (Paymentech)"
-               + "***MASKED***0016");
+               + "633110******0016");
     }
 
     @Test(groups = "fast")
@@ -235,7 +235,7 @@ public class TestLuhnMaskingObfuscator extends ServerTestSuiteNoDB {
                "1 > Content-Type: application/json\n" +
                "1 > Accept: */*",
                "1 * Server in-bound request\n" +
-               "1 > POST http://127.0.0.1:8080/1.0/kb/accounts/2a55045a-ce1d-4344-942d-b825536328f9/payments?pluginProperty=cc_number=***MASKED***1111\n" +
+               "1 > POST http://127.0.0.1:8080/1.0/kb/accounts/2a55045a-ce1d-4344-942d-b825536328f9/payments?pluginProperty=cc_number=411111******1111\n" +
                "1 > X-Killbill-ApiSecret: lazar\n" +
                "1 > Authorization: Basic YWRtaW46cGFzc3dvcmQ=\n" +
                "1 > X-Killbill-CreatedBy: admin\n" +
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
index 5109ad3..b36c2a5 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscator.java
@@ -41,7 +41,7 @@ public class TestObfuscator extends ServerTestSuiteNoDB {
         final Pattern pattern = Pattern.compile("number=([^;]+)");
         final ImmutableList<Pattern> patterns = ImmutableList.<Pattern>of(pattern);
         Assert.assertEquals(obfuscator.obfuscate("number=1234;number=12345;number=123456;number=1234567;number=12345678;number=123456789", patterns, Mockito.mock(ILoggingEvent.class)),
-                            "number=MASKED;number=MASKED;number=MASKED;number=MASKED*;number=*MASKED*;number=*MASKED**");
+                            "number=****;number=*****;number=******;number=*******;number=********;number=*********");
 
     }
 
@@ -51,12 +51,12 @@ public class TestObfuscator extends ServerTestSuiteNoDB {
         final Pattern pattern2 = Pattern.compile("numberB=([^;]+)");
         final ImmutableList<Pattern> patterns = ImmutableList.<Pattern>of(pattern1, pattern2);
         Assert.assertEquals(obfuscator.obfuscate("number=1234;numberB=12345;number=123456;numberB=1234567;number=12345678;numberB=123456789", patterns, Mockito.mock(ILoggingEvent.class)),
-                            "number=MASKED;numberB=MASKED;number=MASKED;numberB=MASKED*;number=*MASKED*;numberB=*MASKED**");
+                            "number=****;numberB=*****;number=******;numberB=*******;number=********;numberB=*********");
 
     }
 
     @Test(groups = "fast")
     public void testObfuscateConfidentialData() {
-        Assert.assertEquals(obfuscator.obfuscateConfidentialData("5137004986396403", "6403"), "***MASKED***");
+        Assert.assertEquals(obfuscator.obfuscateConfidentialData("5137004986396403", "6403"), "************");
     }
 }
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscatorConverter.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscatorConverter.java
index 943a620..4d54512 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscatorConverter.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestObfuscatorConverter.java
@@ -54,9 +54,9 @@ public class TestObfuscatorConverter extends ServerTestSuiteNoDB {
                "</gateway>",
                "Starting purchase call: \n" +
                "<gateway>\n" +
-               "<card>***MASKED***1111</card>\n" +
-               "<bankAccountNumber>*MASKED**</bankAccountNumber>\n" +
-               "<password>**MASKED***</password>\n" +
+               "<card>411111******1111</card>\n" +
+               "<bankAccountNumber>*********</bankAccountNumber>\n" +
+               "<password>***********</password>\n" +
                "</gateway>");
     }
 
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
index 3476f58..11d9162 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/obfuscators/TestPatternObfuscator.java
@@ -45,16 +45,16 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
                "</ns2:shopperName>\n",
                "<ns:expiryMonth>04</expiryMonth>\n" +
                "<ns:expiryYear>2015</expiryYear>\n" +
-               "<ns:holderName>*MASKED*</holderName>\n" +
-               "<ns:number>*****MASKED*****</number>\n" +
-               "<ns2:shopperEmail>****MASKED*****</ns2:shopperEmail>\n" +
+               "<ns:holderName>********</holderName>\n" +
+               "<ns:number>****************</number>\n" +
+               "<ns2:shopperEmail>***************</ns2:shopperEmail>\n" +
                "<ns2:shopperIP>127.0.0.1</ns2:shopperIP>\n" +
                "<ns2:shopperInteraction xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>\n" +
                "<ns2:shopperName>\n" +
-               "    <firstName>MASKED</firstName>\n" +
+               "    <firstName>***</firstName>\n" +
                "    <gender xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>\n" +
                "    <infix xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>\n" +
-               "    <lastName>MASKED</lastName>\n" +
+               "    <lastName>*****</lastName>\n" +
                "</ns2:shopperName>\n");
     }
 
@@ -131,25 +131,25 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
                "      <clientLibraryVersion>1.47.0</clientLibraryVersion>\n" +
                "      <clientEnvironment>java</clientEnvironment>\n" +
                "<billTo>\n" +
-               "  <firstName>MASKED</firstName>\n" +
-               "  <lastName>MASKED</lastName>\n" +
+               "  <firstName>****</firstName>\n" +
+               "  <lastName>***</lastName>\n" +
                "  <street1>5, oakriu road</street1>\n" +
                "  <street2>apt. 298</street2>\n" +
                "  <city>Gdio Foia</city>\n" +
                "  <state>FL</state>\n" +
                "  <postalCode>49302</postalCode>\n" +
                "  <country>US</country>\n" +
-               "  <email>**********MASKED**********</email>\n" +
+               "  <email>**************************</email>\n" +
                "</billTo>\n" +
                "<purchaseTotals>\n" +
                "  <currency>USD</currency>\n" +
                "  <grandTotalAmount>0.00</grandTotalAmount>\n" +
                "</purchaseTotals>\n" +
                "<card>\n" +
-               "  <accountNumber>*****MASKED*****</accountNumber>\n" +
+               "  <accountNumber>****************</accountNumber>\n" +
                "  <expirationMonth>12</expirationMonth>\n" +
                "  <expirationYear>2017</expirationYear>\n" +
-               "  <cvNumber>MASKED</cvNumber>\n" +
+               "  <cvNumber>****</cvNumber>\n" +
                "  <cardType>001</cardType>\n" +
                "</card>\n" +
                "<subscription>\n" +
@@ -171,7 +171,7 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
     @Test(groups = "fast")
     public void testLitle() throws Exception {
         verify("<litleOnlineRequest merchantId=\\\"merchant_id\\\" version=\\\"8.18\\\" xmlns=\\\"http://www.litle.com/schema\\\"><authentication><user>login</user><password>password</password></authentication><sale id=\\\"615b9cb3-8580-4f57-bf69-9\\\" reportGroup=\\\"Default Report Group\\\"><orderId>615b9cb3-8580-4f57-bf69-9</orderId><amount>10000</amount><orderSource>ecommerce</orderSource><billToAddress><name>John Doe</name><email>1428325948-test@tester.com</email><addressLine1>5, oakriu road</addressLine1><addressLine2>apt. 298</addressLine2><city>Gdio Foia</city><state>FL</state><zip>49302</zip><country>US</country></billToAddress><shipToAddress/><card><type>VI</type><number>4242424242424242</number><expDate>1217</expDate><cardValidationNum>1234</cardValidationNum></card></sale></litleOnlineRequest>",
-               "<litleOnlineRequest merchantId=\\\"merchant_id\\\" version=\\\"8.18\\\" xmlns=\\\"http://www.litle.com/schema\\\"><authentication><user>login</user><password>*MASKED*</password></authentication><sale id=\\\"615b9cb3-8580-4f57-bf69-9\\\" reportGroup=\\\"Default Report Group\\\"><orderId>615b9cb3-8580-4f57-bf69-9</orderId><amount>10000</amount><orderSource>ecommerce</orderSource><billToAddress><name>*MASKED*</name><email>**********MASKED**********</email><addressLine1>5, oakriu road</addressLine1><addressLine2>apt. 298</addressLine2><city>Gdio Foia</city><state>FL</state><zip>49302</zip><country>US</country></billToAddress><shipToAddress/><card><type>VI</type><number>*****MASKED*****</number><expDate>1217</expDate><cardValidationNum>MASKED</cardValidationNum></card></sale></litleOnlineRequest>");
+               "<litleOnlineRequest merchantId=\\\"merchant_id\\\" version=\\\"8.18\\\" xmlns=\\\"http://www.litle.com/schema\\\"><authentication><user>login</user><password>********</password></authentication><sale id=\\\"615b9cb3-8580-4f57-bf69-9\\\" reportGroup=\\\"Default Report Group\\\"><orderId>615b9cb3-8580-4f57-bf69-9</orderId><amount>10000</amount><orderSource>ecommerce</orderSource><billToAddress><name>********</name><email>**************************</email><addressLine1>5, oakriu road</addressLine1><addressLine2>apt. 298</addressLine2><city>Gdio Foia</city><state>FL</state><zip>49302</zip><country>US</country></billToAddress><shipToAddress/><card><type>VI</type><number>****************</number><expDate>1217</expDate><cardValidationNum>****</cardValidationNum></card></sale></litleOnlineRequest>");
     }
 
     @Test(groups = "fast")
@@ -203,7 +203,7 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
                "  \"card\": {\n" +
                "    \"id\": \"card_483etw4er9fg4vF3sQdrt3FG\",\n" +
                "    \"object\": \"card\",\n" +
-               "    \"banknumber\": *****MASKED*****,\n" +
+               "    \"banknumber\": ****************,\n" +
                "    \"last4\": \"0000\",\n" +
                "    \"brand\": \"Visa\",\n" +
                "    \"funding\": \"credit\",\n" +
@@ -211,7 +211,7 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
                "    \"exp_year\": 2019,\n" +
                "    \"fingerprint\": \"HOh74kZU387WlUvy\",\n" +
                "    \"country\": \"US\",\n" +
-               "    \"name\": **MASKED***,\n" +
+               "    \"name\": ***********,\n" +
                "    \"address_line1\": null,\n" +
                "    \"address_line2\": null,\n" +
                "    \"address_city\": null,\n" +
@@ -244,7 +244,7 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
                "</entry>\n",
                "<entry>\n" +
                "  <key xsi:type=\"xsd:string\">PayU.ccvv</key>\n" +
-               "  <value xsi:type=\"xsd:string\">MASKED</value>\n" +
+               "  <value xsi:type=\"xsd:string\">****</value>\n" +
                "</entry>\n" +
                "<entry>\n" +
                "  <key xsi:type=\"xsd:string\">PayU.ccnum</key>\n" +
@@ -263,7 +263,7 @@ public class TestPatternObfuscator extends ServerTestSuiteNoDB {
     @Test(groups = "fast", description = "Test for ActiveMerchant wiredump_device logging")
     public void testWithQuotedNewLines() throws Exception {
         verify("[cybersource-plugin] \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><accountNumber>4111111111111111</accountNumber>\\n  <expirationMonth>09</expirationMonth>\\n  \"",
-               "[cybersource-plugin] \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><accountNumber>*****MASKED*****</accountNumber>\\n  <expirationMonth>09</expirationMonth>\\n  \"");
+               "[cybersource-plugin] \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><accountNumber>****************</accountNumber>\\n  <expirationMonth>09</expirationMonth>\\n  \"");
     }
 
     @Test(groups = "fast")
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
index 7bdf408..f0e5e86 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/server/log/ServerTestSuiteNoDB.java
@@ -28,7 +28,7 @@ public abstract class ServerTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
 
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
-        final Injector injector = Guice.createInjector(new GuicyKillbillTestModule(configSource));
+        final Injector injector = Guice.createInjector(new TestServerModuleNoDB(configSource));
         injector.injectMembers(this);
     }
 }
diff --git a/profiles/killbill/src/test/resources/killbill.properties b/profiles/killbill/src/test/resources/killbill.properties
index 427e790..776243b 100644
--- a/profiles/killbill/src/test/resources/killbill.properties
+++ b/profiles/killbill/src/test/resources/killbill.properties
@@ -30,3 +30,8 @@ org.killbill.osgi.bundle.install.dir=/var/tmp/somethingthatdoesnotexist
 
 # Speed up from the (more secure) default
 org.killbill.security.shiroNbHashIterations=10
+
+org.killbill.tenant.broadcast.rate=1s
+
+# exponential delay retries for push notifications
+org.killbill.billing.server.notifications.retries=15m,1h,1d,2d
\ No newline at end of file
diff --git a/profiles/killbill/src/test/resources/overdueWithControlTag.xml b/profiles/killbill/src/test/resources/overdueWithControlTag.xml
new file mode 100644
index 0000000..6d20b0e
--- /dev/null
+++ b/profiles/killbill/src/test/resources/overdueWithControlTag.xml
@@ -0,0 +1,63 @@
+<!--
+  ~ Copyright 2014-2016 Groupon, Inc
+  ~ Copyright 2014-2016 The Billing Project, LLC
+  ~
+  ~ The Billing Project 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.
+  -->
+
+<overdueConfig>
+   <accountOverdueStates>
+       <state name="OD3">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>50</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagInclusion>TEST</controlTagInclusion>
+           </condition>
+           <externalMessage>Reached OD3</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD2">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>40</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagInclusion>TEST</controlTagInclusion>
+           </condition>
+           <externalMessage>Reached OD2</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD1">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>30</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagInclusion>TEST</controlTagInclusion>
+           </condition>
+           <externalMessage>Reached OD1</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+   </accountOverdueStates>
+</overdueConfig>
diff --git a/profiles/killbill/src/test/resources/overdueWithExclusionControlTag.xml b/profiles/killbill/src/test/resources/overdueWithExclusionControlTag.xml
new file mode 100644
index 0000000..1da2113
--- /dev/null
+++ b/profiles/killbill/src/test/resources/overdueWithExclusionControlTag.xml
@@ -0,0 +1,63 @@
+<!--
+  ~ Copyright 2014-2016 Groupon, Inc
+  ~ Copyright 2014-2016 The Billing Project, LLC
+  ~
+  ~ The Billing Project 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.
+  -->
+
+<overdueConfig>
+   <accountOverdueStates>
+       <state name="OD3">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>50</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagExclusion>TEST</controlTagExclusion>
+           </condition>
+           <externalMessage>Reached OD3</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD2">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>40</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagExclusion>TEST</controlTagExclusion>
+           </condition>
+           <externalMessage>Reached OD2</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+       <state name="OD1">
+           <condition>
+               <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+                   <unit>DAYS</unit><number>30</number>
+               </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+               <controlTagExclusion>TEST</controlTagExclusion>
+           </condition>
+           <externalMessage>Reached OD1</externalMessage>
+           <blockChanges>true</blockChanges>
+           <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+           <autoReevaluationInterval>
+               <unit>DAYS</unit><number>5</number>
+           </autoReevaluationInterval>
+       </state>
+   </accountOverdueStates>
+</overdueConfig>
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 49abcfc..87a8077 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killpay</artifactId>
@@ -41,7 +41,7 @@
             <!--Needed by jmxutils-->
             <groupId>com.google.inject.extensions</groupId>
             <artifactId>guice-multibindings</artifactId>
-            <scope>compile</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>com.google.inject.extensions</groupId>
@@ -62,6 +62,7 @@
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-servlet</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
@@ -151,6 +152,7 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-common</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
@@ -164,12 +166,25 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-postgresql</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
+            <scope>runtime</scope>
         </dependency>
     </dependencies>
     <build>
diff --git a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
index 389b1fb..ef0ce9e 100644
--- a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
+++ b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
@@ -25,6 +25,7 @@ import org.killbill.billing.catalog.glue.CatalogModule;
 import org.killbill.billing.currency.glue.CurrencyModule;
 import org.killbill.billing.entitlement.glue.DefaultEntitlementModule;
 import org.killbill.billing.invoice.glue.DefaultInvoiceModule;
+import org.killbill.billing.jaxrs.glue.DefaultJaxrsModule;
 import org.killbill.billing.jaxrs.resources.AccountResource;
 import org.killbill.billing.jaxrs.resources.CustomFieldResource;
 import org.killbill.billing.jaxrs.resources.ExportResource;
@@ -54,6 +55,7 @@ import org.killbill.billing.util.glue.AuditModule;
 import org.killbill.billing.util.glue.BroadcastModule;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
@@ -78,6 +80,7 @@ public class KillpayServerModule extends KillbillServerModule {
         install(new BroadcastModule(configSource));
         install(new BeatrixModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
         install(new CurrencyModule(configSource));
         install(new CustomFieldModule(configSource));
@@ -103,6 +106,7 @@ public class KillpayServerModule extends KillbillServerModule {
         install(new DefaultSubscriptionModule(configSource));
         install(new TemplateModule(configSource));
         install(new UsageModule(configSource));
+        install(new DefaultJaxrsModule(configSource));
         // TODO Dependencies for AccountResource
         install(new DefaultOverdueModule(configSource));
         install(new EmailModule(configSource));
diff --git a/profiles/killpay/src/main/webapp/api.html b/profiles/killpay/src/main/webapp/api.html
index 8cd886f..e4e7e52 100644
--- a/profiles/killpay/src/main/webapp/api.html
+++ b/profiles/killpay/src/main/webapp/api.html
@@ -1,7 +1,9 @@
 <!--
   ~ Copyright 2010-2013 Ning, Inc.
+  ~ Copyright 2014-2016 Groupon, Inc
+  ~ Copyright 2014-2016 The Billing Project, LLC
   ~
-  ~ Ning licenses this file to you under the Apache License, version 2.0
+  ~ The Billing Project 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:
   ~
@@ -17,90 +19,83 @@
 <!DOCTYPE html>
 <html>
 <head>
+  <meta charset="UTF-8">
   <title>Kill Bill APIs</title>
-  <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
-  <link href='//fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'/>
+  <link rel="icon" href="images/favicon.ico" />
+  <link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
-  <link href='css/screen.css' media='print' rel='stylesheet' type='text/css'/>
+  <link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
   <link href='css/killbill-swagger.css' media='screen' rel='stylesheet' type='text/css'/>
   <link href='css/killbill-swagger.css' media='print' rel='stylesheet' type='text/css'/>
-  <script type="text/javascript" src="lib/shred.bundle.js"></script>
+
+  <script src='lib/object-assign-pollyfill.js' type='text/javascript'></script>
   <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
   <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
   <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
   <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
-  <script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>
-  <script src='lib/underscore-min.js' type='text/javascript'></script>
+  <script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
+  <script src='lib/js-yaml.min.js' type='text/javascript'></script>
+  <script src='lib/lodash.min.js' type='text/javascript'></script>
   <script src='lib/backbone-min.js' type='text/javascript'></script>
-  <script src='lib/swagger.js' type='text/javascript'></script>
-  <script src='lib/swagger-client.js' type='text/javascript'></script>
   <script src='lib/swagger-ui.min.js' type='text/javascript'></script>
-  <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
-
-  <!-- enabling this will enable oauth2 implicit scope support -->
+  <script src='lib/highlight.9.1.0.pack.js' type='text/javascript'></script>
+  <script src='lib/highlight.9.1.0.pack_extended.js' type='text/javascript'></script>
+  <script src='lib/jsoneditor.min.js' type='text/javascript'></script>
+  <script src='lib/marked.js' type='text/javascript'></script>
   <script src='lib/swagger-oauth.js' type='text/javascript'></script>
+
   <script type="text/javascript">
     $(function () {
-      var url = window.location.href.replace(/(.*)(\/[^\/]*?)$/, '$1/api-docs')
+      var url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + window.location.pathname.replace(/(.*)(\/[^\/]*?)$/, '$1/swagger.json')
+
+      hljs.configure({
+        highlightSizeThreshold: 5000
+      });
+
+      // Pre load translate...
+      if(window.SwaggerTranslator) {
+        window.SwaggerTranslator.translate();
+      }
       window.swaggerUi = new SwaggerUi({
-        url: (url || "http://127.0.0.1:8080/api-docs"),
+        url: url,
         dom_id: "swagger-ui-container",
-        supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
+        supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
         onComplete: function(swaggerApi, swaggerUi){
-          log("Loaded SwaggerUI");
           if(typeof initOAuth == "function") {
-            /*
             initOAuth({
               clientId: "your-client-id",
+              clientSecret: "your-client-secret-if-required",
               realm: "your-realms",
-              appName: "your-app-name"
+              appName: "your-app-name",
+              scopeSeparator: ",",
+              additionalQueryStringParams: {}
             });
-            */
           }
-          $('pre code').each(function(i, e) {
-            hljs.highlightBlock(e)
-          });
+
+          if(window.SwaggerTranslator) {
+            window.SwaggerTranslator.translate();
+          }
+
+          $("input.parameter[name|='X-Killbill-CreatedBy']").val('Swagger');
         },
         onFailure: function(data) {
-          log("Unable to Load SwaggerUI");
+          log("Unable to Load SwaggerUI: " + data);
         },
         docExpansion: "none",
-        sorter : "alpha"
-      });
-
-      function addApiKeyAuthorization() {
-        var key = $('#input_apiKey')[0].value;
-        log("key: " + key);
-        if(key && key.trim() != "") {
-            log("added key " + key);
-            window.authorizations.add("api_key", new ApiKeyAuthorization("api_key", key, "query"));
-        }
-      }
-
-      $('#input_apiKey').change(function() {
-        addApiKeyAuthorization();
-      });
-
-      // if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
-      /*
-        var apiKey = "myApiKeyXXXX123456789";
-        $('#input_apiKey').val(apiKey);
-        addApiKeyAuthorization();
-      */
-
-      $.each(["#input_kb_apiKey", "#input_kb_apiSecret", "#input_kb_username", "#input_kb_password"], function(idx, selector_id) {
-        $(selector_id).change(function() {
-          setHeaders();
-        });
+        apisSorter: "alpha",
+        operationsSorter: "alpha",
+        jsonEditor: false,
+        defaultModelRendering: 'schema',
+        showRequestHeaders: true
       });
 
       function setHeader(header, value) {
         if (value && value.trim() != "") {
           log("Setting header " + header + " to " + value);
-          window.authorizations.remove(header);
-          window.authorizations.add(header, new ApiKeyAuthorization(header, value, "header"));
+          window.swaggerUi.api.clientAuthorizations.remove(header);
+          window.swaggerUi.api.clientAuthorizations.add(header, new SwaggerClient.ApiKeyAuthorization(header, value, "header"));
         }
       }
 
@@ -110,9 +105,22 @@
         setHeader("Authorization", "Basic " + btoa(($("#input_kb_username")[0].value || "admin") + ":" + ($("#input_kb_password")[0].value || "password")));
       }
 
-      setHeaders();
+      $.each(["#input_kb_apiKey", "#input_kb_apiSecret", "#input_kb_username", "#input_kb_password"], function(idx, selector_id) {
+        $(selector_id).change(function() {
+          setHeaders();
+        });
+      });
+
 
       window.swaggerUi.load();
+
+      setHeaders();
+
+      function log() {
+        if ('console' in window) {
+          console.log.apply(console, arguments);
+        }
+      }
   });
   </script>
 </head>
@@ -126,17 +134,17 @@
       </a>
     </div>
     <form id='api_selector'>
-      <div class='input'><input placeholder="http://127.0.0.1:8080/api-docs" id="input_baseUrl" name="baseUrl" type="text" size="38"/></div>
+      <div class='input'><input placeholder="http://127.0.0.1:8080/swagger.json" id="input_baseUrl" name="baseUrl" type="text" size="38"/></div>
       <div class='input'><input placeholder="api_key" id="input_kb_apiKey" name="apiKey" type="text" size="8"/></div>
       <div class='input'><input placeholder="api_secret" id="input_kb_apiSecret" name="apiSecret" type="text" size="8"/></div>
       <div class='input'><input placeholder="username" id="input_kb_username" name="username" type="text" size="8"/></div>
       <div class='input'><input placeholder="password" id="input_kb_password" name="password" type="text" size="8"/></div>
-      <div class='input'><a id="explore" href="#">Explore</a></div>
+      <div class='input'><a id="explore" class="header__btn" href="#" data-sw-translate>Explore</a></div>
     </form>
   </div>
 </div>
 
-<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
+<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
 <div id="swagger-ui-container" class="swagger-ui-wrap"></div>
 </body>
 </html>
diff --git a/profiles/killpay/src/main/webapp/css/print.css b/profiles/killpay/src/main/webapp/css/print.css
new file mode 100644
index 0000000..d4c1e70
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/css/print.css
@@ -0,0 +1,1362 @@
+/* Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org> */
+.swagger-section pre code {
+  display: block;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section pre code,
+.swagger-section pre .subst,
+.swagger-section pre .tag .title,
+.swagger-section pre .lisp .title,
+.swagger-section pre .clojure .built_in,
+.swagger-section pre .nginx .title {
+  color: black;
+}
+.swagger-section pre .string,
+.swagger-section pre .title,
+.swagger-section pre .constant,
+.swagger-section pre .parent,
+.swagger-section pre .tag .value,
+.swagger-section pre .rules .value,
+.swagger-section pre .rules .value .number,
+.swagger-section pre .preprocessor,
+.swagger-section pre .ruby .symbol,
+.swagger-section pre .ruby .symbol .string,
+.swagger-section pre .aggregate,
+.swagger-section pre .template_tag,
+.swagger-section pre .django .variable,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .addition,
+.swagger-section pre .flow,
+.swagger-section pre .stream,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .apache .cbracket,
+.swagger-section pre .tex .command,
+.swagger-section pre .tex .special,
+.swagger-section pre .erlang_repl .function_or_atom,
+.swagger-section pre .markdown .header {
+  color: #800;
+}
+.swagger-section pre .comment,
+.swagger-section pre .annotation,
+.swagger-section pre .template_comment,
+.swagger-section pre .diff .header,
+.swagger-section pre .chunk,
+.swagger-section pre .markdown .blockquote {
+  color: #888;
+}
+.swagger-section pre .number,
+.swagger-section pre .date,
+.swagger-section pre .regexp,
+.swagger-section pre .literal,
+.swagger-section pre .smalltalk .symbol,
+.swagger-section pre .smalltalk .char,
+.swagger-section pre .go .constant,
+.swagger-section pre .change,
+.swagger-section pre .markdown .bullet,
+.swagger-section pre .markdown .link_url {
+  color: #080;
+}
+.swagger-section pre .label,
+.swagger-section pre .javadoc,
+.swagger-section pre .ruby .string,
+.swagger-section pre .decorator,
+.swagger-section pre .filter .argument,
+.swagger-section pre .localvars,
+.swagger-section pre .array,
+.swagger-section pre .attr_selector,
+.swagger-section pre .important,
+.swagger-section pre .pseudo,
+.swagger-section pre .pi,
+.swagger-section pre .doctype,
+.swagger-section pre .deletion,
+.swagger-section pre .envvar,
+.swagger-section pre .shebang,
+.swagger-section pre .apache .sqbracket,
+.swagger-section pre .nginx .built_in,
+.swagger-section pre .tex .formula,
+.swagger-section pre .erlang_repl .reserved,
+.swagger-section pre .prompt,
+.swagger-section pre .markdown .link_label,
+.swagger-section pre .vhdl .attribute,
+.swagger-section pre .clojure .attribute,
+.swagger-section pre .coffeescript .property {
+  color: #88F;
+}
+.swagger-section pre .keyword,
+.swagger-section pre .id,
+.swagger-section pre .phpdoc,
+.swagger-section pre .title,
+.swagger-section pre .built_in,
+.swagger-section pre .aggregate,
+.swagger-section pre .css .tag,
+.swagger-section pre .javadoctag,
+.swagger-section pre .phpdoc,
+.swagger-section pre .yardoctag,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .winutils,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .go .typename,
+.swagger-section pre .tex .command,
+.swagger-section pre .markdown .strong,
+.swagger-section pre .request,
+.swagger-section pre .status {
+  font-weight: bold;
+}
+.swagger-section pre .markdown .emphasis {
+  font-style: italic;
+}
+.swagger-section pre .nginx .built_in {
+  font-weight: normal;
+}
+.swagger-section pre .coffeescript .javascript,
+.swagger-section pre .javascript .xml,
+.swagger-section pre .tex .formula,
+.swagger-section pre .xml .javascript,
+.swagger-section pre .xml .vbscript,
+.swagger-section pre .xml .css,
+.swagger-section pre .xml .cdata {
+  opacity: 0.5;
+}
+.swagger-section .hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section .hljs,
+.swagger-section .hljs-subst {
+  color: #444;
+}
+.swagger-section .hljs-keyword,
+.swagger-section .hljs-attribute,
+.swagger-section .hljs-selector-tag,
+.swagger-section .hljs-meta-keyword,
+.swagger-section .hljs-doctag,
+.swagger-section .hljs-name {
+  font-weight: bold;
+}
+.swagger-section .hljs-built_in,
+.swagger-section .hljs-literal,
+.swagger-section .hljs-bullet,
+.swagger-section .hljs-code,
+.swagger-section .hljs-addition {
+  color: #1F811F;
+}
+.swagger-section .hljs-regexp,
+.swagger-section .hljs-symbol,
+.swagger-section .hljs-variable,
+.swagger-section .hljs-template-variable,
+.swagger-section .hljs-link,
+.swagger-section .hljs-selector-attr,
+.swagger-section .hljs-selector-pseudo {
+  color: #BC6060;
+}
+.swagger-section .hljs-type,
+.swagger-section .hljs-string,
+.swagger-section .hljs-number,
+.swagger-section .hljs-selector-id,
+.swagger-section .hljs-selector-class,
+.swagger-section .hljs-quote,
+.swagger-section .hljs-template-tag,
+.swagger-section .hljs-deletion {
+  color: #880000;
+}
+.swagger-section .hljs-title,
+.swagger-section .hljs-section {
+  color: #880000;
+  font-weight: bold;
+}
+.swagger-section .hljs-comment {
+  color: #888888;
+}
+.swagger-section .hljs-meta {
+  color: #2B6EA1;
+}
+.swagger-section .hljs-emphasis {
+  font-style: italic;
+}
+.swagger-section .hljs-strong {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap {
+  line-height: 1;
+  font-family: "Droid Sans", sans-serif;
+  min-width: 760px;
+  max-width: 960px;
+  margin-left: auto;
+  margin-right: auto;
+  /* JSONEditor specific styling */
+}
+.swagger-section .swagger-ui-wrap b,
+.swagger-section .swagger-ui-wrap strong {
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap q,
+.swagger-section .swagger-ui-wrap blockquote {
+  quotes: none;
+}
+.swagger-section .swagger-ui-wrap p {
+  line-height: 1.4em;
+  padding: 0 0 10px;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap q:before,
+.swagger-section .swagger-ui-wrap q:after,
+.swagger-section .swagger-ui-wrap blockquote:before,
+.swagger-section .swagger-ui-wrap blockquote:after {
+  content: none;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu h1,
+.swagger-section .swagger-ui-wrap .heading_with_menu h2,
+.swagger-section .swagger-ui-wrap .heading_with_menu h3,
+.swagger-section .swagger-ui-wrap .heading_with_menu h4,
+.swagger-section .swagger-ui-wrap .heading_with_menu h5,
+.swagger-section .swagger-ui-wrap .heading_with_menu h6 {
+  display: block;
+  clear: none;
+  float: left;
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  -ms-box-sizing: border-box;
+  box-sizing: border-box;
+  width: 60%;
+}
+.swagger-section .swagger-ui-wrap table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+.swagger-section .swagger-ui-wrap table thead tr th {
+  padding: 5px;
+  font-size: 0.9em;
+  color: #666666;
+  border-bottom: 1px solid #999999;
+}
+.swagger-section .swagger-ui-wrap table tbody tr:last-child td {
+  border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap table tbody tr.offset {
+  background-color: #f0f0f0;
+}
+.swagger-section .swagger-ui-wrap table tbody tr td {
+  padding: 6px;
+  font-size: 0.9em;
+  border-bottom: 1px solid #cccccc;
+  vertical-align: top;
+  line-height: 1.3em;
+}
+.swagger-section .swagger-ui-wrap ol {
+  margin: 0px 0 10px;
+  padding: 0 0 0 18px;
+  list-style-type: decimal;
+}
+.swagger-section .swagger-ui-wrap ol li {
+  padding: 5px 0px;
+  font-size: 0.9em;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap ol,
+.swagger-section .swagger-ui-wrap ul {
+  list-style: none;
+}
+.swagger-section .swagger-ui-wrap h1 a,
+.swagger-section .swagger-ui-wrap h2 a,
+.swagger-section .swagger-ui-wrap h3 a,
+.swagger-section .swagger-ui-wrap h4 a,
+.swagger-section .swagger-ui-wrap h5 a,
+.swagger-section .swagger-ui-wrap h6 a {
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap h1 a:hover,
+.swagger-section .swagger-ui-wrap h2 a:hover,
+.swagger-section .swagger-ui-wrap h3 a:hover,
+.swagger-section .swagger-ui-wrap h4 a:hover,
+.swagger-section .swagger-ui-wrap h5 a:hover,
+.swagger-section .swagger-ui-wrap h6 a:hover {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap h1 span.divider,
+.swagger-section .swagger-ui-wrap h2 span.divider,
+.swagger-section .swagger-ui-wrap h3 span.divider,
+.swagger-section .swagger-ui-wrap h4 span.divider,
+.swagger-section .swagger-ui-wrap h5 span.divider,
+.swagger-section .swagger-ui-wrap h6 span.divider {
+  color: #aaaaaa;
+}
+.swagger-section .swagger-ui-wrap a {
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap a img {
+  border: none;
+}
+.swagger-section .swagger-ui-wrap article,
+.swagger-section .swagger-ui-wrap aside,
+.swagger-section .swagger-ui-wrap details,
+.swagger-section .swagger-ui-wrap figcaption,
+.swagger-section .swagger-ui-wrap figure,
+.swagger-section .swagger-ui-wrap footer,
+.swagger-section .swagger-ui-wrap header,
+.swagger-section .swagger-ui-wrap hgroup,
+.swagger-section .swagger-ui-wrap menu,
+.swagger-section .swagger-ui-wrap nav,
+.swagger-section .swagger-ui-wrap section,
+.swagger-section .swagger-ui-wrap summary {
+  display: block;
+}
+.swagger-section .swagger-ui-wrap pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap pre code {
+  line-height: 1.6em;
+  background: none;
+}
+.swagger-section .swagger-ui-wrap .content > .content-type > div > label {
+  clear: both;
+  display: block;
+  color: #0F6AB4;
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap .content pre {
+  font-size: 12px;
+  margin-top: 5px;
+  padding: 5px;
+}
+.swagger-section .swagger-ui-wrap .icon-btn {
+  cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .info_title {
+  padding-bottom: 10px;
+  font-weight: bold;
+  font-size: 25px;
+}
+.swagger-section .swagger-ui-wrap .footer {
+  margin-top: 20px;
+}
+.swagger-section .swagger-ui-wrap p.big,
+.swagger-section .swagger-ui-wrap div.big p {
+  font-size: 1em;
+  margin-bottom: 10px;
+}
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input {
+  width: 500px !important;
+}
+.swagger-section .swagger-ui-wrap .info_license {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_tos {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .message-fail {
+  color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap .info_url {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_description {
+  padding-bottom: 10px;
+  font-size: 15px;
+}
+.swagger-section .swagger-ui-wrap .markdown ol li,
+.swagger-section .swagger-ui-wrap .markdown ul li {
+  padding: 3px 0px;
+  line-height: 1.4em;
+  color: #333333;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input {
+  display: block;
+  padding: 4px;
+  width: auto;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title {
+  font-size: 1.3em;
+}
+.swagger-section .swagger-ui-wrap table.fullwidth {
+  width: 100%;
+}
+.swagger-section .swagger-ui-wrap .model-signature {
+  font-family: "Droid Sans", sans-serif;
+  font-size: 1em;
+  line-height: 1.5em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a {
+  text-decoration: none;
+  color: #AAA;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover {
+  text-decoration: underline;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected {
+  color: black;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propType {
+  color: #5555aa;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre:hover {
+  background-color: #ffffdd;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+  font-size: .85em;
+  line-height: 1.2em;
+  overflow: auto;
+  max-height: 200px;
+  cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
+  display: block;
+  min-width: 230px;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li {
+  float: left;
+  margin: 0 5px 5px 0;
+  padding: 2px 5px 2px 0;
+  border-right: 1px solid #ddd;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOpt {
+  color: #555;
+}
+.swagger-section .swagger-ui-wrap .model-signature .snippet small {
+  font-size: 0.75em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOptKey {
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .strong {
+  font-weight: bold;
+  color: #000;
+  font-size: .9em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description div {
+  font-size: 0.9em;
+  line-height: 1.5em;
+  margin-left: 1em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .stronger {
+  font-weight: bold;
+  color: #000;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+  border-spacing: 0;
+  position: absolute;
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+  display: none;
+  font-size: 11px;
+  max-width: 400px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+  margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+  text-align: center;
+  background-color: #eeeeee;
+  border: 1px solid #bbbbbb;
+  font-size: 11px;
+  color: #666666;
+  font-weight: bold;
+  padding: 5px;
+  line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child,
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child {
+  display: inline;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before {
+  display: block;
+  content: '';
+}
+.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child {
+  margin-right: -3px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-container {
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+  width: 300px;
+  height: 100px;
+  border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap .markdown p code,
+.swagger-section .swagger-ui-wrap .markdown li code {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #f0f0f0;
+  color: black;
+  padding: 1px 3px;
+}
+.swagger-section .swagger-ui-wrap .required {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .editor_holder {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap .editor_holder label {
+  font-weight: normal!important;
+  /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */
+}
+.swagger-section .swagger-ui-wrap .editor_holder label.required {
+  font-weight: bold!important;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+  width: 300px;
+  border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap h1 {
+  color: black;
+  font-size: 1.5em;
+  line-height: 1.3em;
+  padding: 10px 0 10px 0;
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu ul {
+  display: block;
+  clear: none;
+  float: right;
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  -ms-box-sizing: border-box;
+  box-sizing: border-box;
+  margin-top: 10px;
+}
+.swagger-section .swagger-ui-wrap h2 {
+  color: black;
+  font-size: 1.3em;
+  padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap h2 a {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub {
+  font-size: 0.7em;
+  color: #999999;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub a {
+  color: #777777;
+}
+.swagger-section .swagger-ui-wrap span.weak {
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap .message-success {
+  color: #89BF04;
+}
+.swagger-section .swagger-ui-wrap caption,
+.swagger-section .swagger-ui-wrap th,
+.swagger-section .swagger-ui-wrap td {
+  text-align: left;
+  font-weight: normal;
+  vertical-align: middle;
+}
+.swagger-section .swagger-ui-wrap .code {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea {
+  font-family: "Droid Sans", sans-serif;
+  height: 250px;
+  padding: 4px;
+  display: block;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select {
+  display: block;
+  clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label {
+  display: block;
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input {
+  display: block;
+  float: left;
+  clear: none;
+  margin: 0 5px 0 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label {
+  display: block;
+  clear: both;
+  width: auto;
+  padding: 0 0 3px;
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr {
+  padding-left: 3px;
+  color: #888888;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints {
+  margin-left: 0;
+  font-style: italic;
+  font-size: 0.9em;
+  margin: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons {
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap span.blank,
+.swagger-section .swagger-ui-wrap span.empty {
+  color: #888888;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .markdown h3 {
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap .markdown h4 {
+  color: #666666;
+}
+.swagger-section .swagger-ui-wrap .markdown pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  padding: 10px;
+  margin: 0 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown pre code {
+  line-height: 1.6em;
+  overflow: auto;
+}
+.swagger-section .swagger-ui-wrap div.gist {
+  margin: 20px 0 25px 0 !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources {
+  font-family: "Droid Sans", sans-serif;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource {
+  border-bottom: 1px solid #dddddd;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a {
+  color: #555555;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child {
+  border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading {
+  border: 1px solid transparent;
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+  overflow: hidden;
+  padding: 0;
+  display: block;
+  clear: none;
+  float: right;
+  margin: 14px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li {
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 2px 10px;
+  border-right: 1px solid #dddddd;
+  color: #666666;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a {
+  color: #aaaaaa;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover {
+  text-decoration: underline;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+  color: #999999;
+  padding-left: 0;
+  display: block;
+  clear: none;
+  float: left;
+  font-family: "Droid Sans", sans-serif;
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+  color: #999999;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+  margin: 0 0 10px;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 {
+  display: block;
+  clear: none;
+  float: left;
+  width: auto;
+  margin: 0;
+  padding: 0;
+  line-height: 1.1em;
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path {
+  padding-left: 10px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a {
+  color: black;
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated {
+  text-decoration: line-through;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  text-decoration: none;
+  color: white;
+  display: inline-block;
+  width: 50px;
+  font-size: 0.7em;
+  text-align: center;
+  padding: 7px 0 4px;
+  -moz-border-radius: 2px;
+  -webkit-border-radius: 2px;
+  -o-border-radius: 2px;
+  -ms-border-radius: 2px;
+  -khtml-border-radius: 2px;
+  border-radius: 2px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span {
+  margin: 0;
+  padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options {
+  overflow: hidden;
+  padding: 0;
+  display: block;
+  clear: none;
+  float: right;
+  margin: 6px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li {
+  float: left;
+  clear: none;
+  margin: 0;
+  padding: 2px 10px;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
+  text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+  border-top: none;
+  padding: 10px;
+  -moz-border-radius-bottomleft: 6px;
+  -webkit-border-bottom-left-radius: 6px;
+  -o-border-bottom-left-radius: 6px;
+  -ms-border-bottom-left-radius: 6px;
+  -khtml-border-bottom-left-radius: 6px;
+  border-bottom-left-radius: 6px;
+  -moz-border-radius-bottomright: 6px;
+  -webkit-border-bottom-right-radius: 6px;
+  -o-border-bottom-right-radius: 6px;
+  -ms-border-bottom-right-radius: 6px;
+  -khtml-border-bottom-right-radius: 6px;
+  border-bottom-right-radius: 6px;
+  margin: 0 0 20px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 {
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header {
+  float: none;
+  clear: both;
+  overflow: hidden;
+  display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a {
+  padding: 4px 0 0 10px;
+  display: inline-block;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit {
+  display: block;
+  clear: none;
+  float: left;
+  padding: 6px 8px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
+  background-image: url('../images/throbber.gif');
+  width: 128px;
+  height: 16px;
+  display: block;
+  clear: none;
+  float: right;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error {
+  outline: 2px solid black;
+  outline-color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] {
+  max-width: 300px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  padding: 10px;
+  font-size: 0.9em;
+  max-height: 400px;
+  overflow-y: auto;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading {
+  background-color: #f9f2e9;
+  border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a {
+  background-color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #f0e0ca;
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a {
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content {
+  background-color: #faf5ee;
+  border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 {
+  color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a {
+  color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading {
+  background-color: #fcffcd;
+  border: 1px solid black;
+  border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  background-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #ffd20f;
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a {
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content {
+  background-color: #fcffcd;
+  border: 1px solid black;
+  border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 {
+  color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a {
+  color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading {
+  background-color: #f5e8e8;
+  border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a {
+  text-transform: uppercase;
+  background-color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #e8c6c7;
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a {
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+  background-color: #f7eded;
+  border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 {
+  color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a {
+  color: #c8787a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading {
+  background-color: #e7f6ec;
+  border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a {
+  background-color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3e8d1;
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a {
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content {
+  background-color: #ebf7f0;
+  border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 {
+  color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a {
+  color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading {
+  background-color: #FCE9E3;
+  border: 1px solid #F5D5C3;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a {
+  background-color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #f0cecb;
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a {
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content {
+  background-color: #faf0ef;
+  border: 1px solid #f0cecb;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 {
+  color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a {
+  color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading {
+  background-color: #e7f0f7;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a {
+  background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3d9ec;
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a {
+  color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading {
+  background-color: #e7f0f7;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a {
+  background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li {
+  border-right: 1px solid #dddddd;
+  border-right-color: #c3d9ec;
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 {
+  color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a {
+  color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+  border-top: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last {
+  padding-right: 0;
+  border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active {
+  text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first {
+  padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap p#colophon {
+  margin: 0 15px 40px 15px;
+  padding: 10px 0;
+  font-size: 0.8em;
+  border-top: 1px solid #dddddd;
+  font-family: "Droid Sans", sans-serif;
+  color: #999999;
+  font-style: italic;
+}
+.swagger-section .swagger-ui-wrap p#colophon a {
+  text-decoration: none;
+  color: #547f00;
+}
+.swagger-section .swagger-ui-wrap h3 {
+  color: black;
+  font-size: 1.1em;
+  padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown ol,
+.swagger-section .swagger-ui-wrap .markdown ul {
+  font-family: "Droid Sans", sans-serif;
+  margin: 5px 0 10px;
+  padding: 0 0 0 18px;
+  list-style-type: disc;
+}
+.swagger-section .swagger-ui-wrap form.form_box {
+  background-color: #ebf3f9;
+  border: 1px solid #c3d9ec;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box label {
+  color: #0f6ab4 !important;
+}
+.swagger-section .swagger-ui-wrap form.form_box input[type=submit] {
+  display: block;
+  padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box p.weak {
+  font-size: 0.8em;
+}
+.swagger-section .swagger-ui-wrap form.form_box p {
+  font-size: 0.9em;
+  padding: 0 0 15px;
+  color: #7e7b6d;
+}
+.swagger-section .swagger-ui-wrap form.form_box p a {
+  color: #646257;
+}
+.swagger-section .swagger-ui-wrap form.form_box p strong {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child {
+  padding-bottom: 0;
+}
+.swagger-section .title {
+  font-style: bold;
+}
+.swagger-section .secondary_form {
+  display: none;
+}
+.swagger-section .main_image {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+.swagger-section .oauth_body {
+  margin-left: 100px;
+  margin-right: 100px;
+}
+.swagger-section .oauth_submit {
+  text-align: center;
+  display: inline-block;
+}
+.swagger-section .authorize-wrapper {
+  margin: 15px 0 10px;
+}
+.swagger-section .authorize-wrapper_operation {
+  float: right;
+}
+.swagger-section .authorize__btn:hover {
+  text-decoration: underline;
+  cursor: pointer;
+}
+.swagger-section .authorize__btn_operation:hover .authorize-scopes {
+  display: block;
+}
+.swagger-section .authorize-scopes {
+  position: absolute;
+  margin-top: 20px;
+  background: #FFF;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  display: none;
+  font-size: 13px;
+  max-width: 300px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+}
+.swagger-section .authorize-scopes .authorize__scope {
+  text-decoration: none;
+}
+.swagger-section .authorize__btn_operation {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .authorize__btn_operation_login {
+  background-position: 0 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section .authorize__btn_operation_logout {
+  background-position: -30px 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section #auth_container {
+  color: #fff;
+  display: inline-block;
+  border: none;
+  padding: 5px;
+  width: 87px;
+  height: 13px;
+}
+.swagger-section #auth_container .authorize__btn {
+  color: #fff;
+}
+.swagger-section .auth_container {
+  padding: 0 0 10px;
+  margin-bottom: 5px;
+  border-bottom: solid 1px #CCC;
+  font-size: 0.9em;
+}
+.swagger-section .auth_container .auth__title {
+  color: #547f00;
+  font-size: 1.2em;
+}
+.swagger-section .auth_container .basic_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .auth_container .auth__description {
+  color: #999999;
+  margin-bottom: 5px;
+}
+.swagger-section .auth_container .auth__button {
+  margin-top: 10px;
+  height: 30px;
+}
+.swagger-section .auth_container .key_auth__field {
+  margin: 5px 0;
+}
+.swagger-section .auth_container .key_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .api-popup-dialog {
+  position: absolute;
+  display: none;
+}
+.swagger-section .api-popup-dialog-wrapper {
+  z-index: 1000;
+  width: 500px;
+  background: #FFF;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  font-size: 13px;
+  color: #777;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+.swagger-section .api-popup-dialog-shadow {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  opacity: 0.2;
+  background-color: gray;
+  z-index: 900;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+  font-size: 24px;
+  padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+  font-size: 24px;
+  padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .error-msg {
+  padding-left: 5px;
+  padding-bottom: 5px;
+}
+.swagger-section .api-popup-dialog .api-popup-content {
+  max-height: 500px;
+  overflow-y: auto;
+}
+.swagger-section .api-popup-dialog .api-popup-authbtn {
+  height: 30px;
+}
+.swagger-section .api-popup-dialog .api-popup-cancel {
+  height: 30px;
+}
+.swagger-section .api-popup-scopes {
+  padding: 10px 20px;
+}
+.swagger-section .api-popup-scopes li {
+  padding: 5px 0;
+  line-height: 20px;
+}
+.swagger-section .api-popup-scopes li input {
+  position: relative;
+  top: 2px;
+}
+.swagger-section .api-popup-scopes .api-scope-desc {
+  padding-left: 20px;
+  font-style: italic;
+}
+.swagger-section .api-popup-actions {
+  padding-top: 10px;
+}
+#header {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+  max-height: none;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+  width: 100px;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+  width: 100px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints {
+  display: block !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+  display: block !important;
+}
diff --git a/profiles/killpay/src/main/webapp/css/screen.css b/profiles/killpay/src/main/webapp/css/screen.css
index 478b998..9d680e2 100644
--- a/profiles/killpay/src/main/webapp/css/screen.css
+++ b/profiles/killpay/src/main/webapp/css/screen.css
@@ -82,7 +82,7 @@
 .swagger-section pre .vhdl .attribute,
 .swagger-section pre .clojure .attribute,
 .swagger-section pre .coffeescript .property {
-  color: #8888ff;
+  color: #88F;
 }
 .swagger-section pre .keyword,
 .swagger-section pre .id,
@@ -120,12 +120,75 @@
 .swagger-section pre .xml .cdata {
   opacity: 0.5;
 }
+.swagger-section .hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #F0F0F0;
+}
+.swagger-section .hljs,
+.swagger-section .hljs-subst {
+  color: #444;
+}
+.swagger-section .hljs-keyword,
+.swagger-section .hljs-attribute,
+.swagger-section .hljs-selector-tag,
+.swagger-section .hljs-meta-keyword,
+.swagger-section .hljs-doctag,
+.swagger-section .hljs-name {
+  font-weight: bold;
+}
+.swagger-section .hljs-built_in,
+.swagger-section .hljs-literal,
+.swagger-section .hljs-bullet,
+.swagger-section .hljs-code,
+.swagger-section .hljs-addition {
+  color: #1F811F;
+}
+.swagger-section .hljs-regexp,
+.swagger-section .hljs-symbol,
+.swagger-section .hljs-variable,
+.swagger-section .hljs-template-variable,
+.swagger-section .hljs-link,
+.swagger-section .hljs-selector-attr,
+.swagger-section .hljs-selector-pseudo {
+  color: #BC6060;
+}
+.swagger-section .hljs-type,
+.swagger-section .hljs-string,
+.swagger-section .hljs-number,
+.swagger-section .hljs-selector-id,
+.swagger-section .hljs-selector-class,
+.swagger-section .hljs-quote,
+.swagger-section .hljs-template-tag,
+.swagger-section .hljs-deletion {
+  color: #880000;
+}
+.swagger-section .hljs-title,
+.swagger-section .hljs-section {
+  color: #880000;
+  font-weight: bold;
+}
+.swagger-section .hljs-comment {
+  color: #888888;
+}
+.swagger-section .hljs-meta {
+  color: #2B6EA1;
+}
+.swagger-section .hljs-emphasis {
+  font-style: italic;
+}
+.swagger-section .hljs-strong {
+  font-weight: bold;
+}
 .swagger-section .swagger-ui-wrap {
   line-height: 1;
   font-family: "Droid Sans", sans-serif;
+  min-width: 760px;
   max-width: 960px;
   margin-left: auto;
   margin-right: auto;
+  /* JSONEditor specific styling */
 }
 .swagger-section .swagger-ui-wrap b,
 .swagger-section .swagger-ui-wrap strong {
@@ -274,6 +337,9 @@
   font-weight: bold;
   font-size: 25px;
 }
+.swagger-section .swagger-ui-wrap .footer {
+  margin-top: 20px;
+}
 .swagger-section .swagger-ui-wrap p.big,
 .swagger-section .swagger-ui-wrap div.big p {
   font-size: 1em;
@@ -294,7 +360,13 @@
 .swagger-section .swagger-ui-wrap .message-fail {
   color: #cc0000;
 }
-.swagger-section .swagger-ui-wrap .info_contact {
+.swagger-section .swagger-ui-wrap .info_url {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+  padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
   padding-bottom: 5px;
 }
 .swagger-section .swagger-ui-wrap .info_description {
@@ -355,6 +427,7 @@
 }
 .swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
   display: block;
+  min-width: 230px;
   margin: 0;
   padding: 0;
 }
@@ -391,6 +464,43 @@
   font-weight: bold;
   color: #000;
 }
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+  border-spacing: 0;
+  position: absolute;
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+  display: none;
+  font-size: 11px;
+  max-width: 400px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+  margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+  text-align: center;
+  background-color: #eeeeee;
+  border: 1px solid #bbbbbb;
+  font-size: 11px;
+  color: #666666;
+  font-weight: bold;
+  padding: 5px;
+  line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+  font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:first-child,
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:last-child {
+  display: inline;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propDesc.markdown > p:not(:first-child):before {
+  display: block;
+  content: '';
+}
+.swagger-section .swagger-ui-wrap .model-signature .description span:last-of-type.propDesc.markdown > p:only-child {
+  margin-right: -3px;
+}
 .swagger-section .swagger-ui-wrap .model-signature .propName {
   font-weight: bold;
 }
@@ -412,6 +522,17 @@
 .swagger-section .swagger-ui-wrap .required {
   font-weight: bold;
 }
+.swagger-section .swagger-ui-wrap .editor_holder {
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap .editor_holder label {
+  font-weight: normal!important;
+  /* JSONEditor uses bold by default for all labels, we revert that back to normal to not give the impression that by default fields are required */
+}
+.swagger-section .swagger-ui-wrap .editor_holder label.required {
+  font-weight: bold!important;
+}
 .swagger-section .swagger-ui-wrap input.parameter {
   width: 300px;
   border: 1px solid #aaa;
@@ -546,6 +667,7 @@
 }
 .swagger-section .swagger-ui-wrap .markdown pre code {
   line-height: 1.6em;
+  overflow: auto;
 }
 .swagger-section .swagger-ui-wrap div.gist {
   margin: 20px 0 25px 0 !important;
@@ -666,6 +788,9 @@
   color: black;
   text-decoration: none;
 }
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a.toggleOperation.deprecated {
+  text-decoration: line-through;
+}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
   text-decoration: underline;
 }
@@ -761,6 +886,9 @@
   outline: 2px solid black;
   outline-color: #cc0000;
 }
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form select[name='parameterContentType'] {
+  max-width: 300px;
+}
 .swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
   font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
   padding: 10px;
@@ -1034,6 +1162,9 @@
 .swagger-section .swagger-ui-wrap form.form_box p strong {
   color: black;
 }
+.swagger-section .swagger-ui-wrap .operation-status td.markdown > p:last-child {
+  padding-bottom: 0;
+}
 .swagger-section .title {
   font-style: bold;
 }
@@ -1051,18 +1182,122 @@
 }
 .swagger-section .oauth_submit {
   text-align: center;
+  display: inline-block;
+}
+.swagger-section .authorize-wrapper {
+  margin: 15px 0 10px;
+}
+.swagger-section .authorize-wrapper_operation {
+  float: right;
+}
+.swagger-section .authorize__btn:hover {
+  text-decoration: underline;
+  cursor: pointer;
+}
+.swagger-section .authorize__btn_operation:hover .authorize-scopes {
+  display: block;
+}
+.swagger-section .authorize-scopes {
+  position: absolute;
+  margin-top: 20px;
+  background: #FFF;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  display: none;
+  font-size: 13px;
+  max-width: 300px;
+  line-height: 30px;
+  color: black;
+  padding: 5px;
+}
+.swagger-section .authorize-scopes .authorize__scope {
+  text-decoration: none;
+}
+.swagger-section .authorize__btn_operation {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .authorize__btn_operation_login {
+  background-position: 0 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section .authorize__btn_operation_logout {
+  background-position: -30px 0;
+  width: 18px;
+  margin-top: -6px;
+  margin-left: 4px;
+}
+.swagger-section #auth_container {
+  color: #fff;
+  display: inline-block;
+  border: none;
+  padding: 5px;
+  width: 87px;
+  height: 13px;
+}
+.swagger-section #auth_container .authorize__btn {
+  color: #fff;
+}
+.swagger-section .auth_container {
+  padding: 0 0 10px;
+  margin-bottom: 5px;
+  border-bottom: solid 1px #CCC;
+  font-size: 0.9em;
+}
+.swagger-section .auth_container .auth__title {
+  color: #547f00;
+  font-size: 1.2em;
+}
+.swagger-section .auth_container .basic_auth__label {
+  display: inline-block;
+  width: 60px;
+}
+.swagger-section .auth_container .auth__description {
+  color: #999999;
+  margin-bottom: 5px;
+}
+.swagger-section .auth_container .auth__button {
+  margin-top: 10px;
+  height: 30px;
+}
+.swagger-section .auth_container .key_auth__field {
+  margin: 5px 0;
+}
+.swagger-section .auth_container .key_auth__label {
+  display: inline-block;
+  width: 60px;
 }
 .swagger-section .api-popup-dialog {
-  z-index: 10000;
   position: absolute;
+  display: none;
+}
+.swagger-section .api-popup-dialog-wrapper {
+  z-index: 1000;
   width: 500px;
   background: #FFF;
   padding: 20px;
   border: 1px solid #ccc;
   border-radius: 5px;
-  display: none;
   font-size: 13px;
   color: #777;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+.swagger-section .api-popup-dialog-shadow {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  opacity: 0.2;
+  background-color: gray;
+  z-index: 900;
 }
 .swagger-section .api-popup-dialog .api-popup-title {
   font-size: 24px;
@@ -1072,14 +1307,18 @@
   font-size: 24px;
   padding: 10px 0;
 }
-.swagger-section .api-popup-dialog p.error-msg {
+.swagger-section .api-popup-dialog .error-msg {
   padding-left: 5px;
   padding-bottom: 5px;
 }
-.swagger-section .api-popup-dialog button.api-popup-authbtn {
+.swagger-section .api-popup-dialog .api-popup-content {
+  max-height: 500px;
+  overflow-y: auto;
+}
+.swagger-section .api-popup-dialog .api-popup-authbtn {
   height: 30px;
 }
-.swagger-section .api-popup-dialog button.api-popup-cancel {
+.swagger-section .api-popup-dialog .api-popup-cancel {
   height: 30px;
 }
 .swagger-section .api-popup-scopes {
@@ -1089,14 +1328,14 @@
   padding: 5px 0;
   line-height: 20px;
 }
-.swagger-section .api-popup-scopes .api-scope-desc {
-  padding-left: 20px;
-  font-style: italic;
-}
 .swagger-section .api-popup-scopes li input {
   position: relative;
   top: 2px;
 }
+.swagger-section .api-popup-scopes .api-scope-desc {
+  padding-left: 20px;
+  font-style: italic;
+}
 .swagger-section .api-popup-actions {
   padding-top: 10px;
 }
@@ -1106,8 +1345,16 @@
 .swagger-section .auth {
   float: right;
 }
-.swagger-section #api_information_panel {
-  position: absolute;
+.swagger-section .api-ic {
+  height: 18px;
+  vertical-align: middle;
+  display: inline-block;
+  background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .api-ic .api_information_panel {
+  position: relative;
+  margin-top: 20px;
+  margin-left: -5px;
   background: #FFF;
   border: 1px solid #ccc;
   border-radius: 5px;
@@ -1118,34 +1365,32 @@
   color: black;
   padding: 5px;
 }
-.swagger-section #api_information_panel p .api-msg-enabled {
+.swagger-section .api-ic .api_information_panel p .api-msg-enabled {
   color: green;
 }
-.swagger-section #api_information_panel p .api-msg-disabled {
+.swagger-section .api-ic .api_information_panel p .api-msg-disabled {
   color: red;
 }
-.swagger-section .api-ic {
-  height: 18px;
-  vertical-align: middle;
-  display: inline-block;
-  background: url(../images/explorer_icons.png) no-repeat;
+.swagger-section .api-ic:hover .api_information_panel {
+  position: absolute;
+  display: block;
 }
 .swagger-section .ic-info {
   background-position: 0 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-warning {
   background-position: -60px 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-error {
   background-position: -30px 0;
   width: 18px;
-  margin-top: -7px;
+  margin-top: -6px;
   margin-left: 4px;
 }
 .swagger-section .ic-off {
@@ -1162,34 +1407,33 @@
 }
 .swagger-section #header {
   background-color: #89bf04;
-  padding: 14px;
+  padding: 9px 14px 19px 14px;
+  height: 23px;
+  min-width: 775px;
 }
-.swagger-section #header a#logo {
-  font-size: 1.5em;
-  font-weight: bold;
-  text-decoration: none;
-  background: transparent url(../images/logo_small.png) no-repeat left center;
-  padding: 20px 0 20px 40px;
-  color: white;
+.swagger-section #input_baseUrl {
+  width: 400px;
 }
-.swagger-section #header form#api_selector {
+.swagger-section #api_selector {
   display: block;
   clear: none;
   float: right;
 }
-.swagger-section #header form#api_selector .input {
-  display: block;
+.swagger-section #api_selector .input {
+  display: inline-block;
   clear: none;
-  float: left;
   margin: 0 10px 0 0;
 }
-.swagger-section #header form#api_selector .input input#input_apiKey {
-  width: 200px;
+.swagger-section #api_selector input {
+  font-size: 0.9em;
+  padding: 3px;
+  margin: 0;
 }
-.swagger-section #header form#api_selector .input input#input_baseUrl {
-  width: 400px;
+.swagger-section #input_apiKey {
+  width: 200px;
 }
-.swagger-section #header form#api_selector .input a#explore {
+.swagger-section #explore,
+.swagger-section #auth_container .authorize__btn {
   display: block;
   text-decoration: none;
   font-weight: bold;
@@ -1204,13 +1448,24 @@
   -khtml-border-radius: 4px;
   border-radius: 4px;
 }
-.swagger-section #header form#api_selector .input a#explore:hover {
+.swagger-section #explore:hover,
+.swagger-section #auth_container .authorize__btn:hover {
   background-color: #547f00;
 }
-.swagger-section #header form#api_selector .input input {
-  font-size: 0.9em;
-  padding: 3px;
-  margin: 0;
+.swagger-section #header #logo {
+  font-size: 1.5em;
+  font-weight: bold;
+  text-decoration: none;
+  color: white;
+}
+.swagger-section #header #logo .logo__img {
+  display: block;
+  float: left;
+  margin-top: 2px;
+}
+.swagger-section #header #logo .logo__title {
+  display: inline-block;
+  padding: 5px 0 0 10px;
 }
 .swagger-section #content_message {
   margin: 10px 15px;
@@ -1222,3 +1477,13 @@
   text-align: center;
   padding-top: 10px;
 }
+.swagger-section .swagger-collapse:before {
+  content: "-";
+}
+.swagger-section .swagger-expand:before {
+  content: "+";
+}
+.swagger-section .error {
+  outline-color: #cc0000;
+  background-color: #f2dede;
+}
diff --git a/profiles/killpay/src/main/webapp/css/style.css b/profiles/killpay/src/main/webapp/css/style.css
new file mode 100644
index 0000000..fc21a31
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/css/style.css
@@ -0,0 +1,250 @@
+.swagger-section #header a#logo {
+  font-size: 1.5em;
+  font-weight: bold;
+  text-decoration: none;
+  background: transparent url(../images/logo.png) no-repeat left center;
+  padding: 20px 0 20px 40px;
+}
+#text-head {
+  font-size: 80px;
+  font-family: 'Roboto', sans-serif;
+  color: #ffffff;
+  float: right;
+  margin-right: 20%;
+}
+.navbar-fixed-top .navbar-nav {
+  height: auto;
+}
+.navbar-fixed-top .navbar-brand {
+  height: auto;
+}
+.navbar-header {
+  height: auto;
+}
+.navbar-inverse {
+  background-color: #000;
+  border-color: #000;
+}
+#navbar-brand {
+  margin-left: 20%;
+}
+.navtext {
+  font-size: 10px;
+}
+.h1,
+h1 {
+  font-size: 60px;
+}
+.navbar-default .navbar-header .navbar-brand {
+  color: #a2dfee;
+}
+/* tag titles */
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+  color: #393939;
+  font-family: 'Arvo', serif;
+  font-size: 1.5em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+  color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+  color: #525252;
+  padding-left: 0px;
+  display: block;
+  clear: none;
+  float: left;
+  font-family: 'Arvo', serif;
+  font-weight: bold;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+  border-color: #0A0A0A;
+}
+.container1 {
+  width: 1500px;
+  margin: auto;
+  margin-top: 0;
+  background-image: url('../images/shield.png');
+  background-repeat: no-repeat;
+  background-position: -40px -20px;
+  margin-bottom: 210px;
+}
+.container-inner {
+  width: 1200px;
+  margin: auto;
+  background-color: rgba(223, 227, 228, 0.75);
+  padding-bottom: 40px;
+  padding-top: 40px;
+  border-radius: 15px;
+}
+.header-content {
+  padding: 0;
+  width: 1000px;
+}
+.title1 {
+  font-size: 80px;
+  font-family: 'Vollkorn', serif;
+  color: #404040;
+  text-align: center;
+  padding-top: 40px;
+  padding-bottom: 100px;
+}
+#icon {
+  margin-top: -18px;
+}
+.subtext {
+  font-size: 25px;
+  font-style: italic;
+  color: #08b;
+  text-align: right;
+  padding-right: 250px;
+}
+.bg-primary {
+  background-color: #00468b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:focus {
+  color: #08b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:hover {
+  color: #08b;
+}
+.navbar-default .nav > li > a,
+.navbar-default .nav > li > a:focus:hover {
+  color: #08b;
+}
+.text-faded {
+  font-size: 25px;
+  font-family: 'Vollkorn', serif;
+}
+.section-heading {
+  font-family: 'Vollkorn', serif;
+  font-size: 45px;
+  padding-bottom: 10px;
+}
+hr {
+  border-color: #00468b;
+  padding-bottom: 10px;
+}
+.description {
+  margin-top: 20px;
+  padding-bottom: 200px;
+}
+.description li {
+  font-family: 'Vollkorn', serif;
+  font-size: 25px;
+  color: #525252;
+  margin-left: 28%;
+  padding-top: 5px;
+}
+.gap {
+  margin-top: 200px;
+}
+.troubleshootingtext {
+  color: rgba(255, 255, 255, 0.7);
+  padding-left: 30%;
+}
+.troubleshootingtext li {
+  list-style-type: circle;
+  font-size: 25px;
+  padding-bottom: 5px;
+}
+.overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1000;
+}
+.block.response_body.json:hover {
+  cursor: pointer;
+}
+.backdrop {
+  color: blue;
+}
+#myModal {
+  height: 100%;
+}
+.modal-backdrop {
+  bottom: 0;
+  position: fixed;
+}
+.curl {
+  padding: 10px;
+  font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+  font-size: 0.9em;
+  max-height: 400px;
+  margin-top: 5px;
+  overflow-y: auto;
+  background-color: #fcf6db;
+  border: 1px solid #e5e0c6;
+  border-radius: 4px;
+}
+.curl_title {
+  font-size: 1.1em;
+  margin: 0;
+  padding: 15px 0 5px;
+  font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
+  font-weight: 500;
+  line-height: 1.1;
+}
+.footer {
+  display: none;
+}
+.swagger-section .swagger-ui-wrap h2 {
+  padding: 0;
+}
+h2 {
+  margin: 0;
+  margin-bottom: 5px;
+}
+.markdown p {
+  font-size: 15px;
+  font-family: 'Arvo', serif;
+}
+.swagger-section .swagger-ui-wrap .code {
+  font-size: 15px;
+  font-family: 'Arvo', serif;
+}
+.swagger-section .swagger-ui-wrap b {
+  font-family: 'Arvo', serif;
+}
+#signin:hover {
+  cursor: pointer;
+}
+.dropdown-menu {
+  padding: 15px;
+}
+.navbar-right .dropdown-menu {
+  left: 0;
+  right: auto;
+}
+#signinbutton {
+  width: 100%;
+  height: 32px;
+  font-size: 13px;
+  font-weight: bold;
+  color: #08b;
+}
+.navbar-default .nav > li .details {
+  color: #000000;
+  text-transform: none;
+  font-size: 15px;
+  font-weight: normal;
+  font-family: 'Open Sans', sans-serif;
+  font-style: italic;
+  line-height: 20px;
+  top: -2px;
+}
+.navbar-default .nav > li .details:hover {
+  color: black;
+}
+#signout {
+  width: 100%;
+  height: 32px;
+  font-size: 13px;
+  font-weight: bold;
+  color: #08b;
+}
diff --git a/profiles/killpay/src/main/webapp/css/typography.css b/profiles/killpay/src/main/webapp/css/typography.css
new file mode 100644
index 0000000..efb785f
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/css/typography.css
@@ -0,0 +1,14 @@
+/* Google Font's Droid Sans */
+@font-face {
+  font-family: 'Droid Sans';
+  font-style: normal;
+  font-weight: 400;
+  src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf'), format('truetype');
+}
+/* Google Font's Droid Sans Bold */
+@font-face {
+  font-family: 'Droid Sans';
+  font-style: normal;
+  font-weight: 700;
+  src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf'), format('truetype');
+}
diff --git a/profiles/killpay/src/main/webapp/fonts/DroidSans.ttf b/profiles/killpay/src/main/webapp/fonts/DroidSans.ttf
new file mode 100644
index 0000000..e517a0c
Binary files /dev/null and b/profiles/killpay/src/main/webapp/fonts/DroidSans.ttf differ
diff --git a/profiles/killpay/src/main/webapp/fonts/DroidSans-Bold.ttf b/profiles/killpay/src/main/webapp/fonts/DroidSans-Bold.ttf
new file mode 100644
index 0000000..036c4d1
Binary files /dev/null and b/profiles/killpay/src/main/webapp/fonts/DroidSans-Bold.ttf differ
diff --git a/profiles/killpay/src/main/webapp/images/throbber.gif b/profiles/killpay/src/main/webapp/images/throbber.gif
new file mode 100644
index 0000000..0639388
Binary files /dev/null and b/profiles/killpay/src/main/webapp/images/throbber.gif differ
diff --git a/profiles/killpay/src/main/webapp/lib/backbone-min.js b/profiles/killpay/src/main/webapp/lib/backbone-min.js
index c1c0d4f..a3f544b 100644
--- a/profiles/killpay/src/main/webapp/lib/backbone-min.js
+++ b/profiles/killpay/src/main/webapp/lib/backbone-min.js
@@ -1,38 +1,15 @@
-// Backbone.js 0.9.2
+// Backbone.js 1.1.2
 
-// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://backbonejs.org
-(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
-{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
-z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
-{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
-b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
-b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
-a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
-h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
-return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
-{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
-!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
-this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
-l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
-a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
-shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
-this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
-e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
-{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
-arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
-C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
-this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
-""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
-!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
-this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
-stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
-function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
-this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
-f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
-for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
-!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
-e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
-b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);
+(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});
+
+// From http://stackoverflow.com/a/19431552
+// Compatibility override - Backbone 1.1 got rid of the 'options' binding
+// automatically to views in the constructor - we need to keep that.
+Backbone.View = (function(View) {
+   return View.extend({
+        constructor: function(options) {
+            this.options = options || {};
+            View.apply(this, arguments);
+        }
+    });
+})(Backbone.View);
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/handlebars-2.0.0.js b/profiles/killpay/src/main/webapp/lib/handlebars-2.0.0.js
new file mode 100644
index 0000000..53cf921
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/handlebars-2.0.0.js
@@ -0,0 +1,28 @@
+/*!
+
+ handlebars v2.0.0
+
+Copyright (C) 2011-2014 by Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+@license
+*/
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.Handlebars=a.Handlebars||b()}(this,function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return i[a]}function c(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])Object.prototype.hasOwnProperty.call(arguments[b],c)&&(a[c]=arguments[b][c]);return a}function d(a){return a instanceof h?a.toString():null==a?"":a?(a=""+a,k.test(a)?a.replace(j,b):a):a+""}function e(a){return a||0===a?n(a)&&0===a.length?!0:!1:!0}function f(a,b){return(a?a+".":"")+b}var g={},h=a,i={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},j=/[&<>"'`]/g,k=/[&<>"'`]/;g.extend=c;var l=Object.prototype.toString;g.toString=l;var m=function(a){return"function"==typeof a};m(/x/)&&(m=function(a){return"function"==typeof a&&"[object Function]"===l.call(a)});var m;g.isFunction=m;var n=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===l.call(a):!1};return g.isArray=n,g.escapeExpression=d,g.isEmpty=e,g.appendContextPath=f,g}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f<c.length;f++)this[c[f]]=e[c[f]];d&&(this.lineNumber=d,this.column=b.firstColumn)}var b,c=["description","fileName","lineNumber","message","name","number","stack"];return a.prototype=new Error,b=a}(),d=function(a,b){"use strict";function c(a,b){this.helpers=a||{},this.partials=b||{},d(this)}function d(a){a.registerHelper("helperMissing",function(){if(1===arguments.length)return void 0;throw new g("Missing helper: '"+arguments[arguments.length-1].name+"'")}),a.registerHelper("blockHelperMissing",function(b,c){var d=c.inverse,e=c.fn;if(b===!0)return e(this);if(b===!1||null==b)return d(this);if(k(b))return b.length>0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):d(this);if(c.data&&c.ids){var g=q(c.data);g.contextPath=f.appendContextPath(c.data.contextPath,c.name),c={data:g}}return e(b,c)}),a.registerHelper("each",function(a,b){if(!b)throw new g("Must pass iterator to #each");var c,d,e=b.fn,h=b.inverse,i=0,j="";if(b.data&&b.ids&&(d=f.appendContextPath(b.data.contextPath,b.ids[0])+"."),l(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(k(a))for(var m=a.length;m>i;i++)c&&(c.index=i,c.first=0===i,c.last=i===a.length-1,d&&(c.contextPath=d+i)),j+=e(a[i],{data:c});else for(var n in a)a.hasOwnProperty(n)&&(c&&(c.key=n,c.index=i,c.first=0===i,d&&(c.contextPath=d+n)),j+=e(a[n],{data:c}),i++);return 0===i&&(j=h(this)),j}),a.registerHelper("if",function(a,b){return l(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||f.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){l(a)&&(a=a.call(this));var c=b.fn;if(f.isEmpty(a))return b.inverse(this);if(b.data&&b.ids){var d=q(b.data);d.contextPath=f.appendContextPath(b.data.contextPath,b.ids[0]),b={data:d}}return c(a,b)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)}),a.registerHelper("lookup",function(a,b){return a&&a[b]})}var e={},f=a,g=b,h="2.0.0";e.VERSION=h;var i=6;e.COMPILER_REVISION=i;var j={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=j;var k=f.isArray,l=f.isFunction,m=f.toString,n="[object Object]";e.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:o,log:p,registerHelper:function(a,b){if(m.call(a)===n){if(b)throw new g("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){m.call(a)===n?f.extend(this.partials,a):this.partials[a]=b},unregisterPartial:function(a){delete this.partials[a]}};var o={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(o.level<=a){var c=o.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};e.logger=o;var p=o.log;e.log=p;var q=function(a){var b=f.extend({},a);return b._parent=a,b};return e.createFrame=q,e}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");if(!a||!a.main)throw new l("Unknown template object: "+typeof a);b.VM.checkRevision(a.compiler);var c=function(c,d,e,f,g,h,i,j,m){g&&(f=k.extend({},f,g));var n=b.VM.invokePartial.call(this,c,e,f,h,i,j,m);if(null==n&&b.compile){var o={helpers:h,partials:i,data:j,depths:m};i[e]=b.compile(c,{data:void 0!==j,compat:a.compat},b),n=i[e](f,o)}if(null!=n){if(d){for(var p=n.split("\n"),q=0,r=p.length;r>q&&(p[q]||q+1!==r);q++)p[q]=d+p[q];n=p.join("\n")}return n}throw new l("The partial "+e+" could not be compiled when running in runtime-only mode")},d={lookup:function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d]&&null!=a[d][b])return a[d][b]},lambda:function(a,b){return"function"==typeof a?a.call(b):a},escapeExpression:k.escapeExpression,invokePartial:c,fn:function(b){return a[b]},programs:[],program:function(a,b,c){var d=this.programs[a],e=this.fn(a);return b||c?d=f(this,a,e,b,c):d||(d=this.programs[a]=f(this,a,e)),d},data:function(a,b){for(;a&&b--;)a=a._parent;return a},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c=k.extend({},b,a)),c},noop:b.VM.noop,compilerInfo:a.compiler},e=function(b,c){c=c||{};var f=c.data;e._setup(c),!c.partial&&a.useData&&(f=i(b,f));var g;return a.useDepths&&(g=c.depths?[b].concat(c.depths):[b]),a.main.call(d,b,d.helpers,d.partials,f,g)};return e.isTop=!0,e._setup=function(c){c.partial?(d.helpers=c.helpers,d.partials=c.partials):(d.helpers=d.merge(c.helpers,b.helpers),a.usePartial&&(d.partials=d.merge(c.partials,b.partials)))},e._child=function(b,c,e){if(a.useDepths&&!e)throw new l("must pass parent depths");return f(d,b,a[b],c,e)},e}function f(a,b,c,d,e){var f=function(b,f){return f=f||{},c.call(a,b,a.helpers,a.partials,f.data||d,e&&[b].concat(e))};return f.program=b,f.depth=e?e.length:0,f}function g(a,b,c,d,e,f,g){var h={partial:!0,helpers:d,partials:e,data:f,depths:g};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,h):void 0}function h(){return""}function i(a,b){return b&&"root"in b||(b=b?o(b):{},b.root=a),b}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES,o=c.createFrame;return j.checkRevision=d,j.template=e,j.program=f,j.invokePartial=g,j.noop=h,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.escapeExpression=j.escapeExpression,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,m["default"]=m,f=m}(d,a,c,b,e),g=function(a){"use strict";function b(a){a=a||{},this.firstLine=a.first_line,this.firstColumn=a.first_column,this.lastColumn=a.last_column,this.lastLine=a.last_line}var c,d=a,e={ProgramNode:function(a,c,d){b.call(this,d),this.type="program",this.statements=a,this.strip=c},MustacheNode:function(a,c,d,f,g){if(b.call(this,g),this.type="mustache",this.strip=f,null!=d&&d.charAt){var h=d.charAt(3)||d.charAt(2);this.escaped="{"!==h&&"&"!==h}else this.escaped=!!d;this.sexpr=a instanceof e.SexprNode?a:new e.SexprNode(a,c),this.id=this.sexpr.id,this.params=this.sexpr.params,this.hash=this.sexpr.hash,this.eligibleHelper=this.sexpr.eligibleHelper,this.isHelper=this.sexpr.isHelper},SexprNode:function(a,c,d){b.call(this,d),this.type="sexpr",this.hash=c;var e=this.id=a[0],f=this.params=a.slice(1);this.isHelper=!(!f.length&&!c),this.eligibleHelper=this.isHelper||e.isSimple},PartialNode:function(a,c,d,e,f){b.call(this,f),this.type="partial",this.partialName=a,this.context=c,this.hash=d,this.strip=e,this.strip.inlineStandalone=!0},BlockNode:function(a,c,d,e,f){b.call(this,f),this.type="block",this.mustache=a,this.program=c,this.inverse=d,this.strip=e,d&&!c&&(this.isInverse=!0)},RawBlockNode:function(a,c,f,g){if(b.call(this,g),a.sexpr.id.original!==f)throw new d(a.sexpr.id.original+" doesn't match "+f,this);c=new e.ContentNode(c,g),this.type="block",this.mustache=a,this.program=new e.ProgramNode([c],{},g)},ContentNode:function(a,c){b.call(this,c),this.type="content",this.original=this.string=a},HashNode:function(a,c){b.call(this,c),this.type="hash",this.pairs=a},IdNode:function(a,c){b.call(this,c),this.type="ID";for(var e="",f=[],g=0,h="",i=0,j=a.length;j>i;i++){var k=a[i].part;if(e+=(a[i].separator||"")+k,".."===k||"."===k||"this"===k){if(f.length>0)throw new d("Invalid path: "+e,this);".."===k?(g++,h+="../"):this.isScoped=!0}else f.push(k)}this.original=e,this.parts=f,this.string=f.join("."),this.depth=g,this.idName=h+this.string,this.isSimple=1===a.length&&!this.isScoped&&0===g,this.stringModeValue=this.string},PartialNameNode:function(a,c){b.call(this,c),this.type="PARTIAL_NAME",this.name=a.original},DataNode:function(a,c){b.call(this,c),this.type="DATA",this.id=a,this.stringModeValue=a.stringModeValue,this.idName="@"+a.stringModeValue},StringNode:function(a,c){b.call(this,c),this.type="STRING",this.original=this.string=this.stringModeValue=a},NumberNode:function(a,c){b.call(this,c),this.type="NUMBER",this.original=this.number=a,this.stringModeValue=Number(a)},BooleanNode:function(a,c){b.call(this,c),this.type="BOOLEAN",this.bool=a,this.stringModeValue="true"===a},CommentNode:function(a,c){b.call(this,c),this.type="comment",this.comment=a,this.strip={inlineStandalone:!0}}};return c=e}(c),h=function(){"use strict";var a,b=function(){function a(){this.yy={}}var b={trace:function(){},yy:{},symbols_:{error:2,root:3,program:4,EOF:5,program_repetition0:6,statement:7,mustache:8,block:9,rawBlock:10,partial:11,CONTENT:12,COMMENT:13,openRawBlock:14,END_RAW_BLOCK:15,OPEN_RAW_BLOCK:16,sexpr:17,CLOSE_RAW_BLOCK:18,openBlock:19,block_option0:20,closeBlock:21,openInverse:22,block_option1:23,OPEN_BLOCK:24,CLOSE:25,OPEN_INVERSE:26,inverseAndProgram:27,INVERSE:28,OPEN_ENDBLOCK:29,path:30,OPEN:31,OPEN_UNESCAPED:32,CLOSE_UNESCAPED:33,OPEN_PARTIAL:34,partialName:35,param:36,partial_option0:37,partial_option1:38,sexpr_repetition0:39,sexpr_option0:40,dataName:41,STRING:42,NUMBER:43,BOOLEAN:44,OPEN_SEXPR:45,CLOSE_SEXPR:46,hash:47,hash_repetition_plus0:48,hashSegment:49,ID:50,EQUALS:51,DATA:52,pathSegments:53,SEP:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],performAction:function(a,b,c,d,e,f){var g=f.length-1;switch(e){case 1:return d.prepareProgram(f[g-1].statements,!0),f[g-1];case 2:this.$=new d.ProgramNode(d.prepareProgram(f[g]),{},this._$);break;case 3:this.$=f[g];break;case 4:this.$=f[g];break;case 5:this.$=f[g];break;case 6:this.$=f[g];break;case 7:this.$=new d.ContentNode(f[g],this._$);break;case 8:this.$=new d.CommentNode(f[g],this._$);break;case 9:this.$=new d.RawBlockNode(f[g-2],f[g-1],f[g],this._$);break;case 10:this.$=new d.MustacheNode(f[g-1],null,"","",this._$);break;case 11:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!1,this._$);break;case 12:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!0,this._$);break;case 13:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 14:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 15:this.$={strip:d.stripFlags(f[g-1],f[g-1]),program:f[g]};break;case 16:this.$={path:f[g-1],strip:d.stripFlags(f[g-2],f[g])};break;case 17:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 18:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 19:this.$=new d.PartialNode(f[g-3],f[g-2],f[g-1],d.stripFlags(f[g-4],f[g]),this._$);break;case 20:this.$=new d.PartialNode(f[g-2],void 0,f[g-1],d.stripFlags(f[g-3],f[g]),this._$);break;case 21:this.$=new d.SexprNode([f[g-2]].concat(f[g-1]),f[g],this._$);break;case 22:this.$=new d.SexprNode([f[g]],null,this._$);break;case 23:this.$=f[g];break;case 24:this.$=new d.StringNode(f[g],this._$);break;case 25:this.$=new d.NumberNode(f[g],this._$);break;case 26:this.$=new d.BooleanNode(f[g],this._$);break;case 27:this.$=f[g];break;case 28:f[g-1].isHelper=!0,this.$=f[g-1];break;case 29:this.$=new d.HashNode(f[g],this._$);break;case 30:this.$=[f[g-2],f[g]];break;case 31:this.$=new d.PartialNameNode(f[g],this._$);break;case 32:this.$=new d.PartialNameNode(new d.StringNode(f[g],this._$),this._$);break;case 33:this.$=new d.PartialNameNode(new d.NumberNode(f[g],this._$));break;case 34:this.$=new d.DataNode(f[g],this._$);break;case 35:this.$=new d.IdNode(f[g],this._$);break;case 36:f[g-2].push({part:f[g],separator:f[g-1]}),this.$=f[g-2];break;case 37:this.$=[{part:f[g]}];break;case 38:this.$=[];break;case 39:f[g-1].push(f[g]);break;case 48:this.$=[];break;case 49:f[g-1].push(f[g]);break;case 52:this.$=[f[g]];break;case 53:f[g-1].push(f[g])}},table:[{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],defaultActions:{4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},parseError:function(a){throw new Error(a)},parse:function(a){function b(){var a;return a=c.lexer.lex()||1,"number"!=typeof a&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var l=this.lexer.yylloc;f.push(l);var m=this.lexer.options&&this.lexer.options.ranges;"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var n,o,p,q,r,s,t,u,v,w={};;){if(p=d[d.length-1],this.defaultActions[p]?q=this.defaultActions[p]:((null===n||"undefined"==typeof n)&&(n=b()),q=g[p]&&g[p][n]),"undefined"==typeof q||!q.length||!q[0]){var x="";if(!k){v=[];for(s in g[p])this.terminals_[s]&&s>2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;g<f.length&&(c=this._input.match(this.rules[f[g]]),!c||b&&!(c[0].length>b[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substr(a,b.yyleng-c)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 12;break;case 1:return 12;case 2:return this.popState(),12;case 3:return b.yytext=b.yytext.substr(5,b.yyleng-9),this.popState(),15;case 4:return 12;case 5:return e(0,4),this.popState(),13;case 6:return 45;case 7:return 46;case 8:return 16;case 9:return this.popState(),this.begin("raw"),18;case 10:return 34;case 11:return 24;case 12:return 29;case 13:return this.popState(),28;case 14:return this.popState(),28;case 15:return 26;case 16:return 26;case 17:return 32;case 18:return 31;case 19:this.popState(),this.begin("com");break;case 20:return e(3,5),this.popState(),13;case 21:return 31;case 22:return 51;case 23:return 50;case 24:return 50;case 25:return 54;case 26:break;case 27:return this.popState(),33;case 28:return this.popState(),25;case 29:return b.yytext=e(1,2).replace(/\\"/g,'"'),42;case 30:return b.yytext=e(1,2).replace(/\\'/g,"'"),42;case 31:return 52;case 32:return 44;case 33:return 44;case 34:return 43;case 35:return 50;case 36:return b.yytext=e(1,2),50;case 37:return"INVALID";case 38:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[5],inclusive:!1},raw:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,1,38],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();return a=b}(),i=function(a){"use strict";function b(a,b){return{left:"~"===a.charAt(2),right:"~"===b.charAt(b.length-3)}}function c(a,b,c,d,i,k){if(a.sexpr.id.original!==d.path.original)throw new j(a.sexpr.id.original+" doesn't match "+d.path.original,a);var l=c&&c.program,m={left:a.strip.left,right:d.strip.right,openStandalone:f(b.statements),closeStandalone:e((l||b).statements)};if(a.strip.right&&g(b.statements,null,!0),l){var n=c.strip;n.left&&h(b.statements,null,!0),n.right&&g(l.statements,null,!0),d.strip.left&&h(l.statements,null,!0),e(b.statements)&&f(l.statements)&&(h(b.statements),g(l.statements))}else d.strip.left&&h(b.statements,null,!0);return i?new this.BlockNode(a,l,b,m,k):new this.BlockNode(a,b,l,m,k)}function d(a,b){for(var c=0,d=a.length;d>c;c++){var i=a[c],j=i.strip;if(j){var k=e(a,c,b,"partial"===i.type),l=f(a,c,b),m=j.openStandalone&&k,n=j.closeStandalone&&l,o=j.inlineStandalone&&k&&l;j.right&&g(a,c,!0),j.left&&h(a,c,!0),o&&(g(a,c),h(a,c)&&"partial"===i.type&&(i.indent=/([ \t]+$)/.exec(a[c-1].original)?RegExp.$1:"")),m&&(g((i.program||i.inverse).statements),h(a,c)),n&&(g(a,c),h((i.inverse||i.program).statements))}}return a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"content"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"content"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"content"===d.type&&(c||!d.rightStripped)){var e=d.string;d.string=d.string.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.string!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"content"===d.type&&(c||!d.leftStripped)){var e=d.string;return d.string=d.string.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.string!==e,d.leftStripped}}var i={},j=a;return i.stripFlags=b,i.prepareBlock=c,i.prepareProgram=d,i}(c),j=function(a,b,c,d){"use strict";function e(a){return a.constructor===h.ProgramNode?a:(g.yy=k,g.parse(a))}var f={},g=a,h=b,i=c,j=d.extend;f.parser=g;var k={};return j(k,i,h),f.parse=e,f}(h,g,i,b),k=function(a,b){"use strict";function c(){}function d(a,b,c){if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function e(a,b,c){function d(){var d=c.parse(a),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var e,f=function(a,b){return e||(e=d()),e.call(this,a,b)};return f._setup=function(a){return e||(e=d()),e._setup(a)},f._child=function(a,b,c){return e||(e=d()),e._child(a,b,c)},f}function f(a,b){if(a===b)return!0;if(i(a)&&i(b)&&a.length===b.length){for(var c=0;c<a.length;c++)if(!f(a[c],b[c]))return!1;return!0}}var g={},h=a,i=b.isArray,j=[].slice;return g.Compiler=c,c.prototype={compiler:c,equals:function(a){var b=this.opcodes.length;if(a.opcodes.length!==b)return!1;for(var c=0;b>c;c++){var d=this.opcodes[c],e=a.opcodes[c];if(d.opcode!==e.opcode||!f(d.args,e.args))return!1}for(b=this.children.length,c=0;b>c;c++)if(!this.children[c].equals(a.children[c]))return!1;return!0},guid:0,compile:function(a,b){this.opcodes=[],this.children=[],this.depths={list:[]},this.options=b,this.stringParams=b.stringParams,this.trackIds=b.trackIds;var c=this.options.knownHelpers;if(this.options.knownHelpers={helperMissing:!0,blockHelperMissing:!0,each:!0,"if":!0,unless:!0,"with":!0,log:!0,lookup:!0},c)for(var d in c)this.options.knownHelpers[d]=c[d];return this.accept(a)},accept:function(a){return this[a.type](a)},program:function(a){for(var b=a.statements,c=0,d=b.length;d>c;c++)this.accept(b[c]);return this.isSimple=1===d,this.depths.list=this.depths.list.sort(function(a,b){return a-b}),this},compileProgram:function(a){var b,c=(new this.compiler).compile(a,this.options),d=this.guid++;
+this.usePartial=this.usePartial||c.usePartial,this.children[d]=c;for(var e=0,f=c.depths.list.length;f>e;e++)b=c.depths.list[e],2>b||this.addDepth(b-1);return d},block:function(a){var b=a.mustache,c=a.program,d=a.inverse;c&&(c=this.compileProgram(c)),d&&(d=this.compileProgram(d));var e=b.sexpr,f=this.classifySexpr(e);"helper"===f?this.helperSexpr(e,c,d):"simple"===f?(this.simpleSexpr(e),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("blockValue",e.id.original)):(this.ambiguousSexpr(e,c,d),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("ambiguousBlockValue")),this.opcode("append")},hash:function(a){var b,c,d=a.pairs;for(this.opcode("pushHash"),b=0,c=d.length;c>b;b++)this.pushParam(d[b][1]);for(;b--;)this.opcode("assignToHash",d[b][0]);this.opcode("popHash")},partial:function(a){var b=a.partialName;this.usePartial=!0,a.hash?this.accept(a.hash):this.opcode("push","undefined"),a.context?this.accept(a.context):(this.opcode("getContext",0),this.opcode("pushContext")),this.opcode("invokePartial",b.name,a.indent||""),this.opcode("append")},content:function(a){a.string&&this.opcode("appendContent",a.string)},mustache:function(a){this.sexpr(a.sexpr),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},ambiguousSexpr:function(a,b,c){var d=a.id,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),this.ID(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.id;"DATA"===b.type?this.DATA(b):b.parts.length?this.ID(b):(this.addDepth(b.depth),this.opcode("getContext",b.depth),this.opcode("pushContext")),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.id,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new h("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.falsy=!0,this.ID(e),this.opcode("invokeHelper",d.length,e.original,e.isSimple)}},sexpr:function(a){var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ID:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0];b?this.opcode("lookupOnContext",a.parts,a.falsy,a.isScoped):this.opcode("pushContext")},DATA:function(a){this.options.data=!0,this.opcode("lookupData",a.id.depth,a.id.parts)},STRING:function(a){this.opcode("pushString",a.string)},NUMBER:function(a){this.opcode("pushLiteral",a.number)},BOOLEAN:function(a){this.opcode("pushLiteral",a.bool)},comment:function(){},opcode:function(a){this.opcodes.push({opcode:a,args:j.call(arguments,1)})},addDepth:function(a){0!==a&&(this.depths[a]||(this.depths[a]=!0,this.depths.list.push(a)))},classifySexpr:function(a){var b=a.isHelper,c=a.eligibleHelper,d=this.options;if(c&&!b){var e=a.id.parts[0];d.knownHelpers[e]?b=!0:d.knownHelpersOnly&&(c=!1)}return b?"helper":c?"ambiguous":"simple"},pushParams:function(a){for(var b=0,c=a.length;c>b;b++)this.pushParam(a[b])},pushParam:function(a){this.stringParams?(a.depth&&this.addDepth(a.depth),this.opcode("getContext",a.depth||0),this.opcode("pushStringParam",a.stringModeValue,a.type),"sexpr"===a.type&&this.sexpr(a)):(this.trackIds&&this.opcode("pushId",a.type,a.idName||a.stringModeValue),this.accept(a))},setupFullMustacheParams:function(a,b,c){var d=a.params;return this.pushParams(d),this.opcode("pushProgram",b),this.opcode("pushProgram",c),a.hash?this.hash(a.hash):this.opcode("emptyHash"),d}},g.precompile=d,g.compile=e,g}(c,b),l=function(a,b){"use strict";function c(a){this.value=a}function d(){}var e,f=a.COMPILER_REVISION,g=a.REVISION_CHANGES,h=b;d.prototype={nameLookup:function(a,b){return d.isValidJavaScriptVariableName(b)?a+"."+b:a+"['"+b+"']"},depthedLookup:function(a){return this.aliases.lookup="this.lookup",'lookup(depths, "'+a+'")'},compilerInfo:function(){var a=f,b=g[a];return[a,b]},appendToBuffer:function(a){return this.environment.isSimple?"return "+a+";":{appendToBuffer:!0,content:a,toString:function(){return"buffer += "+a+";"}}},initializeBuffer:function(){return this.quotedString("")},namespace:"Handlebars",compile:function(a,b,c,d){this.environment=a,this.options=b,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!d,this.name=this.environment.name,this.isChild=!!c,this.context=c||{programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.compileChildren(a,b),this.useDepths=this.useDepths||a.depths.list.length||this.options.compat;var e,f,g,i=a.opcodes;for(f=0,g=i.length;g>f;f++)e=i[f],this[e.opcode].apply(this,e.args);if(this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new h("Compile completed with content left on stack");var j=this.createFunctionContext(d);if(this.isChild)return j;var k={compiler:this.compilerInfo(),main:j},l=this.context.programs;for(f=0,g=l.length;g>f;f++)l[f]&&(k[f]=l[f]);return this.environment.usePartial&&(k.usePartial=!0),this.options.data&&(k.useData=!0),this.useDepths&&(k.useDepths=!0),this.options.compat&&(k.compat=!0),d||(k.compiler=JSON.stringify(k.compiler),k=this.objectLiteral(k)),k},preamble:function(){this.lastContext=0,this.source=[]},createFunctionContext:function(a){var b="",c=this.stackVars.concat(this.registers.list);c.length>0&&(b+=", "+c.join(", "));for(var d in this.aliases)this.aliases.hasOwnProperty(d)&&(b+=", "+d+"="+this.aliases[d]);var e=["depth0","helpers","partials","data"];this.useDepths&&e.push("depths");var f=this.mergeSource(b);return a?(e.push(f),Function.apply(this,e)):"function("+e.join(",")+") {\n  "+f+"}"},mergeSource:function(a){for(var b,c,d="",e=!this.forceBuffer,f=0,g=this.source.length;g>f;f++){var h=this.source[f];h.appendToBuffer?b=b?b+"\n    + "+h.content:h.content:(b&&(d?d+="buffer += "+b+";\n  ":(c=!0,d=b+";\n  "),b=void 0),d+=h+"\n  ",this.environment.isSimple||(e=!1))}return e?(b||!d)&&(d+="return "+(b||'""')+";\n"):(a+=", buffer = "+(c?"":this.initializeBuffer()),d+=b?"return buffer + "+b+";\n":"return buffer;\n"),a&&(d="var "+a.substring(2)+(c?"":";\n  ")+d),d},blockValue:function(a){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var b=[this.contextName(0)];this.setupParams(a,0,b);var c=this.popStack();b.splice(1,0,c),this.push("blockHelperMissing.call("+b.join(", ")+")")},ambiguousBlockValue:function(){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=[this.contextName(0)];this.setupParams("",0,a,!0),this.flushInline();var b=this.topStack();a.splice(1,0,b),this.pushSource("if (!"+this.lastHelper+") { "+b+" = blockHelperMissing.call("+a.join(", ")+"); }")},appendContent:function(a){this.pendingContent&&(a=this.pendingContent+a),this.pendingContent=a},append:function(){this.flushInline();var a=this.popStack();this.pushSource("if ("+a+" != null) { "+this.appendToBuffer(a)+" }"),this.environment.isSimple&&this.pushSource("else { "+this.appendToBuffer("''")+" }")},appendEscaped:function(){this.aliases.escapeExpression="this.escapeExpression",this.pushSource(this.appendToBuffer("escapeExpression("+this.popStack()+")"))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c){var d=0,e=a.length;for(c||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[d++]));e>d;d++)this.replaceStack(function(c){var e=this.nameLookup(c,a[d],"context");return b?" && "+e:" != null ? "+e+" : "+c})},lookupData:function(a,b){a?this.pushStackLiteral("this.data(data, "+a+")"):this.pushStackLiteral("data");for(var c=b.length,d=0;c>d;d++)this.replaceStack(function(a){return" && "+this.nameLookup(a,b[d],"data")})},resolvePossibleLambda:function(){this.aliases.lambda="this.lambda",this.push("lambda("+this.popStack()+", "+this.contextName(0)+")")},pushStringParam:function(a,b){this.pushContext(),this.pushString(b),"sexpr"!==b&&("string"==typeof a?this.pushString(a):this.pushStackLiteral(a))},emptyHash:function(){this.pushStackLiteral("{}"),this.trackIds&&this.push("{}"),this.stringParams&&(this.push("{}"),this.push("{}"))},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[],ids:[]}},popHash:function(){var a=this.hash;this.hash=this.hashes.pop(),this.trackIds&&this.push("{"+a.ids.join(",")+"}"),this.stringParams&&(this.push("{"+a.contexts.join(",")+"}"),this.push("{"+a.types.join(",")+"}")),this.push("{\n    "+a.values.join(",\n    ")+"\n  }")},pushString:function(a){this.pushStackLiteral(this.quotedString(a))},push:function(a){return this.inlineStack.push(a),a},pushLiteral:function(a){this.pushStackLiteral(a)},pushProgram:function(a){null!=a?this.pushStackLiteral(this.programExpression(a)):this.pushStackLiteral(null)},invokeHelper:function(a,b,c){this.aliases.helperMissing="helpers.helperMissing";var d=this.popStack(),e=this.setupHelper(a,b),f=(c?e.name+" || ":"")+d+" || helperMissing";this.push("(("+f+").call("+e.callParams+"))")},invokeKnownHelper:function(a,b){var c=this.setupHelper(a,b);this.push(c.name+".call("+c.callParams+")")},invokeAmbiguous:function(a,b){this.aliases.functionType='"function"',this.aliases.helperMissing="helpers.helperMissing",this.useRegister("helper");var c=this.popStack();this.emptyHash();var d=this.setupHelper(0,a,b),e=this.lastHelper=this.nameLookup("helpers",a,"helper");this.push("((helper = (helper = "+e+" || "+c+") != null ? helper : helperMissing"+(d.paramsInit?"),("+d.paramsInit:"")+"),(typeof helper === functionType ? helper.call("+d.callParams+") : helper))")},invokePartial:function(a,b){var c=[this.nameLookup("partials",a,"partial"),"'"+b+"'","'"+a+"'",this.popStack(),this.popStack(),"helpers","partials"];this.options.data?c.push("data"):this.options.compat&&c.push("undefined"),this.options.compat&&c.push("depths"),this.push("this.invokePartial("+c.join(", ")+")")},assignToHash:function(a){var b,c,d,e=this.popStack();this.trackIds&&(d=this.popStack()),this.stringParams&&(c=this.popStack(),b=this.popStack());var f=this.hash;b&&f.contexts.push("'"+a+"': "+b),c&&f.types.push("'"+a+"': "+c),d&&f.ids.push("'"+a+"': "+d),f.values.push("'"+a+"': ("+e+")")},pushId:function(a,b){"ID"===a||"DATA"===a?this.pushString(b):"sexpr"===a?this.pushStackLiteral("true"):this.pushStackLiteral("null")},compiler:d,compileChildren:function(a,b){for(var c,d,e=a.children,f=0,g=e.length;g>f;f++){c=e[f],d=new this.compiler;var h=this.matchExistingProgram(c);null==h?(this.context.programs.push(""),h=this.context.programs.length,c.index=h,c.name="program"+h,this.context.programs[h]=d.compile(c,b,this.context,!this.precompile),this.context.environments[h]=c,this.useDepths=this.useDepths||d.useDepths):(c.index=h,c.name="program"+h)}},matchExistingProgram:function(a){for(var b=0,c=this.context.environments.length;c>b;b++){var d=this.context.environments[b];if(d&&d.equals(a))return b}},programExpression:function(a){var b=this.environment.children[a],c=(b.depths.list,this.useDepths),d=[b.index,"data"];return c&&d.push("depths"),"this.program("+d.join(", ")+")"},useRegister:function(a){this.registers[a]||(this.registers[a]=!0,this.registers.list.push(a))},pushStackLiteral:function(a){return this.push(new c(a))},pushSource:function(a){this.pendingContent&&(this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent))),this.pendingContent=void 0),a&&this.source.push(a)},pushStack:function(a){this.flushInline();var b=this.incrStack();return this.pushSource(b+" = "+a+";"),this.compileStack.push(b),b},replaceStack:function(a){{var b,d,e,f="";this.isInline()}if(!this.isInline())throw new h("replaceStack on non-inline");var g=this.popStack(!0);if(g instanceof c)f=b=g.value,e=!0;else{d=!this.stackSlot;var i=d?this.incrStack():this.topStackName();f="("+this.push(i)+" = "+g+")",b=this.topStack()}var j=a.call(this,b);e||this.popStack(),d&&this.stackSlot--,this.push("("+f+j+")")},incrStack:function(){return this.stackSlot++,this.stackSlot>this.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;if(a.length){this.inlineStack=[];for(var b=0,d=a.length;d>b;b++){var e=a[b];e instanceof c?this.compileStack.push(e):this.pushStack(e)}}},isInline:function(){return this.inlineStack.length},popStack:function(a){var b=this.isInline(),d=(b?this.inlineStack:this.compileStack).pop();if(!a&&d instanceof c)return d.value;if(!b){if(!this.stackSlot)throw new h("Invalid stack pop");this.stackSlot--}return d},topStack:function(){var a=this.isInline()?this.inlineStack:this.compileStack,b=a[a.length-1];return b instanceof c?b.value:b},contextName:function(a){return this.useDepths&&a?"depths["+a+"]":"depth"+a},quotedString:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")+'"'},objectLiteral:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(this.quotedString(c)+":"+a[c]);return"{"+b.join(",")+"}"},setupHelper:function(a,b,c){var d=[],e=this.setupParams(b,a,d,c),f=this.nameLookup("helpers",b,"helper");return{params:d,paramsInit:e,name:f,callParams:[this.contextName(0)].concat(d).join(", ")}},setupOptions:function(a,b,c){var d,e,f,g={},h=[],i=[],j=[];g.name=this.quotedString(a),g.hash=this.popStack(),this.trackIds&&(g.hashIds=this.popStack()),this.stringParams&&(g.hashTypes=this.popStack(),g.hashContexts=this.popStack()),e=this.popStack(),f=this.popStack(),(f||e)&&(f||(f="this.noop"),e||(e="this.noop"),g.fn=f,g.inverse=e);for(var k=b;k--;)d=this.popStack(),c[k]=d,this.trackIds&&(j[k]=this.popStack()),this.stringParams&&(i[k]=this.popStack(),h[k]=this.popStack());return this.trackIds&&(g.ids="["+j.join(",")+"]"),this.stringParams&&(g.types="["+i.join(",")+"]",g.contexts="["+h.join(",")+"]"),this.options.data&&(g.data="data"),g},setupParams:function(a,b,c,d){var e=this.objectLiteral(this.setupOptions(a,b,c));return d?(this.useRegister("options"),c.push("options"),"options="+e):(c.push(e),"")}};for(var i="break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private transient debugger implements protected volatile double import public let yield".split(" "),j=d.RESERVED_WORDS={},k=0,l=i.length;l>k;k++)j[i[k]]=!0;return d.isValidJavaScriptVariableName=function(a){return!d.RESERVED_WORDS[a]&&/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(a)},e=d}(d,c),m=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c.parser,j=c.parse,k=d.Compiler,l=d.compile,m=d.precompile,n=e,o=g.create,p=function(){var a=o();return a.compile=function(b,c){return l(b,c,a)},a.precompile=function(b,c){return m(b,c,a)},a.AST=h,a.Compiler=k,a.JavaScriptCompiler=n,a.Parser=i,a.parse=j,a};return g=p(),g.create=p,g["default"]=g,f=g}(f,g,j,k,l);return m});
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack.js b/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack.js
new file mode 100644
index 0000000..928386d
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack.js
@@ -0,0 +1,2 @@
+/*! highlight.js v9.1.0 | BSD3 License | git.io/hljslicense */
+!function(e){"undefined"!=typeof exports?e(exports):(self.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return self.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return E(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(E(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function o(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function u(e){l+="</"+t(e)+">"}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":x.classPrefix,i='<span class="'+a,o=t?"":"</span>";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(M);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(M);r;){e+=n(M.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(M)}return e+n(M.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!R[L.sL])return n(M);var t=e?l(L.sL,M,!0,y[L.sL]):f(M,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(y[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,M=""):e.eB?(k+=n(t)+r,M=""):(k+=r,M=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(M+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(M+=t),k+=b();do L.cN&&(k+="</span>"),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),M="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"<unnamed>")+'"');return M+=t,t.length||1}var N=E(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,L=i||N,y={},k="";for(w=L;w!=N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var M="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),w=L;w.parent;w=w.parent)w.cN&&(k+="</span>");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function f(e,t){t=t||x.languages||Object.keys(R);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(E(n)){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return x.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,x.tabReplace)})),x.useBR&&(e=e.replace(/\n/g,"<br>")),e}function h(e,n,t){var r=n?w[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;x.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,o=n?l(n,r,!0):f(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){x=o(x,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=R[n]=t(e);r.aliases&&r.aliases.forEach(function(e){w[e]=n})}function N(){return Object.keys(R)}function E(e){return e=(e||"").toLowerCase(),R[e]||R[w[e]]}var x={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},R={},w={};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=E,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:i,k:t},s={b:"{",e:"}",c:[{cN:"attr",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:r}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return i.splice(i.length,0,s,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("xml",function(s){var t="[A-Za-z0-9\\._:-]+",e={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php"},r={eW:!0,i:/</,r:0,c:[e,{cN:"attr",b:t,r:0},{b:"=",r:0,c:[{cN:"string",c:[e],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"meta",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("<!--","-->",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{name:"style"},c:[r],starts:{e:"</style>",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{name:"script"},c:[r],starts:{e:"</script>",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},e,{cN:"meta",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/</,e:/>\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\s*\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack_extended.js b/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack_extended.js
new file mode 100644
index 0000000..571c740
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/highlight.9.1.0.pack_extended.js
@@ -0,0 +1,34 @@
+'use strict';
+
+(function () {
+    var configure, highlightBlock;
+
+    configure = hljs.configure;
+    // "extending" hljs.configure method
+    hljs.configure = function _configure (options) {
+        var size = options.highlightSizeThreshold;
+
+        // added highlightSizeThreshold option to set maximum size
+        // of processed string. Set to null if not a number
+        hljs.highlightSizeThreshold = size === +size ? size : null;
+
+        configure.call(this, options);
+    };
+
+    highlightBlock = hljs.highlightBlock;
+
+    // "extending" hljs.highlightBlock method
+    hljs.highlightBlock = function _highlightBlock (el) {
+        var innerHTML = el.innerHTML;
+        var size = hljs.highlightSizeThreshold;
+
+        // check if highlightSizeThreshold is not set or element innerHTML
+        // is less than set option highlightSizeThreshold
+        if (size == null || size > innerHTML.length) {
+            // proceed with hljs.highlightBlock
+            highlightBlock.call(hljs, el);
+        }
+    };
+
+})();
+
diff --git a/profiles/killpay/src/main/webapp/lib/jquery-1.8.0.min.js b/profiles/killpay/src/main/webapp/lib/jquery-1.8.0.min.js
index f121291..066d72c 100644
--- a/profiles/killpay/src/main/webapp/lib/jquery-1.8.0.min.js
+++ b/profiles/killpay/src/main/webapp/lib/jquery-1.8.0.min.js
@@ -1,2 +1,2 @@
-/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
+/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
 (function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML="  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()},isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba},p.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){p.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj,g=f.selector;if(!e||e!==d&&!p.contains(d,e))a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b;return c}}}),p.support.submitBubbles||(p.event.special.submit={setup:function(){if(p.nodeName(this,"form"))return!1;p.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=p.nodeName(c,"input")||p.nodeName(c,"button")?c.form:b;d&&!p._data(d,"_submit_attached")&&(p.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),p._data(d,"_submit_attached",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&p.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(p.nodeName(this,"form"))return!1;p.event.remove(this,"._submit")}}),p.support.changeBubbles||(p.event.special.change={setup:function(){if(V.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")p.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),p.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),p.event.simulate("change",this,a,!0)});return!1}p.event.add(this,"beforeactivate._change",function(a){var b=a.target;V.test(b.nodeName)&&!p._data(b,"_change_attached")&&(p.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&p.event.simulate("change",this.parentNode,a,!0)}),p._data(b,"_change_attached",!0))})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){return p.event.remove(this,"._change"),V.test(this.nodeName)}}),p.support.focusinBubbles||p.each({focus:"focusin",blur:"focusout"},function(a,b){var c=0,d=function(a){p.event.simulate(b,a.target,p.event.fix(a),!0)};p.event.special[b]={setup:function(){c++===0&&e.addEventListener(a,d,!0)},teardown:function(){--c===0&&e.removeEventListener(a,d,!0)}}}),p.fn.extend({on:function(a,c,d,e,f){var g,h;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(h in a)this.on(h,c,d,a[h],f);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=ba;else if(!e)return this;return f===1&&(g=e,e=function(a){return p().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=p.guid++)),this.each(function(){p.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){var e,f;if(a&&a.preventDefault&&a.handleObj)return e=a.handleObj,p(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler),this;if(typeof a=="object"){for(f in a)this.off(f,c,a[f]);return this}if(c===!1||typeof c=="function")d=c,c=b;return d===!1&&(d=ba),this.each(function(){p.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){return p(this.context).on(a,this.selector,b,c),this},die:function(a,b){return p(this.context).off(a,this.selector||"**",b),this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a||"**",c)},trigger:function(a,b){return this.each(function(){p.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return p.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||p.guid++,d=0,e=function(c){var e=(p._data(this,"lastToggle"+a.guid)||0)%d;return p._data(this,"lastToggle"+a.guid,e+1),c.preventDefault(),b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),p.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){p.fn[b]=function(a,c){return c==null&&(c=a,a=null),arguments.length>0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e<f;e++)Z(a,b[e],c,d)}function be(a,b,c,d,e,f){var g,h=$.setFilters[b.toLowerCase()];return h||Z.error(b),(a||!(g=e))&&bd(a||"*",d,g=[],e),g.length>0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;a<c;a++)arguments[a]===b&&(g[a]=b)};for(;p<q;p++){s.exec(""),a=f[p],j=[],i=0,k=e;while(g=s.exec(a)){n=s.lastIndex=g.index+g[0].length;if(n>i){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="<select></select>";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="<a name='"+q+"'></a><div name='"+q+"'></div>",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},odd:function(a,b,c){var d=[],e=c?0:1,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},lt:function(a,b,c){return c?a.slice(+b):a.slice(0,+b)},gt:function(a,b,c){return c?a.slice(0,+b+1):a.slice(+b+1)},eq:function(a,b,c){var d=a.splice(+b,1);return c?a:d}}};$.setFilters.nth=$.setFilters.eq,$.filters=$.pseudos,X||($.attrHandle={href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}}),V&&($.order.push("NAME"),$.find.NAME=function(a,b){if(typeof b.getElementsByName!==j)return b.getElementsByName(a)}),Y&&($.order.splice(1,0,"CLASS"),$.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!==j&&!c)return b.getElementsByClassName(a)});try{n.call(i.childNodes,0)[0].nodeType}catch(_){n=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}var ba=Z.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},bb=Z.contains=i.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc=Z.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=bc(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=bc(b);return c};Z.attr=function(a,b){var c,d=ba(a);return d||(b=b.toLowerCase()),$.attrHandle[b]?$.attrHandle[b](a):U||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},Z.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},[0,0].sort(function(){return l=0}),i.compareDocumentPosition?e=function(a,b){return a===b?(k=!0,0):(!a.compareDocumentPosition||!b.compareDocumentPosition?a.compareDocumentPosition:a.compareDocumentPosition(b)&4)?-1:1}:(e=function(a,b){if(a===b)return k=!0,0;if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],g=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return f(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)g.unshift(j),j=j.parentNode;c=e.length,d=g.length;for(var l=0;l<c&&l<d;l++)if(e[l]!==g[l])return f(e[l],g[l]);return l===c?f(a,g[l],-1):f(e[l],b,1)},f=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),Z.uniqueSort=function(a){var b,c=1;if(e){k=l,a.sort(e);if(k)for(;b=a[c];c++)b===a[c-1]&&a.splice(c--,1)}return a};var bl=Z.compile=function(a,b,c){var d,e,f,g=O[a];if(g&&g.context===b)return g;e=bg(a,b,c);for(f=0;d=e[f];f++)e[f]=bj(d,b,c);return g=O[a]=bk(e),g.context=b,g.runs=g.dirruns=0,P.push(a),P.length>$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j<k;j++){p=$.order[j];if(s=L[p].exec(m)){h=$.find[p]((s[1]||"").replace(K,""),q,g);if(h==null)continue;m===r&&(a=a.slice(0,a.length-r.length)+m.replace(L[p],""),a||o.apply(e,n.call(h,0)));break}}}if(a){i=bl(a,b,g),d=i.dirruns++,h==null&&(h=$.find.TAG("*",G.test(a)&&b.parentNode||b));for(j=0;l=h[j];j++)c=i.runs++,i(l,b)&&e.push(l)}return e};h.querySelectorAll&&function(){var a,b=bm,c=/'|\\/g,d=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,e=[],f=[":active"],g=i.matchesSelector||i.mozMatchesSelector||i.webkitMatchesSelector||i.oMatchesSelector||i.msMatchesSelector;T(function(a){a.innerHTML="<select><option selected></option></select>",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="<p test=''></p>",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="<input type='hidden'>",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b<c;b++)if(p.contains(h[b],this))return!0});g=this.pushStack("","find",a);for(b=0,c=this.length;b<c;b++){d=g.length,p.find(a,this[b],g);if(b>0)for(e=d;e<g.length;e++)for(f=0;f<d;f++)if(g[f]===g[e]){g.splice(e--,1);break}}return g},has:function(a){var b,c=p(a,this),d=c.length;return this.filter(function(){for(b=0;b<d;b++)if(p.contains(this,c[b]))return!0})},not:function(a){return this.pushStack(bj(this,a,!1),"not",a)},filter:function(a){return this.pushStack(bj(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?bf.test(a)?p(a,this.context).index(this[0])>=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d<e;d++){c=this[d];while(c&&c.ownerDocument&&c!==b&&c.nodeType!==11){if(g?g.index(c)>-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/<tbody/i,br=/<|&#?\w+;/,bs=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,bu=new RegExp("<(?:"+bl+")[\\s/>]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,bz={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X<div>","</div>"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(f){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){return bh(this[0])?this.length?this.pushStack(p(p.isFunction(a)?a():a),"replaceWith",a):this:p.isFunction(a)?this.each(function(b){var c=p(this),d=c.html();c.replaceWith(a.call(this,b,d))}):(typeof a!="string"&&(a=p(a).detach()),this.each(function(){var b=this.nextSibling,c=this.parentNode;p(this).remove(),b?p(b).before(a):p(c).append(a)}))},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){a=[].concat.apply([],a);var e,f,g,h,i=0,j=a[0],k=[],l=this.length;if(!p.support.checkClone&&l>1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i<l;i++)d.call(c&&p.nodeName(this[i],"table")?bC(this[i],"tbody"):this[i],i===h?g:p.clone(g,!0,!0))}g=f=null,k.length&&p.each(k,function(a,b){b.src?p.ajax?p.ajax({url:b.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):p.error("no ajax"):p.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),p.buildFragment=function(a,c,d){var f,g,h,i=a[0];return c=c||e,c=(c[0]||c).ownerDocument||c[0]||c,typeof c.createDocumentFragment=="undefined"&&(c=e),a.length===1&&typeof i=="string"&&i.length<512&&c===e&&i.charAt(0)==="<"&&!bt.test(i)&&(p.support.checkClone||!bw.test(i))&&(p.support.html5Clone||!bu.test(i))&&(g=!0,f=p.fragments[i],h=f!==b),f||(f=c.createDocumentFragment(),p.clean(a,c,f,d),g&&(p.fragments[i]=h&&f)),{fragment:f,cacheable:g}},p.fragments={},p.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){p.fn[a]=function(c){var d,e=0,f=[],g=p(c),h=g.length,i=this.length===1&&this[0].parentNode;if((i==null||i&&i.nodeType===11&&i.childNodes.length===1)&&h===1)return g[b](this[0]),this;for(;e<h;e++)d=(e>0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1></$2>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]==="<table>"&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("<div>").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d<e;d++)c=a[d],cS[c]=cS[c]||[],cS[c].unshift(b)},prefilter:function(a,b){b?cR.unshift(a):cR.push(a)}}),p.Tween=cY,cY.prototype={constructor:cY,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(p.cssNumber[c]?"":"px")},cur:function(){var a=cY.propHooks[this.prop];return a&&a.get?a.get(this):cY.propHooks._default.get(this)},run:function(a){var b,c=cY.propHooks[this.prop];return this.pos=b=p.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration),this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):cY.propHooks._default.set(this),this}},cY.prototype.init.prototype=cY.prototype,cY.propHooks={_default:{get:function(a){var b;return a.elem[a.prop]==null||!!a.elem.style&&a.elem.style[a.prop]!=null?(b=p.css(a.elem,a.prop,!1,""),!b||b==="auto"?0:b):a.elem[a.prop]},set:function(a){p.fx.step[a.prop]?p.fx.step[a.prop](a):a.elem.style&&(a.elem.style[p.cssProps[a.prop]]!=null||p.cssHooks[a.prop])?p.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},cY.propHooks.scrollTop=cY.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},p.each(["toggle","show","hide"],function(a,b){var c=p.fn[b];p.fn[b]=function(d,e,f){return d==null||typeof d=="boolean"||!a&&p.isFunction(d)&&p.isFunction(e)?c.apply(this,arguments):this.animate(cZ(b,!0),d,e,f)}}),p.fn.extend({fadeTo:function(a,b,c,d){return this.filter(bY).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=p.isEmptyObject(a),f=p.speed(b,c,d),g=function(){var b=cV(this,p.extend({},a),f);e&&b.stop(!0)};return e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,c,d){var e=function(a){var b=a.stop;delete a.stop,b(d)};return typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,c=a!=null&&a+"queueHooks",f=p.timers,g=p._data(this);if(c)g[c]&&g[c].stop&&e(g[c]);else for(c in g)g[c]&&g[c].stop&&cQ.test(c)&&e(g[c]);for(c=f.length;c--;)f[c].elem===this&&(a==null||f[c].queue===a)&&(f[c].anim.stop(d),b=!1,f.splice(c,1));(b||!d)&&p.dequeue(this,a)})}}),p.each({slideDown:cZ("show"),slideUp:cZ("hide"),slideToggle:cZ("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){p.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),p.speed=function(a,b,c){var d=a&&typeof a=="object"?p.extend({},a):{complete:c||!c&&b||p.isFunction(a)&&a,duration:a,easing:c&&b||b&&!p.isFunction(b)&&b};d.duration=p.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in p.fx.speeds?p.fx.speeds[d.duration]:p.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";return d.old=d.complete,d.complete=function(){p.isFunction(d.old)&&d.old.call(this),d.queue&&p.dequeue(this,d.queue)},d},p.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},p.timers=[],p.fx=cY.prototype.init,p.fx.tick=function(){var a,b=p.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||p.fx.stop()},p.fx.timer=function(a){a()&&p.timers.push(a)&&!cN&&(cN=setInterval(p.fx.tick,p.fx.interval))},p.fx.interval=13,p.fx.stop=function(){clearInterval(cN),cN=null},p.fx.speeds={slow:600,fast:200,_default:400},p.fx.step={},p.expr&&p.expr.filters&&(p.expr.filters.animated=function(a){return p.grep(p.timers,function(b){return a===b.elem}).length});var c$=/^(?:body|html)$/i;p.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){p.offset.setOffset(this,a,b)});var c,d,e,f,g,h,i,j,k,l,m=this[0],n=m&&m.ownerDocument;if(!n)return;return(e=n.body)===m?p.offset.bodyOffset(m):(d=n.documentElement,p.contains(d,m)?(c=m.getBoundingClientRect(),f=c_(n),g=d.clientTop||e.clientTop||0,h=d.clientLeft||e.clientLeft||0,i=f.pageYOffset||d.scrollTop,j=f.pageXOffset||d.scrollLeft,k=c.top+i-g,l=c.left+j-h,{top:k,left:l}):{top:0,left:0})},p.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;return p.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(p.css(a,"marginTop"))||0,c+=parseFloat(p.css(a,"marginLeft"))||0),{top:b,left:c}},setOffset:function(a,b,c){var d=p.css(a,"position");d==="static"&&(a.style.position="relative");var e=p(a),f=e.offset(),g=p.css(a,"top"),h=p.css(a,"left"),i=(d==="absolute"||d==="fixed")&&p.inArray("auto",[g,h])>-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/jsoneditor.min.js b/profiles/killpay/src/main/webapp/lib/jsoneditor.min.js
new file mode 100644
index 0000000..343397f
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/jsoneditor.min.js
@@ -0,0 +1,11 @@
+/*! JSON Editor v0.7.22 - JSON Schema -> HTML Editor
+ * By Jeremy Dorn - https://github.com/jdorn/json-editor/
+ * Released under the MIT license
+ *
+ * Date: 2015-08-12
+ */
+!function(){var a;!function(){var b=!1,c=/xyz/.test(function(){window.postMessage("xyz")})?/\b_super\b/:/.*/;return a=function(){},a.extend=function(a){function d(){!b&&this.init&&this.init.apply(this,arguments)}var e=this.prototype;b=!0;var f=new this;b=!1;for(var g in a)f[g]="function"==typeof a[g]&&"function"==typeof e[g]&&c.test(a[g])?function(a,b){return function(){var c=this._super;this._super=e[a];var d=b.apply(this,arguments);return this._super=c,d}}(g,a[g]):a[g];return d.prototype=f,d.prototype.constructor=d,d.extend=arguments.callee,d},a}(),function(){function a(a,b){b=b||{bubbles:!1,cancelable:!1,detail:void 0};var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,b.bubbles,b.cancelable,b.detail),c}a.prototype=window.Event.prototype,window.CustomEvent=a}(),function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!window.requestAnimationFrame;++c)window.requestAnimationFrame=window[b[c]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[b[c]+"CancelAnimationFrame"]||window[b[c]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(b,c){var d=(new Date).getTime(),e=Math.max(0,16-(d-a)),f=window.setTimeout(function(){b(d+e)},e);return a=d+e,f}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(a){clearTimeout(a)})}(),function(){Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)})}();var b=function(a){return"object"!=typeof a||a.nodeType||null!==a&&a===a.window?!1:a.constructor&&!Object.prototype.hasOwnProperty.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},c=function(a){var d,e,f;for(e=1;e<arguments.length;e++){d=arguments[e];for(f in d)d.hasOwnProperty(f)&&(d[f]&&b(d[f])?(a.hasOwnProperty(f)||(a[f]={}),c(a[f],d[f])):a[f]=d[f])}return a},d=function(a,b){if(a&&"object"==typeof a){var c;if(Array.isArray(a)||"number"==typeof a.length&&a.length>0&&a.length-1 in a){for(c=0;c<a.length;c++)if(b(c,a[c])===!1)return}else if(Object.keys){var d=Object.keys(a);for(c=0;c<d.length;c++)if(b(d[c],a[d[c]])===!1)return}else for(c in a)if(a.hasOwnProperty(c)&&b(c,a[c])===!1)return}},e=function(a,b){var c=document.createEvent("HTMLEvents");c.initEvent(b,!0,!0),a.dispatchEvent(c)},f=function(a,b){if(!(a instanceof Element))throw new Error("element should be an instance of Element");b=c({},f.defaults.options,b||{}),this.element=a,this.options=b,this.init()};f.prototype={constructor:f,init:function(){var a=this;this.ready=!1;var b=f.defaults.themes[this.options.theme||f.defaults.theme];if(!b)throw"Unknown theme "+(this.options.theme||f.defaults.theme);this.schema=this.options.schema,this.theme=new b,this.template=this.options.template,this.refs=this.options.refs||{},this.uuid=0,this.__data={};var c=f.defaults.iconlibs[this.options.iconlib||f.defaults.iconlib];c&&(this.iconlib=new c),this.root_container=this.theme.getContainer(),this.element.appendChild(this.root_container),this.translate=this.options.translate||f.defaults.translate,this._loadExternalRefs(this.schema,function(){a._getDefinitions(a.schema),a.validator=new f.Validator(a);var b=a.getEditorClass(a.schema);a.root=a.createEditor(b,{jsoneditor:a,schema:a.schema,required:!0,container:a.root_container}),a.root.preBuild(),a.root.build(),a.root.postBuild(),a.options.startval&&a.root.setValue(a.options.startval),a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.ready=!0,window.requestAnimationFrame(function(){a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),a.root.showValidationErrors(a.validation_results),a.trigger("ready"),a.trigger("change"))})})},getValue:function(){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before getting the value";return this.root.getValue()},setValue:function(a){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before setting the value";return this.root.setValue(a),this},validate:function(a){if(!this.ready)throw"JSON Editor not ready yet.  Listen for 'ready' event before validating";return 1===arguments.length?this.validator.validate(a):this.validation_results},destroy:function(){this.destroyed||this.ready&&(this.schema=null,this.options=null,this.root.destroy(),this.root=null,this.root_container=null,this.validator=null,this.validation_results=null,this.theme=null,this.iconlib=null,this.template=null,this.__data=null,this.ready=!1,this.element.innerHTML="",this.destroyed=!0)},on:function(a,b){return this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[],this.callbacks[a].push(b),this},off:function(a,b){if(a&&b){this.callbacks=this.callbacks||{},this.callbacks[a]=this.callbacks[a]||[];for(var c=[],d=0;d<this.callbacks[a].length;d++)this.callbacks[a][d]!==b&&c.push(this.callbacks[a][d]);this.callbacks[a]=c}else a?(this.callbacks=this.callbacks||{},this.callbacks[a]=[]):this.callbacks={};return this},trigger:function(a){if(this.callbacks&&this.callbacks[a]&&this.callbacks[a].length)for(var b=0;b<this.callbacks[a].length;b++)this.callbacks[a][b]();return this},setOption:function(a,b){if("show_errors"!==a)throw"Option "+a+" must be set during instantiation and cannot be changed later";return this.options.show_errors=b,this.onChange(),this},getEditorClass:function(a){var b;if(a=this.expandSchema(a),d(f.defaults.resolvers,function(c,d){var e=d(a);return e&&f.defaults.editors[e]?(b=e,!1):void 0}),!b)throw"Unknown editor for schema "+JSON.stringify(a);if(!f.defaults.editors[b])throw"Unknown editor "+b;return f.defaults.editors[b]},createEditor:function(a,b){return b=c({},a.options||{},b),new a(b)},onChange:function(){if(this.ready&&!this.firing_change){this.firing_change=!0;var a=this;return window.requestAnimationFrame(function(){a.firing_change=!1,a.ready&&(a.validation_results=a.validator.validate(a.root.getValue()),"never"!==a.options.show_errors?a.root.showValidationErrors(a.validation_results):a.root.showValidationErrors([]),a.trigger("change"))}),this}},compileTemplate:function(a,b){b=b||f.defaults.template;var c;if("string"==typeof b){if(!f.defaults.templates[b])throw"Unknown template engine "+b;if(c=f.defaults.templates[b](),!c)throw"Template engine "+b+" missing required library."}else c=b;if(!c)throw"No template engine set";if(!c.compile)throw"Invalid template engine set";return c.compile(a)},_data:function(a,b,c){if(3!==arguments.length)return a.hasAttribute("data-jsoneditor-"+b)?this.__data[a.getAttribute("data-jsoneditor-"+b)]:null;var d;a.hasAttribute("data-jsoneditor-"+b)?d=a.getAttribute("data-jsoneditor-"+b):(d=this.uuid++,a.setAttribute("data-jsoneditor-"+b,d)),this.__data[d]=c},registerEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=a,this},unregisterEditor:function(a){return this.editors=this.editors||{},this.editors[a.path]=null,this},getEditor:function(a){return this.editors?this.editors[a]:void 0},watch:function(a,b){return this.watchlist=this.watchlist||{},this.watchlist[a]=this.watchlist[a]||[],this.watchlist[a].push(b),this},unwatch:function(a,b){if(!this.watchlist||!this.watchlist[a])return this;if(!b)return this.watchlist[a]=null,this;for(var c=[],d=0;d<this.watchlist[a].length;d++)this.watchlist[a][d]!==b&&c.push(this.watchlist[a][d]);return this.watchlist[a]=c.length?c:null,this},notifyWatchers:function(a){if(!this.watchlist||!this.watchlist[a])return this;for(var b=0;b<this.watchlist[a].length;b++)this.watchlist[a][b]()},isEnabled:function(){return!this.root||this.root.isEnabled()},enable:function(){this.root.enable()},disable:function(){this.root.disable()},_getDefinitions:function(a,b){if(b=b||"#/definitions/",a.definitions)for(var c in a.definitions)a.definitions.hasOwnProperty(c)&&(this.refs[b+c]=a.definitions[c],a.definitions[c].definitions&&this._getDefinitions(a.definitions[c],b+c+"/definitions/"))},_getExternalRefs:function(a){var b={},c=function(a){for(var c in a)a.hasOwnProperty(c)&&(b[c]=!0)};a.$ref&&"object"!=typeof a.$ref&&"#"!==a.$ref.substr(0,1)&&!this.refs[a.$ref]&&(b[a.$ref]=!0);for(var d in a)if(a.hasOwnProperty(d))if(a[d]&&"object"==typeof a[d]&&Array.isArray(a[d]))for(var e=0;e<a[d].length;e++)"object"==typeof a[d][e]&&c(this._getExternalRefs(a[d][e]));else a[d]&&"object"==typeof a[d]&&c(this._getExternalRefs(a[d]));return b},_loadExternalRefs:function(a,b){var c=this,e=this._getExternalRefs(a),f=0,g=0,h=!1;d(e,function(a){if(!c.refs[a]){if(!c.options.ajax)throw"Must set ajax option to true to load external ref "+a;c.refs[a]="loading",g++;var d=new XMLHttpRequest;d.open("GET",a,!0),d.onreadystatechange=function(){if(4==d.readyState){if(200!==d.status)throw window.console.log(d),"Failed to fetch ref via ajax- "+a;var e;try{e=JSON.parse(d.responseText)}catch(i){throw window.console.log(i),"Failed to parse external ref "+a}if(!e||"object"!=typeof e)throw"External ref does not contain a valid schema - "+a;c.refs[a]=e,c._loadExternalRefs(e,function(){f++,f>=g&&!h&&(h=!0,b())})}},d.send()}}),g||b()},expandRefs:function(a){for(a=c({},a);a.$ref;){var b=a.$ref;delete a.$ref,this.refs[b]||(b=decodeURIComponent(b)),a=this.extendSchemas(a,this.refs[b])}return a},expandSchema:function(a){var b,e=this,f=c({},a);if("object"==typeof a.type&&(Array.isArray(a.type)?d(a.type,function(b,c){"object"==typeof c&&(a.type[b]=e.expandSchema(c))}):a.type=e.expandSchema(a.type)),"object"==typeof a.disallow&&(Array.isArray(a.disallow)?d(a.disallow,function(b,c){"object"==typeof c&&(a.disallow[b]=e.expandSchema(c))}):a.disallow=e.expandSchema(a.disallow)),a.anyOf&&d(a.anyOf,function(b,c){a.anyOf[b]=e.expandSchema(c)}),a.dependencies&&d(a.dependencies,function(b,c){"object"!=typeof c||Array.isArray(c)||(a.dependencies[b]=e.expandSchema(c))}),a.not&&(a.not=this.expandSchema(a.not)),a.allOf){for(b=0;b<a.allOf.length;b++)f=this.extendSchemas(f,this.expandSchema(a.allOf[b]));delete f.allOf}if(a["extends"]){if(Array.isArray(a["extends"]))for(b=0;b<a["extends"].length;b++)f=this.extendSchemas(f,this.expandSchema(a["extends"][b]));else f=this.extendSchemas(f,this.expandSchema(a["extends"]));delete f["extends"]}if(a.oneOf){var g=c({},f);for(delete g.oneOf,b=0;b<a.oneOf.length;b++)f.oneOf[b]=this.extendSchemas(this.expandSchema(a.oneOf[b]),g)}return this.expandRefs(f)},extendSchemas:function(a,b){a=c({},a),b=c({},b);var e=this,f={};return d(a,function(a,c){"undefined"!=typeof b[a]?"required"===a&&"object"==typeof c&&Array.isArray(c)?f.required=c.concat(b[a]).reduce(function(a,b){return a.indexOf(b)<0&&a.push(b),a},[]):"type"!==a||"string"!=typeof c&&!Array.isArray(c)?"object"==typeof c&&Array.isArray(c)?f[a]=c.filter(function(c){return-1!==b[a].indexOf(c)}):"object"==typeof c&&null!==c?f[a]=e.extendSchemas(c,b[a]):f[a]=c:("string"==typeof c&&(c=[c]),"string"==typeof b.type&&(b.type=[b.type]),f.type=c.filter(function(a){return-1!==b.type.indexOf(a)}),1===f.type.length&&"string"==typeof f.type[0]&&(f.type=f.type[0])):f[a]=c}),d(b,function(b,c){"undefined"==typeof a[b]&&(f[b]=c)}),f}},f.defaults={themes:{},templates:{},iconlibs:{},editors:{},languages:{},resolvers:[],custom_validators:[]},f.Validator=a.extend({init:function(a,b){this.jsoneditor=a,this.schema=b||this.jsoneditor.schema,this.options={},this.translate=this.jsoneditor.translate||f.defaults.translate},validate:function(a){return this._validateSchema(this.schema,a)},_validateSchema:function(a,b,e){var g,h,i,j=this,k=[],l=JSON.stringify(b);if(e=e||"root",a=c({},this.jsoneditor.expandRefs(a)),a.required&&a.required===!0){if("undefined"==typeof b)return k.push({path:e,property:"required",message:this.translate("error_notset")}),k}else if("undefined"==typeof b){if(!this.jsoneditor.options.required_by_default)return k;k.push({path:e,property:"required",message:this.translate("error_notset")})}if(a["enum"]){for(g=!1,h=0;h<a["enum"].length;h++)l===JSON.stringify(a["enum"][h])&&(g=!0);g||k.push({path:e,property:"enum",message:this.translate("error_enum")})}if(a["extends"])for(h=0;h<a["extends"].length;h++)k=k.concat(this._validateSchema(a["extends"][h],b,e));if(a.allOf)for(h=0;h<a.allOf.length;h++)k=k.concat(this._validateSchema(a.allOf[h],b,e));if(a.anyOf){for(g=!1,h=0;h<a.anyOf.length;h++)if(!this._validateSchema(a.anyOf[h],b,e).length){g=!0;break}g||k.push({path:e,property:"anyOf",message:this.translate("error_anyOf")})}if(a.oneOf){g=0;var m=[];for(h=0;h<a.oneOf.length;h++){var n=this._validateSchema(a.oneOf[h],b,e);for(n.length||g++,i=0;i<n.length;i++)n[i].path=e+".oneOf["+h+"]"+n[i].path.substr(e.length);m=m.concat(n)}1!==g&&(k.push({path:e,property:"oneOf",message:this.translate("error_oneOf",[g])}),k=k.concat(m))}if(a.not&&(this._validateSchema(a.not,b,e).length||k.push({path:e,property:"not",message:this.translate("error_not")})),a.type)if(Array.isArray(a.type)){for(g=!1,h=0;h<a.type.length;h++)if(this._checkType(a.type[h],b)){g=!0;break}g||k.push({path:e,property:"type",message:this.translate("error_type_union")})}else this._checkType(a.type,b)||k.push({path:e,property:"type",message:this.translate("error_type",[a.type])});if(a.disallow)if(Array.isArray(a.disallow)){for(g=!0,h=0;h<a.disallow.length;h++)if(this._checkType(a.disallow[h],b)){g=!1;break}g||k.push({path:e,property:"disallow",message:this.translate("error_disallow_union")})}else this._checkType(a.disallow,b)&&k.push({path:e,property:"disallow",message:this.translate("error_disallow",[a.disallow])});if("number"==typeof b)(a.multipleOf||a.divisibleBy)&&(g=b/(a.multipleOf||a.divisibleBy),g!==Math.floor(g)&&k.push({path:e,property:a.multipleOf?"multipleOf":"divisibleBy",message:this.translate("error_multipleOf",[a.multipleOf||a.divisibleBy])})),a.hasOwnProperty("maximum")&&(a.exclusiveMaximum&&b>=a.maximum?k.push({path:e,property:"maximum",message:this.translate("error_maximum_excl",[a.maximum])}):!a.exclusiveMaximum&&b>a.maximum&&k.push({path:e,property:"maximum",message:this.translate("error_maximum_incl",[a.maximum])})),a.hasOwnProperty("minimum")&&(a.exclusiveMinimum&&b<=a.minimum?k.push({path:e,property:"minimum",message:this.translate("error_minimum_excl",[a.minimum])}):!a.exclusiveMinimum&&b<a.minimum&&k.push({path:e,property:"minimum",message:this.translate("error_minimum_incl",[a.minimum])}));else if("string"==typeof b)a.maxLength&&(b+"").length>a.maxLength&&k.push({path:e,property:"maxLength",message:this.translate("error_maxLength",[a.maxLength])}),a.minLength&&(b+"").length<a.minLength&&k.push({path:e,property:"minLength",message:this.translate(1===a.minLength?"error_notempty":"error_minLength",[a.minLength])}),a.pattern&&(new RegExp(a.pattern).test(b)||k.push({path:e,property:"pattern",message:this.translate("error_pattern")}));else if("object"==typeof b&&null!==b&&Array.isArray(b)){if(a.items)if(Array.isArray(a.items))for(h=0;h<b.length;h++)if(a.items[h])k=k.concat(this._validateSchema(a.items[h],b[h],e+"."+h));else{if(a.additionalItems===!0)break;if(!a.additionalItems){if(a.additionalItems===!1){k.push({path:e,property:"additionalItems",message:this.translate("error_additionalItems")});break}break}k=k.concat(this._validateSchema(a.additionalItems,b[h],e+"."+h))}else for(h=0;h<b.length;h++)k=k.concat(this._validateSchema(a.items,b[h],e+"."+h));if(a.maxItems&&b.length>a.maxItems&&k.push({path:e,property:"maxItems",message:this.translate("error_maxItems",[a.maxItems])}),a.minItems&&b.length<a.minItems&&k.push({path:e,property:"minItems",message:this.translate("error_minItems",[a.minItems])}),a.uniqueItems){var o={};for(h=0;h<b.length;h++){if(g=JSON.stringify(b[h]),o[g]){k.push({path:e,property:"uniqueItems",message:this.translate("error_uniqueItems")});break}o[g]=!0}}}else if("object"==typeof b&&null!==b){if(a.maxProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g>a.maxProperties&&k.push({path:e,property:"maxProperties",message:this.translate("error_maxProperties",[a.maxProperties])})}if(a.minProperties){g=0;for(h in b)b.hasOwnProperty(h)&&g++;g<a.minProperties&&k.push({path:e,property:"minProperties",message:this.translate("error_minProperties",[a.minProperties])})}if(a.required&&Array.isArray(a.required))for(h=0;h<a.required.length;h++)"undefined"==typeof b[a.required[h]]&&k.push({path:e,property:"required",message:this.translate("error_required",[a.required[h]])});var p={};if(a.properties)for(h in a.properties)a.properties.hasOwnProperty(h)&&(p[h]=!0,k=k.concat(this._validateSchema(a.properties[h],b[h],e+"."+h)));if(a.patternProperties)for(h in a.patternProperties)if(a.patternProperties.hasOwnProperty(h)){var q=new RegExp(h);for(i in b)b.hasOwnProperty(i)&&q.test(i)&&(p[i]=!0,k=k.concat(this._validateSchema(a.patternProperties[h],b[i],e+"."+i)))}if("undefined"!=typeof a.additionalProperties||!this.jsoneditor.options.no_additional_properties||a.oneOf||a.anyOf||(a.additionalProperties=!1),"undefined"!=typeof a.additionalProperties)for(h in b)if(b.hasOwnProperty(h)&&!p[h]){if(!a.additionalProperties){k.push({path:e,property:"additionalProperties",message:this.translate("error_additional_properties",[h])});break}if(a.additionalProperties===!0)break;k=k.concat(this._validateSchema(a.additionalProperties,b[h],e+"."+h))}if(a.dependencies)for(h in a.dependencies)if(a.dependencies.hasOwnProperty(h)&&"undefined"!=typeof b[h])if(Array.isArray(a.dependencies[h]))for(i=0;i<a.dependencies[h].length;i++)"undefined"==typeof b[a.dependencies[h][i]]&&k.push({path:e,property:"dependencies",message:this.translate("error_dependency",[a.dependencies[h][i]])});else k=k.concat(this._validateSchema(a.dependencies[h],b,e))}return d(f.defaults.custom_validators,function(c,d){k=k.concat(d.call(j,a,b,e))}),k},_checkType:function(a,b){return"string"==typeof a?"string"===a?"string"==typeof b:"number"===a?"number"==typeof b:"integer"===a?"number"==typeof b&&b===Math.floor(b):"boolean"===a?"boolean"==typeof b:"array"===a?Array.isArray(b):"object"===a?null!==b&&!Array.isArray(b)&&"object"==typeof b:"null"===a?null===b:!0:!this._validateSchema(a,b).length}}),f.AbstractEditor=a.extend({onChildEditorChange:function(a){this.onChange(!0)},notify:function(){this.jsoneditor.notifyWatchers(this.path)},change:function(){this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange()},onChange:function(a){this.notify(),this.watch_listener&&this.watch_listener(),a&&this.change()},register:function(){this.jsoneditor.registerEditor(this),this.onChange()},unregister:function(){this.jsoneditor&&this.jsoneditor.unregisterEditor(this)},getNumColumns:function(){return 12},init:function(a){this.jsoneditor=a.jsoneditor,this.theme=this.jsoneditor.theme,this.template_engine=this.jsoneditor.template,this.iconlib=this.jsoneditor.iconlib,this.original_schema=a.schema,this.schema=this.jsoneditor.expandSchema(this.original_schema),this.options=c({},this.options||{},a.schema.options||{},a),a.path||this.schema.id||(this.schema.id="root"),this.path=a.path||"root",this.formname=a.formname||this.path.replace(/\.([^.]+)/g,"[$1]"),this.jsoneditor.options.form_name_root&&(this.formname=this.formname.replace(/^root\[/,this.jsoneditor.options.form_name_root+"[")),this.key=this.path.split(".").pop(),this.parent=a.parent,this.link_watchers=[],a.container&&this.setContainer(a.container)},setContainer:function(a){this.container=a,this.schema.id&&this.container.setAttribute("data-schemaid",this.schema.id),this.schema.type&&"string"==typeof this.schema.type&&this.container.setAttribute("data-schematype",this.schema.type),this.container.setAttribute("data-schemapath",this.path)},preBuild:function(){},build:function(){},postBuild:function(){this.setupWatchListeners(),this.addLinks(),this.setValue(this.getDefault(),!0),this.updateHeaderText(),this.register(),this.onWatchedFieldChange()},setupWatchListeners:function(){var a=this;if(this.watched={},this.schema.vars&&(this.schema.watch=this.schema.vars),this.watched_values={},this.watch_listener=function(){a.refreshWatchedFieldValues()&&a.onWatchedFieldChange()},this.register(),this.schema.hasOwnProperty("watch")){var b,c,d,e,f;for(var g in this.schema.watch)if(this.schema.watch.hasOwnProperty(g)){if(b=this.schema.watch[g],Array.isArray(b)?c=[b[0]].concat(b[1].split(".")):(c=b.split("."),a.theme.closest(a.container,'[data-schemaid="'+c[0]+'"]')||c.unshift("#")),d=c.shift(),"#"===d&&(d=a.jsoneditor.schema.id||"root"),e=a.theme.closest(a.container,'[data-schemaid="'+d+'"]'),!e)throw"Could not find ancestor node with id "+d;f=e.getAttribute("data-schemapath")+"."+c.join("."),a.jsoneditor.watch(f,a.watch_listener),a.watched[g]=f}}this.schema.headerTemplate&&(this.header_template=this.jsoneditor.compileTemplate(this.schema.headerTemplate,this.template_engine))},addLinks:function(){if(!this.no_link_holder&&(this.link_holder=this.theme.getLinksHolder(),this.container.appendChild(this.link_holder),this.schema.links))for(var a=0;a<this.schema.links.length;a++)this.addLink(this.getLink(this.schema.links[a]))},getButton:function(a,b,c){var d="json-editor-btn-"+b;b=this.iconlib?this.iconlib.getIcon(b):null,!b&&c&&(a=c,c=null);var e=this.theme.getButton(a,b,c);return e.className+=" "+d+" ",e},setButtonText:function(a,b,c,d){return c=this.iconlib?this.iconlib.getIcon(c):null,!c&&d&&(b=d,d=null),this.theme.setButtonText(a,b,c,d)},addLink:function(a){this.link_holder&&this.link_holder.appendChild(a)},getLink:function(a){var b,c,d=a.mediaType||"application/javascript",e=d.split("/")[0],f=this.jsoneditor.compileTemplate(a.href,this.template_engine);if("image"===e){b=this.theme.getBlockLinkHolder(),c=document.createElement("a"),c.setAttribute("target","_blank");var g=document.createElement("img");this.theme.createImageLink(b,c,g),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.setAttribute("title",a.rel||d),g.setAttribute("src",d)})}else if(["audio","video"].indexOf(e)>=0){b=this.theme.getBlockLinkHolder(),c=this.theme.getBlockLink(),c.setAttribute("target","_blank");var h=document.createElement(e);h.setAttribute("controls","controls"),this.theme.createMediaLink(b,c,h),this.link_watchers.push(function(b){var d=f(b);c.setAttribute("href",d),c.textContent=a.rel||d,h.setAttribute("src",d)})}else b=this.theme.getBlockLink(),b.setAttribute("target","_blank"),b.textContent=a.rel,this.link_watchers.push(function(c){var d=f(c);b.setAttribute("href",d),b.textContent=a.rel||d});return b},refreshWatchedFieldValues:function(){if(this.watched_values){var a={},b=!1,c=this;if(this.watched){var d,e;for(var f in this.watched)this.watched.hasOwnProperty(f)&&(e=c.jsoneditor.getEditor(this.watched[f]),d=e?e.getValue():null,c.watched_values[f]!==d&&(b=!0),a[f]=d)}return a.self=this.getValue(),this.watched_values.self!==a.self&&(b=!0),this.watched_values=a,b}},getWatchedFieldValues:function(){return this.watched_values},updateHeaderText:function(){if(this.header)if(this.header.children.length){for(var a=0;a<this.header.childNodes.length;a++)if(3===this.header.childNodes[a].nodeType){this.header.childNodes[a].nodeValue=this.getHeaderText();break}}else this.header.textContent=this.getHeaderText()},getHeaderText:function(a){return this.header_text?this.header_text:a?this.schema.title:this.getTitle()},onWatchedFieldChange:function(){var a;if(this.header_template){a=c(this.getWatchedFieldValues(),{key:this.key,i:this.key,i0:1*this.key,i1:1*this.key+1,title:this.getTitle()});var b=this.header_template(a);b!==this.header_text&&(this.header_text=b,this.updateHeaderText(),this.notify())}if(this.link_watchers.length){a=this.getWatchedFieldValues();for(var d=0;d<this.link_watchers.length;d++)this.link_watchers[d](a)}},setValue:function(a){this.value=a},getValue:function(){return this.value},refreshValue:function(){},getChildEditors:function(){return!1},destroy:function(){var a=this;this.unregister(this),d(this.watched,function(b,c){a.jsoneditor.unwatch(c,a.watch_listener)}),this.watched=null,this.watched_values=null,this.watch_listener=null,this.header_text=null,this.header_template=null,this.value=null,this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.container=null,this.jsoneditor=null,this.schema=null,this.path=null,this.key=null,this.parent=null},getDefault:function(){if(this.schema["default"])return this.schema["default"];if(this.schema["enum"])return this.schema["enum"][0];var a=this.schema.type||this.schema.oneOf;if(a&&Array.isArray(a)&&(a=a[0]),a&&"object"==typeof a&&(a=a.type),a&&Array.isArray(a)&&(a=a[0]),"string"==typeof a){if("number"===a)return 0;if("boolean"===a)return!1;if("integer"===a)return 0;if("string"===a)return"";if("object"===a)return{};if("array"===a)return[]}return null},getTitle:function(){return this.schema.title||this.key},enable:function(){this.disabled=!1},disable:function(){this.disabled=!0},isEnabled:function(){return!this.disabled},isRequired:function(){return"boolean"==typeof this.schema.required?this.schema.required:this.parent&&this.parent.schema&&Array.isArray(this.parent.schema.required)?this.parent.schema.required.indexOf(this.key)>-1:this.jsoneditor.options.required_by_default?!0:!1},getDisplayText:function(a){var b=[],c={};d(a,function(a,b){b.title&&(c[b.title]=c[b.title]||0,c[b.title]++),b.description&&(c[b.description]=c[b.description]||0,c[b.description]++),b.format&&(c[b.format]=c[b.format]||0,c[b.format]++),b.type&&(c[b.type]=c[b.type]||0,c[b.type]++)}),d(a,function(a,d){var e;e="string"==typeof d?d:d.title&&c[d.title]<=1?d.title:d.format&&c[d.format]<=1?d.format:d.type&&c[d.type]<=1?d.type:d.description&&c[d.description]<=1?d.descripton:d.title?d.title:d.format?d.format:d.type?d.type:d.description?d.description:JSON.stringify(d).length<50?JSON.stringify(d):"type",b.push(e)});var e={};return d(b,function(a,d){e[d]=e[d]||0,e[d]++,c[d]>1&&(b[a]=d+" "+e[d])}),b},getOption:function(a){try{throw"getOption is deprecated"}catch(b){window.console.error(b)}return this.options[a]},showValidationErrors:function(a){}}),f.defaults.editors["null"]=f.AbstractEditor.extend({getValue:function(){return null},setValue:function(){this.onChange()},getNumColumns:function(){return 2}}),f.defaults.editors.string=f.AbstractEditor.extend({register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},setValue:function(a,b,c){if((!this.template||c)&&(null===a||"undefined"==typeof a?a="":"object"==typeof a?a=JSON.stringify(a):"string"!=typeof a&&(a=""+a),a!==this.serialized)){var d=this.sanitize(a);if(this.input.value!==d){this.input.value=d,this.sceditor_instance?this.sceditor_instance.val(d):this.epiceditor?this.epiceditor.importFile(null,d):this.ace_editor&&this.ace_editor.setValue(d);var e=c||this.getValue()!==a;this.refreshValue(),b?this.is_dirty=!1:"change"===this.jsoneditor.options.show_errors&&(this.is_dirty=!0),this.adjust_height&&this.adjust_height(this.input),this.onChange(e)}}},getNumColumns:function(){var a,b=Math.ceil(Math.max(this.getTitle().length,this.schema.maxLength||0,this.schema.minLength||0)/5);return a="textarea"===this.input_type?6:["text","email"].indexOf(this.input_type)>=0?4:2,Math.min(12,Math.max(b,a))},build:function(){var a=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.format=this.schema.format,!this.format&&this.schema.media&&this.schema.media.type&&(this.format=this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,"")),!this.format&&this.options.default_format&&(this.format=this.options.default_format),this.options.format&&(this.format=this.options.format),this.format)if("textarea"===this.format)this.input_type="textarea",this.input=this.theme.getTextareaInput();else if("range"===this.format){this.input_type="range";var b=this.schema.minimum||0,c=this.schema.maximum||Math.max(100,b+1),d=1;this.schema.multipleOf&&(b%this.schema.multipleOf&&(b=Math.ceil(b/this.schema.multipleOf)*this.schema.multipleOf),c%this.schema.multipleOf&&(c=Math.floor(c/this.schema.multipleOf)*this.schema.multipleOf),d=this.schema.multipleOf),this.input=this.theme.getRangeInput(b,c,d)}else["actionscript","batchfile","bbcode","c","c++","cpp","coffee","csharp","css","dart","django","ejs","erlang","golang","handlebars","haskell","haxe","html","ini","jade","java","javascript","json","less","lisp","lua","makefile","markdown","matlab","mysql","objectivec","pascal","perl","pgsql","php","python","r","ruby","sass","scala","scss","smarty","sql","stylus","svg","twig","vbscript","xml","yaml"].indexOf(this.format)>=0?(this.input_type=this.format,this.source_code=!0,this.input=this.theme.getTextareaInput()):(this.input_type=this.format,this.input=this.theme.getFormInputField(this.input_type));else this.input_type="text",this.input=this.theme.getFormInputField(this.input_type);"undefined"!=typeof this.schema.maxLength&&this.input.setAttribute("maxlength",this.schema.maxLength),"undefined"!=typeof this.schema.pattern?this.input.setAttribute("pattern",this.schema.pattern):"undefined"!=typeof this.schema.minLength&&this.input.setAttribute("pattern",".{"+this.schema.minLength+",}"),this.options.compact?this.container.className+=" compact":this.options.input_width&&(this.input.style.width=this.options.input_width),(this.schema.readOnly||this.schema.readonly||this.schema.template)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),a.schema.template)return void(this.value=a.value);var c=this.value,d=a.sanitize(c);c!==d&&(this.value=d),a.is_dirty=!0,a.refreshValue(),a.onChange(!0)}),this.options.input_height&&(this.input.style.height=this.options.input_height),this.options.expand_height&&(this.adjust_height=function(a){if(a){var b,c=a.offsetHeight;if(a.offsetHeight<a.scrollHeight)for(b=0;a.offsetHeight<a.scrollHeight+3&&!(b>100);)b++,c++,a.style.height=c+"px";else{for(b=0;a.offsetHeight>=a.scrollHeight+3&&!(b>100);)b++,c--,a.style.height=c+"px";a.style.height=c+1+"px"}}},this.input.addEventListener("keyup",function(b){a.adjust_height(this)}),this.input.addEventListener("change",function(b){a.adjust_height(this)}),this.adjust_height()),this.format&&this.input.setAttribute("data-schemaformat",this.format),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),window.requestAnimationFrame(function(){a.input.parentNode&&a.afterInputReady(),a.adjust_height&&a.adjust_height(a.input)}),this.schema.template?(this.template=this.jsoneditor.compileTemplate(this.schema.template,this.template_engine),this.refreshValue()):this.refreshValue()},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},afterInputReady:function(){var a,b=this;if(this.source_code)if(this.options.wysiwyg&&["html","bbcode"].indexOf(this.input_type)>=0&&window.jQuery&&window.jQuery.fn&&window.jQuery.fn.sceditor)a=c({},{plugins:"html"===b.input_type?"xhtml":"bbcode",emoticonsEnabled:!1,width:"100%",height:300},f.plugins.sceditor,b.options.sceditor_options||{}),window.jQuery(b.input).sceditor(a),b.sceditor_instance=window.jQuery(b.input).sceditor("instance"),b.sceditor_instance.blur(function(){var a=window.jQuery("<div>"+b.sceditor_instance.val()+"</div>");window.jQuery("#sceditor-start-marker,#sceditor-end-marker,.sceditor-nlf",a).remove(),b.input.value=a.html(),b.value=b.input.value,b.is_dirty=!0,b.onChange(!0)});else if("markdown"===this.input_type&&window.EpicEditor)this.epiceditor_container=document.createElement("div"),this.input.parentNode.insertBefore(this.epiceditor_container,this.input),this.input.style.display="none",a=c({},f.plugins.epiceditor,{container:this.epiceditor_container,clientSideStorage:!1}),this.epiceditor=new window.EpicEditor(a).load(),this.epiceditor.importFile(null,this.getValue()),this.epiceditor.on("update",function(){var a=b.epiceditor.exportFile();b.input.value=a,b.value=a,b.is_dirty=!0,b.onChange(!0);
+});else if(window.ace){var d=this.input_type;("cpp"===d||"c++"===d||"c"===d)&&(d="c_cpp"),this.ace_container=document.createElement("div"),this.ace_container.style.width="100%",this.ace_container.style.position="relative",this.ace_container.style.height="400px",this.input.parentNode.insertBefore(this.ace_container,this.input),this.input.style.display="none",this.ace_editor=window.ace.edit(this.ace_container),this.ace_editor.setValue(this.getValue()),f.plugins.ace.theme&&this.ace_editor.setTheme("ace/theme/"+f.plugins.ace.theme),d=window.ace.require("ace/mode/"+d),d&&this.ace_editor.getSession().setMode(new d.Mode),this.ace_editor.on("change",function(){var a=b.ace_editor.getValue();b.input.value=a,b.refreshValue(),b.is_dirty=!0,b.onChange(!0)})}b.theme.afterInputReady(b.input)},refreshValue:function(){this.value=this.input.value,"string"!=typeof this.value&&(this.value=""),this.serialized=this.value},destroy:function(){this.sceditor_instance?this.sceditor_instance.destroy():this.epiceditor?this.epiceditor.unload():this.ace_editor&&this.ace_editor.destroy(),this.template=null,this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this._super()},sanitize:function(a){return a},onWatchedFieldChange:function(){var a;this.template&&(a=this.getWatchedFieldValues(),this.setValue(this.template(a),!1,!0)),this._super()},showValidationErrors:function(a){var b=this;if("always"===this.jsoneditor.options.show_errors);else if(!this.is_dirty&&this.previous_error_setting===this.jsoneditor.options.show_errors)return;this.previous_error_setting=this.jsoneditor.options.show_errors;var c=[];d(a,function(a,d){d.path===b.path&&c.push(d.message)}),c.length?this.theme.addInputError(this.input,c.join(". ")+"."):this.theme.removeInputError(this.input)}}),f.defaults.editors.number=f.defaults.editors.string.extend({sanitize:function(a){return(a+"").replace(/[^0-9\.\-eE]/g,"")},getNumColumns:function(){return 2},getValue:function(){return 1*this.value}}),f.defaults.editors.integer=f.defaults.editors.number.extend({sanitize:function(a){return a+="",a.replace(/[^0-9\-]/g,"")},getNumColumns:function(){return 2}}),f.defaults.editors.object=f.AbstractEditor.extend({getDefault:function(){return c({},this.schema["default"]||{})},getChildEditors:function(){return this.editors},register:function(){if(this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].register()},unregister:function(){if(this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].unregister()},getNumColumns:function(){return Math.max(Math.min(12,this.maxwidth),3)},enable:function(){if(this.editjson_button&&(this.editjson_button.disabled=!1),this.addproperty_button&&(this.addproperty_button.disabled=!1),this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].enable()},disable:function(){if(this.editjson_button&&(this.editjson_button.disabled=!0),this.addproperty_button&&(this.addproperty_button.disabled=!0),this.hideEditJSON(),this._super(),this.editors)for(var a in this.editors)this.editors.hasOwnProperty(a)&&this.editors[a].disable()},layoutEditors:function(){var a,b,c=this;if(this.row_container){this.property_order=Object.keys(this.editors),this.property_order=this.property_order.sort(function(a,b){var d=c.editors[a].schema.propertyOrder,e=c.editors[b].schema.propertyOrder;return"number"!=typeof d&&(d=1e3),"number"!=typeof e&&(e=1e3),d-e});var e;if("grid"===this.format){var f=[];for(d(this.property_order,function(a,b){var d=c.editors[b];if(!d.property_removed){for(var e=!1,g=d.options.hidden?0:d.options.grid_columns||d.getNumColumns(),h=d.options.hidden?0:d.container.offsetHeight,i=0;i<f.length;i++)f[i].width+g<=12&&(!h||.5*f[i].minh<h&&2*f[i].maxh>h)&&(e=i);e===!1&&(f.push({width:0,minh:999999,maxh:0,editors:[]}),e=f.length-1),f[e].editors.push({key:b,width:g,height:h}),f[e].width+=g,f[e].minh=Math.min(f[e].minh,h),f[e].maxh=Math.max(f[e].maxh,h)}}),a=0;a<f.length;a++)if(f[a].width<12){var g=!1,h=0;for(b=0;b<f[a].editors.length;b++)g===!1?g=b:f[a].editors[b].width>f[a].editors[g].width&&(g=b),f[a].editors[b].width*=12/f[a].width,f[a].editors[b].width=Math.floor(f[a].editors[b].width),h+=f[a].editors[b].width;12>h&&(f[a].editors[g].width+=12-h),f[a].width=12}if(this.layout===JSON.stringify(f))return!1;for(this.layout=JSON.stringify(f),e=document.createElement("div"),a=0;a<f.length;a++){var i=this.theme.getGridRow();for(e.appendChild(i),b=0;b<f[a].editors.length;b++){var j=f[a].editors[b].key,k=this.editors[j];k.options.hidden?k.container.style.display="none":this.theme.setGridColumnSize(k.container,f[a].editors[b].width),i.appendChild(k.container)}}}else e=document.createElement("div"),d(this.property_order,function(a,b){var d=c.editors[b];if(!d.property_removed){var f=c.theme.getGridRow();e.appendChild(f),d.options.hidden?d.container.style.display="none":c.theme.setGridColumnSize(d.container,12),f.appendChild(d.container)}});this.row_container.innerHTML="",this.row_container.appendChild(e)}},getPropertySchema:function(a){var b=this.schema.properties[a]||{};b=c({},b);var d=this.schema.properties[a]?!0:!1;if(this.schema.patternProperties)for(var e in this.schema.patternProperties)if(this.schema.patternProperties.hasOwnProperty(e)){var f=new RegExp(e);f.test(a)&&(b.allOf=b.allOf||[],b.allOf.push(this.schema.patternProperties[e]),d=!0)}return!d&&this.schema.additionalProperties&&"object"==typeof this.schema.additionalProperties&&(b=c({},this.schema.additionalProperties)),b},preBuild:function(){this._super(),this.editors={},this.cached_editors={};var a=this;if(this.format=this.options.layout||this.options.object_layout||this.schema.format||this.jsoneditor.options.object_layout||"normal",this.schema.properties=this.schema.properties||{},this.minwidth=0,this.maxwidth=0,this.options.table_row)d(this.schema.properties,function(b,c){var d=a.jsoneditor.getEditorClass(c);a.editors[b]=a.jsoneditor.createEditor(d,{jsoneditor:a.jsoneditor,schema:c,path:a.path+"."+b,parent:a,compact:!0,required:!0}),a.editors[b].preBuild();var e=a.editors[b].options.hidden?0:a.editors[b].options.grid_columns||a.editors[b].getNumColumns();a.minwidth+=e,a.maxwidth+=e}),this.no_link_holder=!0;else{if(this.options.table)throw"Not supported yet";this.defaultProperties=this.schema.defaultProperties||Object.keys(this.schema.properties),a.maxwidth+=1,d(this.defaultProperties,function(b,c){a.addObjectProperty(c,!0),a.editors[c]&&(a.minwidth=Math.max(a.minwidth,a.editors[c].options.grid_columns||a.editors[c].getNumColumns()),a.maxwidth+=a.editors[c].options.grid_columns||a.editors[c].getNumColumns())})}this.property_order=Object.keys(this.editors),this.property_order=this.property_order.sort(function(b,c){var d=a.editors[b].schema.propertyOrder,e=a.editors[c].schema.propertyOrder;return"number"!=typeof d&&(d=1e3),"number"!=typeof e&&(e=1e3),d-e})},build:function(){var a=this;if(this.options.table_row)this.editor_holder=this.container,d(this.editors,function(b,c){var d=a.theme.getTableCell();a.editor_holder.appendChild(d),c.setContainer(d),c.build(),c.postBuild(),a.editors[b].options.hidden&&(d.style.display="none"),a.editors[b].options.input_width&&(d.style.width=a.editors[b].options.input_width)});else{if(this.options.table)throw"Not supported yet";this.header=document.createElement("span"),this.header.textContent=this.getTitle(),this.title=this.theme.getHeader(this.header),this.container.appendChild(this.title),this.container.style.position="relative",this.editjson_holder=this.theme.getModal(),this.editjson_textarea=this.theme.getTextareaInput(),this.editjson_textarea.style.height="170px",this.editjson_textarea.style.width="300px",this.editjson_textarea.style.display="block",this.editjson_save=this.getButton("Save","save","Save"),this.editjson_save.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.saveJSON()}),this.editjson_cancel=this.getButton("Cancel","cancel","Cancel"),this.editjson_cancel.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.hideEditJSON()}),this.editjson_holder.appendChild(this.editjson_textarea),this.editjson_holder.appendChild(this.editjson_save),this.editjson_holder.appendChild(this.editjson_cancel),this.addproperty_holder=this.theme.getModal(),this.addproperty_list=document.createElement("div"),this.addproperty_list.style.width="295px",this.addproperty_list.style.maxHeight="160px",this.addproperty_list.style.padding="5px 0",this.addproperty_list.style.overflowY="auto",this.addproperty_list.style.overflowX="hidden",this.addproperty_list.style.paddingLeft="5px",this.addproperty_list.setAttribute("class","property-selector"),this.addproperty_add=this.getButton("add","add","add"),this.addproperty_input=this.theme.getFormInputField("text"),this.addproperty_input.setAttribute("placeholder","Property name..."),this.addproperty_input.style.width="220px",this.addproperty_input.style.marginBottom="0",this.addproperty_input.style.display="inline-block",this.addproperty_add.addEventListener("click",function(b){if(b.preventDefault(),b.stopPropagation(),a.addproperty_input.value){if(a.editors[a.addproperty_input.value])return void window.alert("there is already a property with that name");a.addObjectProperty(a.addproperty_input.value),a.editors[a.addproperty_input.value]&&a.editors[a.addproperty_input.value].disable(),a.onChange(!0)}}),this.addproperty_holder.appendChild(this.addproperty_list),this.addproperty_holder.appendChild(this.addproperty_input),this.addproperty_holder.appendChild(this.addproperty_add);var b=document.createElement("div");b.style.clear="both",this.addproperty_holder.appendChild(b),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.error_holder=document.createElement("div"),this.container.appendChild(this.error_holder),this.editor_holder=this.theme.getIndentedPanel(),this.editor_holder.style.paddingBottom="0",this.container.appendChild(this.editor_holder),this.row_container=this.theme.getGridContainer(),this.editor_holder.appendChild(this.row_container),d(this.editors,function(b,c){var d=a.theme.getGridColumn();a.row_container.appendChild(d),c.setContainer(d),c.build(),c.postBuild()}),this.title_controls=this.theme.getHeaderButtonHolder(),this.editjson_controls=this.theme.getHeaderButtonHolder(),this.addproperty_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.title.appendChild(this.editjson_controls),this.title.appendChild(this.addproperty_controls),this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls.appendChild(this.toggle_button),this.toggle_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.collapsed?(a.editor_holder.style.display="",a.collapsed=!1,a.setButtonText(a.toggle_button,"","collapse","Collapse")):(a.editor_holder.style.display="none",a.collapsed=!0,a.setButtonText(a.toggle_button,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none"),this.editjson_button=this.getButton("JSON","edit","Edit JSON"),this.editjson_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.toggleEditJSON()}),this.editjson_controls.appendChild(this.editjson_button),this.editjson_controls.appendChild(this.editjson_holder),this.schema.options&&"undefined"!=typeof this.schema.options.disable_edit_json?this.schema.options.disable_edit_json&&(this.editjson_button.style.display="none"):this.jsoneditor.options.disable_edit_json&&(this.editjson_button.style.display="none"),this.addproperty_button=this.getButton("Properties","edit","Object Properties"),this.addproperty_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.toggleAddProperty()}),this.addproperty_controls.appendChild(this.addproperty_button),this.addproperty_controls.appendChild(this.addproperty_holder),this.refreshAddProperties()}this.options.table_row?(this.editor_holder=this.container,d(this.property_order,function(b,c){a.editor_holder.appendChild(a.editors[c].container)})):(this.layoutEditors(),this.layoutEditors())},showEditJSON:function(){this.editjson_holder&&(this.hideAddProperty(),this.editjson_holder.style.left=this.editjson_button.offsetLeft+"px",this.editjson_holder.style.top=this.editjson_button.offsetTop+this.editjson_button.offsetHeight+"px",this.editjson_textarea.value=JSON.stringify(this.getValue(),null,2),this.disable(),this.editjson_holder.style.display="",this.editjson_button.disabled=!1,this.editing_json=!0)},hideEditJSON:function(){this.editjson_holder&&this.editing_json&&(this.editjson_holder.style.display="none",this.enable(),this.editing_json=!1)},saveJSON:function(){if(this.editjson_holder)try{var a=JSON.parse(this.editjson_textarea.value);this.setValue(a),this.hideEditJSON()}catch(b){throw window.alert("invalid JSON"),b}},toggleEditJSON:function(){this.editing_json?this.hideEditJSON():this.showEditJSON()},insertPropertyControlUsingPropertyOrder:function(a,b,c){var d;this.schema.properties[a]&&(d=this.schema.properties[a].propertyOrder),"number"!=typeof d&&(d=1e3),b.propertyOrder=d;for(var e=0;e<c.childNodes.length;e++){var f=c.childNodes[e];if(b.propertyOrder<f.propertyOrder){this.addproperty_list.insertBefore(b,f),b=null;break}}b&&this.addproperty_list.appendChild(b)},addPropertyCheckbox:function(a){var b,c,d,e,f=this;return b=f.theme.getCheckbox(),b.style.width="auto",d=this.schema.properties[a]&&this.schema.properties[a].title?this.schema.properties[a].title:a,c=f.theme.getCheckboxLabel(d),e=f.theme.getFormControl(c,b),e.style.paddingBottom=e.style.marginBottom=e.style.paddingTop=e.style.marginTop=0,e.style.height="auto",this.insertPropertyControlUsingPropertyOrder(a,e,this.addproperty_list),b.checked=a in this.editors,b.addEventListener("change",function(){b.checked?f.addObjectProperty(a):f.removeObjectProperty(a),f.onChange(!0)}),f.addproperty_checkboxes[a]=b,b},showAddProperty:function(){this.addproperty_holder&&(this.hideEditJSON(),this.addproperty_holder.style.left=this.addproperty_button.offsetLeft+"px",this.addproperty_holder.style.top=this.addproperty_button.offsetTop+this.addproperty_button.offsetHeight+"px",this.disable(),this.adding_property=!0,this.addproperty_button.disabled=!1,this.addproperty_holder.style.display="",this.refreshAddProperties())},hideAddProperty:function(){this.addproperty_holder&&this.adding_property&&(this.addproperty_holder.style.display="none",this.enable(),this.adding_property=!1)},toggleAddProperty:function(){this.adding_property?this.hideAddProperty():this.showAddProperty()},removeObjectProperty:function(a){this.editors[a]&&(this.editors[a].unregister(),delete this.editors[a],this.refreshValue(),this.layoutEditors())},addObjectProperty:function(a,b){var c=this;if(!this.editors[a]){if(this.cached_editors[a]){if(this.editors[a]=this.cached_editors[a],b)return;this.editors[a].register()}else{if(!(this.canHaveAdditionalProperties()||this.schema.properties&&this.schema.properties[a]))return;var d=c.getPropertySchema(a),e=c.jsoneditor.getEditorClass(d);if(c.editors[a]=c.jsoneditor.createEditor(e,{jsoneditor:c.jsoneditor,schema:d,path:c.path+"."+a,parent:c}),c.editors[a].preBuild(),!b){var f=c.theme.getChildEditorHolder();c.editor_holder.appendChild(f),c.editors[a].setContainer(f),c.editors[a].build(),c.editors[a].postBuild()}c.cached_editors[a]=c.editors[a]}b||(c.refreshValue(),c.layoutEditors())}},onChildEditorChange:function(a){this.refreshValue(),this._super(a)},canHaveAdditionalProperties:function(){return"boolean"==typeof this.schema.additionalProperties?this.schema.additionalProperties:!this.jsoneditor.options.no_additional_properties},destroy:function(){d(this.cached_editors,function(a,b){b.destroy()}),this.editor_holder&&(this.editor_holder.innerHTML=""),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.error_holder&&this.error_holder.parentNode&&this.error_holder.parentNode.removeChild(this.error_holder),this.editors=null,this.cached_editors=null,this.editor_holder&&this.editor_holder.parentNode&&this.editor_holder.parentNode.removeChild(this.editor_holder),this.editor_holder=null,this._super()},getValue:function(){var a=this._super();if(this.jsoneditor.options.remove_empty_properties||this.options.remove_empty_properties)for(var b in a)a.hasOwnProperty(b)&&(a[b]||delete a[b]);return a},refreshValue:function(){this.value={};for(var a in this.editors)this.editors.hasOwnProperty(a)&&(this.value[a]=this.editors[a].getValue());this.adding_property&&this.refreshAddProperties()},refreshAddProperties:function(){if(this.options.disable_properties||this.options.disable_properties!==!1&&this.jsoneditor.options.disable_properties)return void(this.addproperty_controls.style.display="none");var a,b=!1,c=!1,d=0,e=!1;for(a in this.editors)this.editors.hasOwnProperty(a)&&d++;b=this.canHaveAdditionalProperties()&&!("undefined"!=typeof this.schema.maxProperties&&d>=this.schema.maxProperties),this.addproperty_checkboxes&&(this.addproperty_list.innerHTML=""),this.addproperty_checkboxes={};for(a in this.cached_editors)this.cached_editors.hasOwnProperty(a)&&(this.addPropertyCheckbox(a),this.isRequired(this.cached_editors[a])&&a in this.editors&&(this.addproperty_checkboxes[a].disabled=!0),"undefined"!=typeof this.schema.minProperties&&d<=this.schema.minProperties?(this.addproperty_checkboxes[a].disabled=this.addproperty_checkboxes[a].checked,this.addproperty_checkboxes[a].checked||(e=!0)):a in this.editors?(e=!0,c=!0):b||this.schema.properties.hasOwnProperty(a)?(this.addproperty_checkboxes[a].disabled=!1,e=!0):this.addproperty_checkboxes[a].disabled=!0);this.canHaveAdditionalProperties()&&(e=!0);for(a in this.schema.properties)this.schema.properties.hasOwnProperty(a)&&(this.cached_editors[a]||(e=!0,this.addPropertyCheckbox(a)));e?this.canHaveAdditionalProperties()?b?this.addproperty_add.disabled=!1:this.addproperty_add.disabled=!0:(this.addproperty_add.style.display="none",this.addproperty_input.style.display="none"):(this.hideAddProperty(),this.addproperty_controls.style.display="none")},isRequired:function(a){return"boolean"==typeof a.schema.required?a.schema.required:Array.isArray(this.schema.required)?this.schema.required.indexOf(a.key)>-1:this.jsoneditor.options.required_by_default?!0:!1},setValue:function(a,b){var c=this;a=a||{},("object"!=typeof a||Array.isArray(a))&&(a={}),d(this.cached_editors,function(d,e){"undefined"!=typeof a[d]?(c.addObjectProperty(d),e.setValue(a[d],b)):b||c.isRequired(e)?e.setValue(e.getDefault(),b):c.removeObjectProperty(d)}),d(a,function(a,d){c.cached_editors[a]||(c.addObjectProperty(a),c.editors[a]&&c.editors[a].setValue(d,b))}),this.refreshValue(),this.layoutEditors(),this.onChange()},showValidationErrors:function(a){var b=this,c=[],e=[];if(d(a,function(a,d){d.path===b.path?c.push(d):e.push(d)}),this.error_holder)if(c.length){this.error_holder.innerHTML="",this.error_holder.style.display="",d(c,function(a,c){b.error_holder.appendChild(b.theme.getErrorMessage(c.message))})}else this.error_holder.style.display="none";this.options.table_row&&(c.length?this.theme.addTableRowError(this.container):this.theme.removeTableRowError(this.container)),d(this.editors,function(a,b){b.showValidationErrors(e)})}}),f.defaults.editors.array=f.AbstractEditor.extend({getDefault:function(){return this.schema["default"]||[]},register:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].register()},unregister:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].unregister()},getNumColumns:function(){var a=this.getItemInfo(0);return this.tabs_holder?Math.max(Math.min(12,a.width+2),4):a.width},enable:function(){if(this.add_row_button&&(this.add_row_button.disabled=!1),this.remove_all_rows_button&&(this.remove_all_rows_button.disabled=!1),this.delete_last_row_button&&(this.delete_last_row_button.disabled=!1),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].enable(),this.rows[a].moveup_button&&(this.rows[a].moveup_button.disabled=!1),this.rows[a].movedown_button&&(this.rows[a].movedown_button.disabled=!1),this.rows[a].delete_button&&(this.rows[a].delete_button.disabled=!1);this._super()},disable:function(){if(this.add_row_button&&(this.add_row_button.disabled=!0),this.remove_all_rows_button&&(this.remove_all_rows_button.disabled=!0),this.delete_last_row_button&&(this.delete_last_row_button.disabled=!0),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].disable(),this.rows[a].moveup_button&&(this.rows[a].moveup_button.disabled=!0),this.rows[a].movedown_button&&(this.rows[a].movedown_button.disabled=!0),this.rows[a].delete_button&&(this.rows[a].delete_button.disabled=!0);this._super()},preBuild:function(){this._super(),this.rows=[],this.row_cache=[],this.hide_delete_buttons=this.options.disable_array_delete||this.jsoneditor.options.disable_array_delete,this.hide_move_buttons=this.options.disable_array_reorder||this.jsoneditor.options.disable_array_reorder,this.hide_add_button=this.options.disable_array_add||this.jsoneditor.options.disable_array_add},build:function(){this.options.compact?(this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls),this.row_holder=document.createElement("div"),this.panel.appendChild(this.row_holder)):(this.header=document.createElement("span"),this.header.textContent=this.getTitle(),this.title=this.theme.getHeader(this.header,this.isRequired()),this.container.appendChild(this.title),this.title_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.error_holder=document.createElement("div"),this.container.appendChild(this.error_holder),"tabs"===this.schema.format?(this.controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.controls),this.tabs_holder=this.theme.getTabHolder(),this.container.appendChild(this.tabs_holder),this.row_holder=this.theme.getTabContentHolder(this.tabs_holder),this.active_tab=null):(this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.row_holder=document.createElement("div"),this.panel.appendChild(this.row_holder),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls))),this.addControls()},onChildEditorChange:function(a){this.refreshValue(),this.refreshTabs(!0),this._super(a)},getItemTitle:function(){if(!this.item_title)if(this.schema.items&&!Array.isArray(this.schema.items)){var a=this.jsoneditor.expandRefs(this.schema.items);this.item_title=a.title||"item"}else this.item_title="item";return this.item_title},getItemSchema:function(a){return Array.isArray(this.schema.items)?a>=this.schema.items.length?this.schema.additionalItems===!0?{}:this.schema.additionalItems?c({},this.schema.additionalItems):void 0:c({},this.schema.items[a]):this.schema.items?c({},this.schema.items):{}},getItemInfo:function(a){var b=this.getItemSchema(a);this.item_info=this.item_info||{};var c=JSON.stringify(b);return"undefined"!=typeof this.item_info[c]?this.item_info[c]:(b=this.jsoneditor.expandRefs(b),this.item_info[c]={title:b.title||"item","default":b["default"],width:12,child_editors:b.properties||b.items},this.item_info[c])},getElementEditor:function(a){var b=this.getItemInfo(a),c=this.getItemSchema(a);c=this.jsoneditor.expandRefs(c),c.title=b.title+" "+(a+1);var d,e=this.jsoneditor.getEditorClass(c);d=this.tabs_holder?this.theme.getTabContent():b.child_editors?this.theme.getChildEditorHolder():this.theme.getIndentedPanel(),this.row_holder.appendChild(d);var f=this.jsoneditor.createEditor(e,{jsoneditor:this.jsoneditor,schema:c,container:d,path:this.path+"."+a,parent:this,required:!0});return f.preBuild(),f.build(),f.postBuild(),f.title_controls||(f.array_controls=this.theme.getButtonHolder(),d.appendChild(f.array_controls)),f},destroy:function(){this.empty(!0),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.row_holder&&this.row_holder.parentNode&&this.row_holder.parentNode.removeChild(this.row_holder),this.controls&&this.controls.parentNode&&this.controls.parentNode.removeChild(this.controls),this.panel&&this.panel.parentNode&&this.panel.parentNode.removeChild(this.panel),this.rows=this.row_cache=this.title=this.description=this.row_holder=this.panel=this.controls=null,this._super()},empty:function(a){if(this.rows){var b=this;d(this.rows,function(c,d){a&&(d.tab&&d.tab.parentNode&&d.tab.parentNode.removeChild(d.tab),b.destroyRow(d,!0),b.row_cache[c]=null),b.rows[c]=null}),b.rows=[],a&&(b.row_cache=[])}},destroyRow:function(a,b){var c=a.container;b?(a.destroy(),c.parentNode&&c.parentNode.removeChild(c),a.tab&&a.tab.parentNode&&a.tab.parentNode.removeChild(a.tab)):(a.tab&&(a.tab.style.display="none"),c.style.display="none",a.unregister())},getMax:function(){return Array.isArray(this.schema.items)&&this.schema.additionalItems===!1?Math.min(this.schema.items.length,this.schema.maxItems||1/0):this.schema.maxItems||1/0},refreshTabs:function(a){var b=this;d(this.rows,function(c,d){d.tab&&(a?d.tab_text.textContent=d.getHeaderText():d.tab===b.active_tab?(b.theme.markTabActive(d.tab),d.container.style.display=""):(b.theme.markTabInactive(d.tab),d.container.style.display="none"))})},setValue:function(a,b){a=a||[],Array.isArray(a)||(a=[a]);var c=JSON.stringify(a);if(c!==this.serialized){if(this.schema.minItems)for(;a.length<this.schema.minItems;)a.push(this.getItemInfo(a.length)["default"]);this.getMax()&&a.length>this.getMax()&&(a=a.slice(0,this.getMax()));var e=this;d(a,function(a,c){e.rows[a]?e.rows[a].setValue(c,b):e.row_cache[a]?(e.rows[a]=e.row_cache[a],e.rows[a].setValue(c,b),e.rows[a].container.style.display="",e.rows[a].tab&&(e.rows[a].tab.style.display=""),e.rows[a].register()):e.addRow(c,b)});for(var f=a.length;f<e.rows.length;f++)e.destroyRow(e.rows[f]),e.rows[f]=null;e.rows=e.rows.slice(0,a.length);var g=null;d(e.rows,function(a,b){return b.tab===e.active_tab?(g=b.tab,!1):void 0}),!g&&e.rows.length&&(g=e.rows[0].tab),e.active_tab=g,e.refreshValue(b),e.refreshTabs(!0),e.refreshTabs(),e.onChange()}},refreshValue:function(a){var b=this,c=this.value?this.value.length:0;if(this.value=[],d(this.rows,function(a,c){b.value[a]=c.getValue()}),c!==this.value.length||a){var e=this.schema.minItems&&this.schema.minItems>=this.rows.length;d(this.rows,function(a,c){c.movedown_button&&(a===b.rows.length-1?c.movedown_button.style.display="none":c.movedown_button.style.display=""),c.delete_button&&(e?c.delete_button.style.display="none":c.delete_button.style.display=""),b.value[a]=c.getValue()});var f=!1;this.value.length?1===this.value.length?(this.remove_all_rows_button.style.display="none",e||this.hide_delete_buttons?this.delete_last_row_button.style.display="none":(this.delete_last_row_button.style.display="",f=!0)):e||this.hide_delete_buttons?(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"):(this.delete_last_row_button.style.display="",this.remove_all_rows_button.style.display="",f=!0):(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"),this.getMax()&&this.getMax()<=this.rows.length||this.hide_add_button?this.add_row_button.style.display="none":(this.add_row_button.style.display="",f=!0),!this.collapsed&&f?this.controls.style.display="inline-block":this.controls.style.display="none"}},addRow:function(a,b){var c=this,e=this.rows.length;c.rows[e]=this.getElementEditor(e),c.row_cache[e]=c.rows[e],c.tabs_holder&&(c.rows[e].tab_text=document.createElement("span"),c.rows[e].tab_text.textContent=c.rows[e].getHeaderText(),c.rows[e].tab=c.theme.getTab(c.rows[e].tab_text),c.rows[e].tab.addEventListener("click",function(a){c.active_tab=c.rows[e].tab,c.refreshTabs(),a.preventDefault(),a.stopPropagation()}),c.theme.addTab(c.tabs_holder,c.rows[e].tab));var f=c.rows[e].title_controls||c.rows[e].array_controls;c.hide_delete_buttons||(c.rows[e].delete_button=this.getButton(c.getItemTitle(),"delete","Delete "+c.getItemTitle()),c.rows[e].delete_button.className+=" delete",c.rows[e].delete_button.setAttribute("data-i",e),c.rows[e].delete_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i"),e=c.getValue(),f=[],g=null;d(e,function(a,d){return a===b?void(c.rows[a].tab===c.active_tab&&(c.rows[a+1]?g=c.rows[a].tab:a&&(g=c.rows[a-1].tab))):void f.push(d)}),c.setValue(f),g&&(c.active_tab=g,c.refreshTabs()),c.onChange(!0)}),f&&f.appendChild(c.rows[e].delete_button)),e&&!c.hide_move_buttons&&(c.rows[e].moveup_button=this.getButton("","moveup","Move up"),c.rows[e].moveup_button.className+=" moveup",c.rows[e].moveup_button.setAttribute("data-i",e),c.rows[e].moveup_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i");if(!(0>=b)){var d=c.getValue(),e=d[b-1];d[b-1]=d[b],d[b]=e,c.setValue(d),c.active_tab=c.rows[b-1].tab,c.refreshTabs(),c.onChange(!0)}}),f&&f.appendChild(c.rows[e].moveup_button)),c.hide_move_buttons||(c.rows[e].movedown_button=this.getButton("","movedown","Move down"),c.rows[e].movedown_button.className+=" movedown",c.rows[e].movedown_button.setAttribute("data-i",e),c.rows[e].movedown_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var b=1*this.getAttribute("data-i"),d=c.getValue();if(!(b>=d.length-1)){var e=d[b+1];d[b+1]=d[b],d[b]=e,c.setValue(d),c.active_tab=c.rows[b+1].tab,c.refreshTabs(),c.onChange(!0)}}),f&&f.appendChild(c.rows[e].movedown_button)),a&&c.rows[e].setValue(a,b),c.refreshTabs()},addControls:function(){var a=this;this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls.appendChild(this.toggle_button);var b=a.row_holder.style.display,c=a.controls.style.display;this.toggle_button.addEventListener("click",function(d){d.preventDefault(),d.stopPropagation(),a.collapsed?(a.collapsed=!1,a.panel&&(a.panel.style.display=""),a.row_holder.style.display=b,a.tabs_holder&&(a.tabs_holder.style.display=""),a.controls.style.display=c,a.setButtonText(this,"","collapse","Collapse")):(a.collapsed=!0,a.row_holder.style.display="none",a.tabs_holder&&(a.tabs_holder.style.display="none"),a.controls.style.display="none",a.panel&&(a.panel.style.display="none"),a.setButtonText(this,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none"),this.add_row_button=this.getButton(this.getItemTitle(),"add","Add "+this.getItemTitle()),this.add_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.rows.length;a.row_cache[c]?(a.rows[c]=a.row_cache[c],a.rows[c].setValue(a.rows[c].getDefault()),a.rows[c].container.style.display="",a.rows[c].tab&&(a.rows[c].tab.style.display=""),a.rows[c].register()):a.addRow(),a.active_tab=a.rows[c].tab,a.refreshTabs(),a.refreshValue(),a.onChange(!0)}),a.controls.appendChild(this.add_row_button),this.delete_last_row_button=this.getButton("Last "+this.getItemTitle(),"delete","Delete Last "+this.getItemTitle()),
+this.delete_last_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.getValue(),d=null;a.rows.length>1&&a.rows[a.rows.length-1].tab===a.active_tab&&(d=a.rows[a.rows.length-2].tab),c.pop(),a.setValue(c),d&&(a.active_tab=d,a.refreshTabs()),a.onChange(!0)}),a.controls.appendChild(this.delete_last_row_button),this.remove_all_rows_button=this.getButton("All","delete","Delete All"),this.remove_all_rows_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.setValue([]),a.onChange(!0)}),a.controls.appendChild(this.remove_all_rows_button),a.tabs&&(this.add_row_button.style.width="100%",this.add_row_button.style.textAlign="left",this.add_row_button.style.marginBottom="3px",this.delete_last_row_button.style.width="100%",this.delete_last_row_button.style.textAlign="left",this.delete_last_row_button.style.marginBottom="3px",this.remove_all_rows_button.style.width="100%",this.remove_all_rows_button.style.textAlign="left",this.remove_all_rows_button.style.marginBottom="3px")},showValidationErrors:function(a){var b=this,c=[],e=[];if(d(a,function(a,d){d.path===b.path?c.push(d):e.push(d)}),this.error_holder)if(c.length){this.error_holder.innerHTML="",this.error_holder.style.display="",d(c,function(a,c){b.error_holder.appendChild(b.theme.getErrorMessage(c.message))})}else this.error_holder.style.display="none";d(this.rows,function(a,b){b.showValidationErrors(e)})}}),f.defaults.editors.table=f.defaults.editors.array.extend({register:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].register()},unregister:function(){if(this._super(),this.rows)for(var a=0;a<this.rows.length;a++)this.rows[a].unregister()},getNumColumns:function(){return Math.max(Math.min(12,this.width),3)},preBuild:function(){var a=this.jsoneditor.expandRefs(this.schema.items||{});this.item_title=a.title||"row",this.item_default=a["default"]||null,this.item_has_child_editors=a.properties||a.items,this.width=12,this._super()},build:function(){var a=this;this.table=this.theme.getTable(),this.container.appendChild(this.table),this.thead=this.theme.getTableHead(),this.table.appendChild(this.thead),this.header_row=this.theme.getTableRow(),this.thead.appendChild(this.header_row),this.row_holder=this.theme.getTableBody(),this.table.appendChild(this.row_holder);var b=this.getElementEditor(0,!0);if(this.item_default=b.getDefault(),this.width=b.getNumColumns()+2,this.options.compact?(this.panel=document.createElement("div"),this.container.appendChild(this.panel)):(this.title=this.theme.getHeader(this.getTitle(),this.isRequired()),this.container.appendChild(this.title),this.title_controls=this.theme.getHeaderButtonHolder(),this.title.appendChild(this.title_controls),this.schema.description&&(this.description=this.theme.getDescription(this.schema.description),this.container.appendChild(this.description)),this.panel=this.theme.getIndentedPanel(),this.container.appendChild(this.panel),this.error_holder=document.createElement("div"),this.panel.appendChild(this.error_holder)),this.panel.appendChild(this.table),this.controls=this.theme.getButtonHolder(),this.panel.appendChild(this.controls),this.item_has_child_editors)for(var c=b.getChildEditors(),d=b.property_order||Object.keys(c),e=0;e<d.length;e++){var f=a.theme.getTableHeaderCell(c[d[e]].getTitle());c[d[e]].options.hidden&&(f.style.display="none"),a.header_row.appendChild(f)}else a.header_row.appendChild(a.theme.getTableHeaderCell(this.item_title));b.destroy(),this.row_holder.innerHTML="",this.controls_header_cell=a.theme.getTableHeaderCell(" "),a.header_row.appendChild(this.controls_header_cell),this.addControls()},onChildEditorChange:function(a){this.refreshValue(),this._super()},getItemDefault:function(){return c({},{"default":this.item_default})["default"]},getItemTitle:function(){return this.item_title},getElementEditor:function(a,b){var d=c({},this.schema.items),e=this.jsoneditor.getEditorClass(d,this.jsoneditor),f=this.row_holder.appendChild(this.theme.getTableRow()),g=f;this.item_has_child_editors||(g=this.theme.getTableCell(),f.appendChild(g));var h=this.jsoneditor.createEditor(e,{jsoneditor:this.jsoneditor,schema:d,container:g,path:this.path+"."+a,parent:this,compact:!0,table_row:!0});return h.preBuild(),b||(h.build(),h.postBuild(),h.controls_cell=f.appendChild(this.theme.getTableCell()),h.row=f,h.table_controls=this.theme.getButtonHolder(),h.controls_cell.appendChild(h.table_controls),h.table_controls.style.margin=0,h.table_controls.style.padding=0),h},destroy:function(){this.innerHTML="",this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.row_holder&&this.row_holder.parentNode&&this.row_holder.parentNode.removeChild(this.row_holder),this.table&&this.table.parentNode&&this.table.parentNode.removeChild(this.table),this.panel&&this.panel.parentNode&&this.panel.parentNode.removeChild(this.panel),this.rows=this.title=this.description=this.row_holder=this.table=this.panel=null,this._super()},setValue:function(a,b){if(a=a||[],this.schema.minItems)for(;a.length<this.schema.minItems;)a.push(this.getItemDefault());this.schema.maxItems&&a.length>this.schema.maxItems&&(a=a.slice(0,this.schema.maxItems));var c=JSON.stringify(a);if(c!==this.serialized){var e=!1,f=this;d(a,function(a,b){f.rows[a]?f.rows[a].setValue(b):(f.addRow(b),e=!0)});for(var g=a.length;g<f.rows.length;g++){var h=f.rows[g].container;f.item_has_child_editors||f.rows[g].row.parentNode.removeChild(f.rows[g].row),f.rows[g].destroy(),h.parentNode&&h.parentNode.removeChild(h),f.rows[g]=null,e=!0}f.rows=f.rows.slice(0,a.length),f.refreshValue(),(e||b)&&f.refreshRowButtons(),f.onChange()}},refreshRowButtons:function(){var a=this,b=this.schema.minItems&&this.schema.minItems>=this.rows.length,c=!1;d(this.rows,function(d,e){e.movedown_button&&(d===a.rows.length-1?e.movedown_button.style.display="none":(c=!0,e.movedown_button.style.display="")),e.delete_button&&(b?e.delete_button.style.display="none":(c=!0,e.delete_button.style.display="")),e.moveup_button&&(c=!0)}),d(this.rows,function(a,b){c?b.controls_cell.style.display="":b.controls_cell.style.display="none"}),c?this.controls_header_cell.style.display="":this.controls_header_cell.style.display="none";var e=!1;this.value.length?1===this.value.length||this.hide_delete_buttons?(this.table.style.display="",this.remove_all_rows_button.style.display="none",b||this.hide_delete_buttons?this.delete_last_row_button.style.display="none":(this.delete_last_row_button.style.display="",e=!0)):(this.table.style.display="",b||this.hide_delete_buttons?(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none"):(this.delete_last_row_button.style.display="",this.remove_all_rows_button.style.display="",e=!0)):(this.delete_last_row_button.style.display="none",this.remove_all_rows_button.style.display="none",this.table.style.display="none"),this.schema.maxItems&&this.schema.maxItems<=this.rows.length||this.hide_add_button?this.add_row_button.style.display="none":(this.add_row_button.style.display="",e=!0),e?this.controls.style.display="":this.controls.style.display="none"},refreshValue:function(){var a=this;this.value=[],d(this.rows,function(b,c){a.value[b]=c.getValue()}),this.serialized=JSON.stringify(this.value)},addRow:function(a){var b=this,c=this.rows.length;b.rows[c]=this.getElementEditor(c);var e=b.rows[c].table_controls;this.hide_delete_buttons||(b.rows[c].delete_button=this.getButton("","delete","Delete"),b.rows[c].delete_button.className+=" delete",b.rows[c].delete_button.setAttribute("data-i",c),b.rows[c].delete_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i"),e=b.getValue(),f=[];d(e,function(a,b){a!==c&&f.push(b)}),b.setValue(f),b.onChange(!0)}),e.appendChild(b.rows[c].delete_button)),c&&!this.hide_move_buttons&&(b.rows[c].moveup_button=this.getButton("","moveup","Move up"),b.rows[c].moveup_button.className+=" moveup",b.rows[c].moveup_button.setAttribute("data-i",c),b.rows[c].moveup_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i");if(!(0>=c)){var d=b.getValue(),e=d[c-1];d[c-1]=d[c],d[c]=e,b.setValue(d),b.onChange(!0)}}),e.appendChild(b.rows[c].moveup_button)),this.hide_move_buttons||(b.rows[c].movedown_button=this.getButton("","movedown","Move down"),b.rows[c].movedown_button.className+=" movedown",b.rows[c].movedown_button.setAttribute("data-i",c),b.rows[c].movedown_button.addEventListener("click",function(a){a.preventDefault(),a.stopPropagation();var c=1*this.getAttribute("data-i"),d=b.getValue();if(!(c>=d.length-1)){var e=d[c+1];d[c+1]=d[c],d[c]=e,b.setValue(d),b.onChange(!0)}}),e.appendChild(b.rows[c].movedown_button)),a&&b.rows[c].setValue(a)},addControls:function(){var a=this;this.collapsed=!1,this.toggle_button=this.getButton("","collapse","Collapse"),this.title_controls&&(this.title_controls.appendChild(this.toggle_button),this.toggle_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.collapsed?(a.collapsed=!1,a.panel.style.display="",a.setButtonText(this,"","collapse","Collapse")):(a.collapsed=!0,a.panel.style.display="none",a.setButtonText(this,"","expand","Expand"))}),this.options.collapsed&&e(this.toggle_button,"click"),this.schema.options&&"undefined"!=typeof this.schema.options.disable_collapse?this.schema.options.disable_collapse&&(this.toggle_button.style.display="none"):this.jsoneditor.options.disable_collapse&&(this.toggle_button.style.display="none")),this.add_row_button=this.getButton(this.getItemTitle(),"add","Add "+this.getItemTitle()),this.add_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.addRow(),a.refreshValue(),a.refreshRowButtons(),a.onChange(!0)}),a.controls.appendChild(this.add_row_button),this.delete_last_row_button=this.getButton("Last "+this.getItemTitle(),"delete","Delete Last "+this.getItemTitle()),this.delete_last_row_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation();var c=a.getValue();c.pop(),a.setValue(c),a.onChange(!0)}),a.controls.appendChild(this.delete_last_row_button),this.remove_all_rows_button=this.getButton("All","delete","Delete All"),this.remove_all_rows_button.addEventListener("click",function(b){b.preventDefault(),b.stopPropagation(),a.setValue([]),a.onChange(!0)}),a.controls.appendChild(this.remove_all_rows_button)}}),f.defaults.editors.multiple=f.AbstractEditor.extend({register:function(){if(this.editors){for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].unregister();this.editors[this.type]&&this.editors[this.type].register()}this._super()},unregister:function(){if(this._super(),this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].unregister()},getNumColumns:function(){return this.editors[this.type]?Math.max(this.editors[this.type].getNumColumns(),4):4},enable:function(){if(this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].enable();this.switcher.disabled=!1,this._super()},disable:function(){if(this.editors)for(var a=0;a<this.editors.length;a++)this.editors[a]&&this.editors[a].disable();this.switcher.disabled=!0,this._super()},switchEditor:function(a){var b=this;this.editors[a]||this.buildChildEditor(a),b.type=a,b.register();var c=b.getValue();d(b.editors,function(a,d){d&&(b.type===a?(b.keep_values&&d.setValue(c,!0),d.container.style.display=""):d.container.style.display="none")}),b.refreshValue(),b.refreshHeaderText()},buildChildEditor:function(a){var b=this,d=this.types[a],e=b.theme.getChildEditorHolder();b.editor_holder.appendChild(e);var f;"string"==typeof d?(f=c({},b.schema),f.type=d):(f=c({},b.schema,d),f=b.jsoneditor.expandRefs(f),d.required&&Array.isArray(d.required)&&b.schema.required&&Array.isArray(b.schema.required)&&(f.required=b.schema.required.concat(d.required)));var g=b.jsoneditor.getEditorClass(f);b.editors[a]=b.jsoneditor.createEditor(g,{jsoneditor:b.jsoneditor,schema:f,container:e,path:b.path,parent:b,required:!0}),b.editors[a].preBuild(),b.editors[a].build(),b.editors[a].postBuild(),b.editors[a].header&&(b.editors[a].header.style.display="none"),b.editors[a].option=b.switcher_options[a],e.addEventListener("change_header_text",function(){b.refreshHeaderText()}),a!==b.type&&(e.style.display="none")},preBuild:function(){if(this.types=[],this.type=0,this.editors=[],this.validators=[],this.keep_values=!0,"undefined"!=typeof this.jsoneditor.options.keep_oneof_values&&(this.keep_values=this.jsoneditor.options.keep_oneof_values),"undefined"!=typeof this.options.keep_oneof_values&&(this.keep_values=this.options.keep_oneof_values),this.schema.oneOf)this.oneOf=!0,this.types=this.schema.oneOf,d(this.types,function(a,b){}),delete this.schema.oneOf;else{if(this.schema.type&&"any"!==this.schema.type)Array.isArray(this.schema.type)?this.types=this.schema.type:this.types=[this.schema.type];else if(this.types=["string","number","integer","boolean","object","array","null"],this.schema.disallow){var a=this.schema.disallow;"object"==typeof a&&Array.isArray(a)||(a=[a]);var b=[];d(this.types,function(c,d){-1===a.indexOf(d)&&b.push(d)}),this.types=b}delete this.schema.type}this.display_text=this.getDisplayText(this.types)},build:function(){var a=this,b=this.container;this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.container.appendChild(this.header),this.switcher=this.theme.getSwitcher(this.display_text),b.appendChild(this.switcher),this.switcher.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.switchEditor(a.display_text.indexOf(this.value)),a.onChange(!0)}),this.editor_holder=document.createElement("div"),b.appendChild(this.editor_holder),this.switcher_options=this.theme.getSwitcherOptions(this.switcher),d(this.types,function(b,d){a.editors[b]=!1;var e;"string"==typeof d?(e=c({},a.schema),e.type=d):(e=c({},a.schema,d),d.required&&Array.isArray(d.required)&&a.schema.required&&Array.isArray(a.schema.required)&&(e.required=a.schema.required.concat(d.required))),a.validators[b]=new f.Validator(a.jsoneditor,e)}),this.switchEditor(0)},onChildEditorChange:function(a){this.editors[this.type]&&(this.refreshValue(),this.refreshHeaderText()),this._super()},refreshHeaderText:function(){var a=this.getDisplayText(this.types);d(this.switcher_options,function(b,c){c.textContent=a[b]})},refreshValue:function(){this.value=this.editors[this.type].getValue()},setValue:function(a,b){var c=this;d(this.validators,function(b,d){return d.validate(a).length?void 0:(c.type=b,c.switcher.value=c.display_text[b],!1)}),this.switchEditor(this.type),this.editors[this.type].setValue(a,b),this.refreshValue(),c.onChange()},destroy:function(){d(this.editors,function(a,b){b&&b.destroy()}),this.editor_holder&&this.editor_holder.parentNode&&this.editor_holder.parentNode.removeChild(this.editor_holder),this.switcher&&this.switcher.parentNode&&this.switcher.parentNode.removeChild(this.switcher),this._super()},showValidationErrors:function(a){var b=this;this.oneOf?d(this.editors,function(e,f){if(f){var g=b.path+".oneOf["+e+"]",h=[];d(a,function(a,d){if(d.path.substr(0,g.length)===g){var e=c({},d);e.path=b.path+e.path.substr(g.length),h.push(e)}}),f.showValidationErrors(h)}}):d(this.editors,function(b,c){c&&c.showValidationErrors(a)})}}),f.defaults.editors["enum"]=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){this.container;this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.container.appendChild(this.title),this.options.enum_titles=this.options.enum_titles||[],this["enum"]=this.schema["enum"],this.selected=0,this.select_options=[],this.html_values=[];for(var a=this,b=0;b<this["enum"].length;b++)this.select_options[b]=this.options.enum_titles[b]||"Value "+(b+1),this.html_values[b]=this.getHTML(this["enum"][b]);this.switcher=this.theme.getSwitcher(this.select_options),this.container.appendChild(this.switcher),this.display_area=this.theme.getIndentedPanel(),this.container.appendChild(this.display_area),this.options.hide_display&&(this.display_area.style.display="none"),this.switcher.addEventListener("change",function(){a.selected=a.select_options.indexOf(this.value),a.value=a["enum"][a.selected],a.refreshValue(),a.onChange(!0)}),this.value=this["enum"][0],this.refreshValue(),1===this["enum"].length&&(this.switcher.style.display="none")},refreshValue:function(){var a=this;a.selected=-1;var b=JSON.stringify(this.value);return d(this["enum"],function(c,d){return b===JSON.stringify(d)?(a.selected=c,!1):void 0}),a.selected<0?void a.setValue(a["enum"][0]):(this.switcher.value=this.select_options[this.selected],void(this.display_area.innerHTML=this.html_values[this.selected]))},enable:function(){this.always_disabled||(this.switcher.disabled=!1),this._super()},disable:function(){this.switcher.disabled=!0,this._super()},getHTML:function(a){var b=this;if(null===a)return"<em>null</em>";if("object"==typeof a){var c="";return d(a,function(d,e){var f=b.getHTML(e);Array.isArray(a)||(f="<div><em>"+d+"</em>: "+f+"</div>"),c+="<li>"+f+"</li>"}),c=Array.isArray(a)?"<ol>"+c+"</ol>":"<ul style='margin-top:0;margin-bottom:0;padding-top:0;padding-bottom:0;'>"+c+"</ul>"}return"boolean"==typeof a?a?"true":"false":"string"==typeof a?a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):a},setValue:function(a){this.value!==a&&(this.value=a,this.refreshValue(),this.onChange())},destroy:function(){this.display_area&&this.display_area.parentNode&&this.display_area.parentNode.removeChild(this.display_area),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.switcher&&this.switcher.parentNode&&this.switcher.parentNode.removeChild(this.switcher),this._super()}}),f.defaults.editors.select=f.AbstractEditor.extend({setValue:function(a,b){a=this.typecast(a||"");var c=a;this.enum_values.indexOf(c)<0&&(c=this.enum_values[0]),this.value!==c&&(this.input.value=this.enum_options[this.enum_values.indexOf(c)],this.select2&&this.select2.select2("val",this.input.value),this.value=c,this.onChange())},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){if(!this.enum_options)return 3;for(var a=this.getTitle().length,b=0;b<this.enum_options.length;b++)a=Math.max(a,this.enum_options[b].length+4);return Math.min(12,Math.max(a/7,2))},typecast:function(a){return"boolean"===this.schema.type?!!a:"number"===this.schema.type?1*a:"integer"===this.schema.type?Math.floor(1*a):""+a},getValue:function(){return this.value},preBuild:function(){var a=this;if(this.input_type="select",this.enum_options=[],this.enum_values=[],this.enum_display=[],this.schema["enum"]){var b=this.schema.options&&this.schema.options.enum_titles||[];d(this.schema["enum"],function(c,d){a.enum_options[c]=""+d,a.enum_display[c]=""+(b[c]||d),a.enum_values[c]=a.typecast(d)}),this.isRequired()||(a.enum_display.unshift(" "),a.enum_options.unshift("undefined"),a.enum_values.unshift(void 0))}else if("boolean"===this.schema.type)a.enum_display=this.schema.options&&this.schema.options.enum_titles||["true","false"],a.enum_options=["1",""],a.enum_values=[!0,!1],this.isRequired()||(a.enum_display.unshift(" "),a.enum_options.unshift("undefined"),a.enum_values.unshift(void 0));else{if(!this.schema.enumSource)throw"'select' editor requires the enum property to be set.";if(this.enumSource=[],this.enum_display=[],this.enum_options=[],this.enum_values=[],Array.isArray(this.schema.enumSource))for(h=0;h<this.schema.enumSource.length;h++)"string"==typeof this.schema.enumSource[h]?this.enumSource[h]={source:this.schema.enumSource[h]}:Array.isArray(this.schema.enumSource[h])?this.enumSource[h]=this.schema.enumSource[h]:this.enumSource[h]=c({},this.schema.enumSource[h]);else this.schema.enumValue?this.enumSource=[{source:this.schema.enumSource,value:this.schema.enumValue}]:this.enumSource=[{source:this.schema.enumSource}];for(h=0;h<this.enumSource.length;h++)this.enumSource[h].value&&(this.enumSource[h].value=this.jsoneditor.compileTemplate(this.enumSource[h].value,this.template_engine)),this.enumSource[h].title&&(this.enumSource[h].title=this.jsoneditor.compileTemplate(this.enumSource[h].title,this.template_engine)),this.enumSource[h].filter&&(this.enumSource[h].filter=this.jsoneditor.compileTemplate(this.enumSource[h].filter,this.template_engine))}},build:function(){var a=this;this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.options.compact&&(this.container.className+=" compact"),this.input=this.theme.getSelectInput(this.enum_options),this.theme.setSelectOptions(this.input,this.enum_options,this.enum_display),(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.onInputChange()}),this.control=this.theme.getFormControl(this.label,this.input,this.description),this.container.appendChild(this.control),this.value=this.enum_values[0]},onInputChange:function(){var a=this.input.value,b=a;-1===this.enum_options.indexOf(a)&&(b=this.enum_options[0]),this.value=this.enum_values[this.enum_options.indexOf(a)],this.onChange(!0)},setupSelect2:function(){if(window.jQuery&&window.jQuery.fn&&window.jQuery.fn.select2&&(this.enum_options.length>2||this.enum_options.length&&this.enumSource)){var a=c({},f.plugins.select2);this.schema.options&&this.schema.options.select2_options&&(a=c(a,this.schema.options.select2_options)),this.select2=window.jQuery(this.input).select2(a);var b=this;this.select2.on("select2-blur",function(){b.input.value=b.select2.select2("val"),b.onInputChange()})}else this.select2=null},postBuild:function(){this._super(),this.theme.afterInputReady(this.input),this.setupSelect2()},onWatchedFieldChange:function(){var a,b;if(this.enumSource){a=this.getWatchedFieldValues();for(var c=[],d=[],e=0;e<this.enumSource.length;e++)if(Array.isArray(this.enumSource[e]))c=c.concat(this.enumSource[e]),d=d.concat(this.enumSource[e]);else{var f=[];if(f=Array.isArray(this.enumSource[e].source)?this.enumSource[e].source:a[this.enumSource[e].source]){if(this.enumSource[e].slice&&(f=Array.prototype.slice.apply(f,this.enumSource[e].slice)),this.enumSource[e].filter){var g=[];for(b=0;b<f.length;b++)this.enumSource[e].filter({i:b,item:f[b],watched:a})&&g.push(f[b]);f=g}var h=[],i=[];for(b=0;b<f.length;b++){var j=f[b];this.enumSource[e].value?i[b]=this.enumSource[e].value({i:b,item:j}):i[b]=f[b],this.enumSource[e].title?h[b]=this.enumSource[e].title({i:b,item:j}):h[b]=i[b]}c=c.concat(i),d=d.concat(h)}}var k=this.value;this.theme.setSelectOptions(this.input,c,d),this.enum_options=c,this.enum_display=d,this.enum_values=c,this.select2&&this.select2.select2("destroy"),-1!==c.indexOf(k)?(this.input.value=k,this.value=k):(this.input.value=c[0],this.value=c[0]||"",this.parent?this.parent.onChildEditorChange(this):this.jsoneditor.onChange(),this.jsoneditor.notifyWatchers(this.path)),this.setupSelect2()}this._super()},enable:function(){this.always_disabled||(this.input.disabled=!1,this.select2&&this.select2.select2("enable",!0)),this._super()},disable:function(){this.input.disabled=!0,this.select2&&this.select2.select2("enable",!1),this._super()},destroy:function(){this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.select2&&(this.select2.select2("destroy"),this.select2=null),this._super()}}),f.defaults.editors.multiselect=f.AbstractEditor.extend({preBuild:function(){this._super(),this.select_options={},this.select_values={};var a=this.jsoneditor.expandRefs(this.schema.items||{}),b=a["enum"]||[];for(this.option_keys=[],h=0;h<b.length;h++)this.sanitize(b[h])===b[h]&&(this.option_keys.push(b[h]+""),this.select_values[b[h]+""]=b[h])},build:function(){var a,b=this;if(this.options.compact||(this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),!this.schema.format&&this.option_keys.length<8||"checkbox"===this.schema.format){for(this.input_type="checkboxes",this.inputs={},this.controls={},a=0;a<this.option_keys.length;a++){this.inputs[this.option_keys[a]]=this.theme.getCheckbox(),this.select_options[this.option_keys[a]]=this.inputs[this.option_keys[a]];var c=this.theme.getCheckboxLabel(this.option_keys[a]);this.controls[this.option_keys[a]]=this.theme.getFormControl(c,this.inputs[this.option_keys[a]])}this.control=this.theme.getMultiCheckboxHolder(this.controls,this.label,this.description)}else{for(this.input_type="select",this.input=this.theme.getSelectInput(this.option_keys),this.input.multiple=!0,this.input.size=Math.min(10,this.option_keys.length),a=0;a<this.option_keys.length;a++)this.select_options[this.option_keys[a]]=this.input.children[a];(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.control=this.theme.getFormControl(this.label,this.input,this.description)}this.container.appendChild(this.control),this.control.addEventListener("change",function(c){c.preventDefault(),c.stopPropagation();var d=[];for(a=0;a<b.option_keys.length;a++)(b.select_options[b.option_keys[a]].selected||b.select_options[b.option_keys[a]].checked)&&d.push(b.select_values[b.option_keys[a]]);b.updateValue(d),b.onChange(!0)})},setValue:function(a,b){var c;for(a=a||[],"object"!=typeof a?a=[a]:Array.isArray(a)||(a=[]),c=0;c<a.length;c++)"string"!=typeof a[c]&&(a[c]+="");for(c in this.select_options)this.select_options.hasOwnProperty(c)&&(this.select_options[c]["select"===this.input_type?"selected":"checked"]=-1!==a.indexOf(c));this.updateValue(a),this.onChange()},setupSelect2:function(){if(window.jQuery&&window.jQuery.fn&&window.jQuery.fn.select2){var a=window.jQuery.extend({},f.plugins.select2);this.schema.options&&this.schema.options.select2_options&&(a=c(a,this.schema.options.select2_options)),this.select2=window.jQuery(this.input).select2(a);var b=this;this.select2.on("select2-blur",function(){var a=b.select2.select2("val");b.value=a,b.onChange(!0)})}else this.select2=null},onInputChange:function(){this.value=this.input.value,this.onChange(!0)},postBuild:function(){this._super(),this.setupSelect2()},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){var a=this.getTitle().length;for(var b in this.select_values)this.select_values.hasOwnProperty(b)&&(a=Math.max(a,(this.select_values[b]+"").length+4));return Math.min(12,Math.max(a/7,2))},updateValue:function(a){for(var b=!1,c=[],d=0;d<a.length;d++)if(this.select_options[a[d]+""]){var e=this.sanitize(this.select_values[a[d]]);c.push(e),e!==a[d]&&(b=!0)}else b=!0;return this.value=c,this.select2&&this.select2.select2("val",this.value),b},sanitize:function(a){return"number"===this.schema.items.type?1*a:"integer"===this.schema.items.type?Math.floor(1*a):""+a},enable:function(){if(!this.always_disabled){if(this.input)this.input.disabled=!1;else if(this.inputs)for(var a in this.inputs)this.inputs.hasOwnProperty(a)&&(this.inputs[a].disabled=!1);this.select2&&this.select2.select2("enable",!0)}this._super()},disable:function(){if(this.input)this.input.disabled=!0;else if(this.inputs)for(var a in this.inputs)this.inputs.hasOwnProperty(a)&&(this.inputs[a].disabled=!0);this.select2&&this.select2.select2("enable",!1),this._super()},destroy:function(){this.select2&&(this.select2.select2("destroy"),this.select2=null),this._super()}}),f.defaults.editors.base64=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){var a=this;if(this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.input=this.theme.getFormInputField("hidden"),this.container.appendChild(this.input),!this.schema.readOnly&&!this.schema.readonly){if(!window.FileReader)throw"FileReader required for base64 editor";this.uploader=this.theme.getFormInputField("file"),this.uploader.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),this.files&&this.files.length){var c=new FileReader;c.onload=function(b){a.value=b.target.result,a.refreshPreview(),a.onChange(!0),c=null},c.readAsDataURL(this.files[0])}})}this.preview=this.theme.getFormInputDescription(this.schema.description),this.container.appendChild(this.preview),this.control=this.theme.getFormControl(this.label,this.uploader||this.input,this.preview),this.container.appendChild(this.control)},refreshPreview:function(){if(this.last_preview!==this.value&&(this.last_preview=this.value,this.preview.innerHTML="",this.value)){var a=this.value.match(/^data:([^;,]+)[;,]/);if(a&&(a=a[1]),a){if(this.preview.innerHTML="<strong>Type:</strong> "+a+", <strong>Size:</strong> "+Math.floor((this.value.length-this.value.split(",")[0].length-1)/1.33333)+" bytes","image"===a.substr(0,5)){this.preview.innerHTML+="<br>";var b=document.createElement("img");b.style.maxWidth="100%",b.style.maxHeight="100px",b.src=this.value,this.preview.appendChild(b)}}else this.preview.innerHTML="<em>Invalid data URI</em>"}},enable:function(){this.uploader&&(this.uploader.disabled=!1),this._super()},disable:function(){this.uploader&&(this.uploader.disabled=!0),this._super()},setValue:function(a){this.value!==a&&(this.value=a,this.input.value=this.value,this.refreshPreview(),this.onChange())},destroy:function(){this.preview&&this.preview.parentNode&&this.preview.parentNode.removeChild(this.preview),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.uploader&&this.uploader.parentNode&&this.uploader.parentNode.removeChild(this.uploader),this._super()}}),f.defaults.editors.upload=f.AbstractEditor.extend({getNumColumns:function(){return 4},build:function(){var a=this;if(this.title=this.header=this.label=this.theme.getFormInputLabel(this.getTitle(),this.isRequired()),this.input=this.theme.getFormInputField("hidden"),this.container.appendChild(this.input),!this.schema.readOnly&&!this.schema.readonly){if(!this.jsoneditor.options.upload)throw"Upload handler required for upload editor";this.uploader=this.theme.getFormInputField("file"),this.uploader.addEventListener("change",function(b){if(b.preventDefault(),b.stopPropagation(),this.files&&this.files.length){var c=new FileReader;c.onload=function(b){a.preview_value=b.target.result,a.refreshPreview(),a.onChange(!0),c=null},c.readAsDataURL(this.files[0])}})}var b=this.schema.description;b||(b=""),this.preview=this.theme.getFormInputDescription(b),this.container.appendChild(this.preview),this.control=this.theme.getFormControl(this.label,this.uploader||this.input,this.preview),this.container.appendChild(this.control)},refreshPreview:function(){if(this.last_preview!==this.preview_value&&(this.last_preview=this.preview_value,this.preview.innerHTML="",this.preview_value)){var a=this,b=this.preview_value.match(/^data:([^;,]+)[;,]/);b&&(b=b[1]),b||(b="unknown");var c=this.uploader.files[0];if(this.preview.innerHTML="<strong>Type:</strong> "+b+", <strong>Size:</strong> "+c.size+" bytes","image"===b.substr(0,5)){this.preview.innerHTML+="<br>";var d=document.createElement("img");d.style.maxWidth="100%",d.style.maxHeight="100px",d.src=this.preview_value,
+this.preview.appendChild(d)}this.preview.innerHTML+="<br>";var e=this.getButton("Upload","upload","Upload");this.preview.appendChild(e),e.addEventListener("click",function(b){b.preventDefault(),e.setAttribute("disabled","disabled"),a.theme.removeInputError(a.uploader),a.theme.getProgressBar&&(a.progressBar=a.theme.getProgressBar(),a.preview.appendChild(a.progressBar)),a.jsoneditor.options.upload(a.path,c,{success:function(b){a.setValue(b),a.parent?a.parent.onChildEditorChange(a):a.jsoneditor.onChange(),a.progressBar&&a.preview.removeChild(a.progressBar),e.removeAttribute("disabled")},failure:function(b){a.theme.addInputError(a.uploader,b),a.progressBar&&a.preview.removeChild(a.progressBar),e.removeAttribute("disabled")},updateProgress:function(b){a.progressBar&&(b?a.theme.updateProgressBar(a.progressBar,b):a.theme.updateProgressBarUnknown(a.progressBar))}})})}},enable:function(){this.uploader&&(this.uploader.disabled=!1),this._super()},disable:function(){this.uploader&&(this.uploader.disabled=!0),this._super()},setValue:function(a){this.value!==a&&(this.value=a,this.input.value=this.value,this.onChange())},destroy:function(){this.preview&&this.preview.parentNode&&this.preview.parentNode.removeChild(this.preview),this.title&&this.title.parentNode&&this.title.parentNode.removeChild(this.title),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this.uploader&&this.uploader.parentNode&&this.uploader.parentNode.removeChild(this.uploader),this._super()}}),f.defaults.editors.checkbox=f.AbstractEditor.extend({setValue:function(a,b){this.value=!!a,this.input.checked=this.value,this.onChange()},register:function(){this._super(),this.input&&this.input.setAttribute("name",this.formname)},unregister:function(){this._super(),this.input&&this.input.removeAttribute("name")},getNumColumns:function(){return Math.min(12,Math.max(this.getTitle().length/7,2))},build:function(){var a=this;this.options.compact||(this.label=this.header=this.theme.getCheckboxLabel(this.getTitle())),this.schema.description&&(this.description=this.theme.getFormInputDescription(this.schema.description)),this.options.compact&&(this.container.className+=" compact"),this.input=this.theme.getCheckbox(),this.control=this.theme.getFormControl(this.label,this.input,this.description),(this.schema.readOnly||this.schema.readonly)&&(this.always_disabled=!0,this.input.disabled=!0),this.input.addEventListener("change",function(b){b.preventDefault(),b.stopPropagation(),a.value=this.checked,a.onChange(!0)}),this.container.appendChild(this.control)},enable:function(){this.always_disabled||(this.input.disabled=!1),this._super()},disable:function(){this.input.disabled=!0,this._super()},destroy:function(){this.label&&this.label.parentNode&&this.label.parentNode.removeChild(this.label),this.description&&this.description.parentNode&&this.description.parentNode.removeChild(this.description),this.input&&this.input.parentNode&&this.input.parentNode.removeChild(this.input),this._super()}});var g=function(){var a=document.documentElement;return a.matches?"matches":a.webkitMatchesSelector?"webkitMatchesSelector":a.mozMatchesSelector?"mozMatchesSelector":a.msMatchesSelector?"msMatchesSelector":a.oMatchesSelector?"oMatchesSelector":void 0}();f.AbstractTheme=a.extend({getContainer:function(){return document.createElement("div")},getFloatRightLinkHolder:function(){var a=document.createElement("div");return a.style=a.style||{},a.style.cssFloat="right",a.style.marginLeft="10px",a},getModal:function(){var a=document.createElement("div");return a.style.backgroundColor="white",a.style.border="1px solid black",a.style.boxShadow="3px 3px black",a.style.position="absolute",a.style.zIndex="10",a.style.display="none",a},getGridContainer:function(){var a=document.createElement("div");return a},getGridRow:function(){var a=document.createElement("div");return a.className="row",a},getGridColumn:function(){var a=document.createElement("div");return a},setGridColumnSize:function(a,b){},getLink:function(a){var b=document.createElement("a");return b.setAttribute("href","#"),b.appendChild(document.createTextNode(a)),b},disableHeader:function(a){a.style.color="#ccc"},disableLabel:function(a){a.style.color="#ccc"},enableHeader:function(a){a.style.color=""},enableLabel:function(a){a.style.color=""},getFormInputLabel:function(a){var b=document.createElement("label");return b.appendChild(document.createTextNode(a)),b},getCheckboxLabel:function(a){var b=this.getFormInputLabel(a);return b.style.fontWeight="normal",b},getHeader:function(a,b){var c=document.createElement("h3");return"string"==typeof a?c.textContent=a:c.appendChild(a),b&&(c.className+=" required"),c},getCheckbox:function(){var a=this.getFormInputField("checkbox");return a.style.display="inline-block",a.style.width="auto",a},getMultiCheckboxHolder:function(a,b,c){var d=document.createElement("div");b&&(b.style.display="block",d.appendChild(b));for(var e in a)a.hasOwnProperty(e)&&(a[e].style.display="inline-block",a[e].style.marginRight="20px",d.appendChild(a[e]));return c&&d.appendChild(c),d},getSelectInput:function(a){var b=document.createElement("select");return a&&this.setSelectOptions(b,a),b},getSwitcher:function(a){var b=this.getSelectInput(a);return b.style.backgroundColor="transparent",b.style.display="inline-block",b.style.fontStyle="italic",b.style.fontWeight="normal",b.style.height="auto",b.style.marginBottom=0,b.style.marginLeft="5px",b.style.padding="0 0 0 3px",b.style.width="auto",b},getSwitcherOptions:function(a){return a.getElementsByTagName("option")},setSwitcherOptions:function(a,b,c){this.setSelectOptions(a,b,c)},setSelectOptions:function(a,b,c){c=c||[],a.innerHTML="";for(var d=0;d<b.length;d++){var e=document.createElement("option");e.setAttribute("value",b[d]),e.textContent=c[d]||b[d],a.appendChild(e)}},getTextareaInput:function(){var a=document.createElement("textarea");return a.style=a.style||{},a.style.width="100%",a.style.height="300px",a.style.boxSizing="border-box",a},getRangeInput:function(a,b,c){var d=this.getFormInputField("range");return d.setAttribute("min",a),d.setAttribute("max",b),d.setAttribute("step",c),d},getFormInputField:function(a){var b=document.createElement("input");return b.setAttribute("type",a),b},afterInputReady:function(a){},getFormControl:function(a,b,c){var d=document.createElement("div");return d.className="form-control",a&&d.appendChild(a),"checkbox"===b.type?a.insertBefore(b,a.firstChild):d.appendChild(b),c&&d.appendChild(c),d},getIndentedPanel:function(){var a=document.createElement("div");return a.style=a.style||{},a.style.paddingLeft="10px",a.style.marginLeft="10px",a.style.borderLeft="1px solid #ccc",a},getChildEditorHolder:function(){return document.createElement("div")},getDescription:function(a){var b=document.createElement("p");return b.innerHTML=a,b},getCheckboxDescription:function(a){return this.getDescription(a)},getFormInputDescription:function(a){return this.getDescription(a)},getHeaderButtonHolder:function(){return this.getButtonHolder()},getButtonHolder:function(){return document.createElement("div")},getButton:function(a,b,c){var d=document.createElement("button");return d.type="button",this.setButtonText(d,a,b,c),d},setButtonText:function(a,b,c,d){a.innerHTML="",c&&(a.appendChild(c),a.innerHTML+=" "),a.appendChild(document.createTextNode(b)),d&&a.setAttribute("title",d)},getTable:function(){return document.createElement("table")},getTableRow:function(){return document.createElement("tr")},getTableHead:function(){return document.createElement("thead")},getTableBody:function(){return document.createElement("tbody")},getTableHeaderCell:function(a){var b=document.createElement("th");return b.textContent=a,b},getTableCell:function(){var a=document.createElement("td");return a},getErrorMessage:function(a){var b=document.createElement("p");return b.style=b.style||{},b.style.color="red",b.appendChild(document.createTextNode(a)),b},addInputError:function(a,b){},removeInputError:function(a){},addTableRowError:function(a){},removeTableRowError:function(a){},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<div style='float: left; width: 130px;' class='tabs'></div><div class='content' style='margin-left: 130px;'></div><div style='clear:both;'></div>",a},applyStyles:function(a,b){a.style=a.style||{};for(var c in b)b.hasOwnProperty(c)&&(a.style[c]=b[c])},closest:function(a,b){for(;a&&a!==document;){if(!g)return!1;if(a[g](b))return a;a=a.parentNode}return!1},getTab:function(a){var b=document.createElement("div");return b.appendChild(a),b.style=b.style||{},this.applyStyles(b,{border:"1px solid #ccc",borderWidth:"1px 0 1px 1px",textAlign:"center",lineHeight:"30px",borderRadius:"5px",borderBottomRightRadius:0,borderTopRightRadius:0,fontWeight:"bold",cursor:"pointer"}),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){return this.getIndentedPanel()},markTabActive:function(a){this.applyStyles(a,{opacity:1,background:"white"})},markTabInactive:function(a){this.applyStyles(a,{opacity:.5,background:""})},addTab:function(a,b){a.children[0].appendChild(b)},getBlockLink:function(){var a=document.createElement("a");return a.style.display="block",a},getBlockLinkHolder:function(){var a=document.createElement("div");return a},getLinksHolder:function(){var a=document.createElement("div");return a},createMediaLink:function(a,b,c){a.appendChild(b),c.style.width="100%",a.appendChild(c)},createImageLink:function(a,b,c){a.appendChild(b),b.appendChild(c)}}),f.defaults.themes.bootstrap2=f.AbstractTheme.extend({getRangeInput:function(a,b,c){return this._super(a,b,c)},getGridContainer:function(){var a=document.createElement("div");return a.className="container-fluid",a},getGridRow:function(){var a=document.createElement("div");return a.className="row-fluid",a},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="inline-block",c.style.fontWeight="bold",b&&(c.className+=" required"),c},setGridColumnSize:function(a,b){a.className="span"+b},getSelectInput:function(a){var b=this._super(a);return b.style.width="auto",b.style.maxWidth="98%",b},getFormInputField:function(a){var b=this._super(a);return b.style.width="98%",b},afterInputReady:function(a){a.controlgroup||(a.controlgroup=this.closest(a,".control-group"),a.controls=this.closest(a,".controls"),this.closest(a,".compact")&&(a.controlgroup.className=a.controlgroup.className.replace(/control-group/g,"").replace(/[ ]{2,}/g," "),a.controls.className=a.controlgroup.className.replace(/controls/g,"").replace(/[ ]{2,}/g," "),a.style.marginBottom=0))},getIndentedPanel:function(){var a=document.createElement("div");return a.className="well well-small",a},getFormInputDescription:function(a){var b=document.createElement("p");return b.className="help-inline",b.textContent=a,b},getFormControl:function(a,b,c){var d=document.createElement("div");d.className="control-group";var e=document.createElement("div");return e.className="controls",a&&"checkbox"===b.getAttribute("type")?(d.appendChild(e),a.className+=" checkbox",a.appendChild(b),e.appendChild(a),e.style.height="30px"):(a&&(a.className+=" control-label",d.appendChild(a)),e.appendChild(b),d.appendChild(e)),c&&e.appendChild(c),d},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="btn-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+=" btn btn-default",d},getTable:function(){var a=document.createElement("table");return a.className="table table-bordered",a.style.width="auto",a.style.maxWidth="none",a},addInputError:function(a,b){a.controlgroup&&a.controls&&(a.controlgroup.className+=" error",a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("p"),a.errmsg.className="help-block errormsg",a.controls.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none",a.controlgroup.className=a.controlgroup.className.replace(/\s?error/g,""))},getTabHolder:function(){var a=document.createElement("div");return a.className="tabbable tabs-left",a.innerHTML="<ul class='nav nav-tabs span2' style='margin-right: 0;'></ul><div class='tab-content span10' style='overflow:visible;'></div>",a},getTab:function(a){var b=document.createElement("li"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="tab-pane active",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s?active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)},getProgressBar:function(){var a=document.createElement("div");a.className="progress";var b=document.createElement("div");return b.className="bar",b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.className="progress progress-striped active",a.firstChild.style.width="100%")}}),f.defaults.themes.bootstrap3=f.AbstractTheme.extend({getSelectInput:function(a){var b=this._super(a);return b.className+="form-control",b},setGridColumnSize:function(a,b){a.className="col-md-"+b},afterInputReady:function(a){a.controlgroup||(a.controlgroup=this.closest(a,".form-group"),this.closest(a,".compact")&&(a.controlgroup.style.marginBottom=0))},getTextareaInput:function(){var a=document.createElement("textarea");return a.className="form-control",a},getRangeInput:function(a,b,c){return this._super(a,b,c)},getFormInputField:function(a){var b=this._super(a);return"checkbox"!==a&&(b.className+="form-control"),b},getFormControl:function(a,b,c){var d=document.createElement("div");return a&&"checkbox"===b.type?(d.className+=" checkbox",a.appendChild(b),a.style.fontSize="14px",d.style.marginTop="0",d.appendChild(a),b.style.position="relative",b.style.cssFloat="left"):(d.className+=" form-group",a&&(a.className+=" control-label",d.appendChild(a)),d.appendChild(b)),c&&d.appendChild(c),d},getIndentedPanel:function(){var a=document.createElement("div");return a.className="well well-sm",a},getFormInputDescription:function(a){var b=document.createElement("p");return b.className="help-block",b.innerHTML=a,b},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="btn-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+="btn btn-default",d},getTable:function(){var a=document.createElement("table");return a.className="table table-bordered",a.style.width="auto",a.style.maxWidth="none",a},addInputError:function(a,b){a.controlgroup&&(a.controlgroup.className+=" has-error",a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("p"),a.errmsg.className="help-block errormsg",a.controlgroup.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none",a.controlgroup.className=a.controlgroup.className.replace(/\s?has-error/g,""))},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<div class='tabs list-group col-md-2'></div><div class='col-md-10'></div>",a.className="rows",a},getTab:function(a){var b=document.createElement("a");return b.className="list-group-item",b.setAttribute("href","#"),b.appendChild(a),b},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s?active/g,"")},getProgressBar:function(){var a=0,b=100,c=0,d=document.createElement("div");d.className="progress";var e=document.createElement("div");return e.className="progress-bar",e.setAttribute("role","progressbar"),e.setAttribute("aria-valuenow",c),e.setAttribute("aria-valuemin",a),e.setAttribute("aria-valuenax",b),e.innerHTML=c+"%",d.appendChild(e),d},updateProgressBar:function(a,b){if(a){var c=a.firstChild,d=b+"%";c.setAttribute("aria-valuenow",b),c.style.width=d,c.innerHTML=d}},updateProgressBarUnknown:function(a){if(a){var b=a.firstChild;a.className="progress progress-striped active",b.removeAttribute("aria-valuenow"),b.style.width="100%",b.innerHTML=""}}}),f.defaults.themes.foundation=f.AbstractTheme.extend({getChildEditorHolder:function(){var a=document.createElement("div");return a.style.marginBottom="15px",a},getSelectInput:function(a){var b=this._super(a);return b.style.minWidth="none",b.style.padding="5px",b.style.marginTop="3px",b},getSwitcher:function(a){var b=this._super(a);return b.style.paddingRight="8px",b},afterInputReady:function(a){this.closest(a,".compact")&&(a.style.marginBottom=0),a.group=this.closest(a,".form-control")},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="inline-block",b&&(c.className+=" required"),c},getFormInputField:function(a){var b=this._super(a);return b.style.width="100%",b.style.marginBottom="checkbox"===a?"0":"12px",b},getFormInputDescription:function(a){var b=document.createElement("p");return b.textContent=a,b.style.marginTop="-10px",b.style.fontStyle="italic",b},getIndentedPanel:function(){var a=document.createElement("div");return a.className="panel",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.display="inline-block",a.style.marginLeft="10px",a.style.verticalAlign="middle",a},getButtonHolder:function(){var a=document.createElement("div");return a.className="button-group",a},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className+=" small button",d},addInputError:function(a,b){a.group&&(a.group.className+=" error",a.errmsg?a.errmsg.style.display="":(a.insertAdjacentHTML("afterend",'<small class="error"></small>'),a.errmsg=a.parentNode.getElementsByClassName("error")[0]),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.group.className=a.group.className.replace(/ error/g,""),a.errmsg.style.display="none")},getProgressBar:function(){var a=document.createElement("div");a.className="progress";var b=document.createElement("span");return b.className="meter",b.style.width="0%",a.appendChild(b),a},updateProgressBar:function(a,b){a&&(a.firstChild.style.width=b+"%")},updateProgressBarUnknown:function(a){a&&(a.firstChild.style.width="100%")}}),f.defaults.themes.foundation3=f.defaults.themes.foundation.extend({getHeaderButtonHolder:function(){var a=this._super();return a.style.fontSize=".6em",a},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.fontWeight="bold",b&&(c.className+=" required"),c},getTabHolder:function(){var a=document.createElement("div");return a.className="row",a.innerHTML="<dl class='tabs vertical two columns'></dl><div class='tabs-content ten columns'></div>",a},setGridColumnSize:function(a,b){var c=["zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];a.className="columns "+c[b]},getTab:function(a){var b=document.createElement("dd"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="content active",a.style.paddingLeft="5px",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s*active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)}}),f.defaults.themes.foundation4=f.defaults.themes.foundation.extend({getHeaderButtonHolder:function(){var a=this._super();return a.style.fontSize=".6em",a},setGridColumnSize:function(a,b){a.className="columns large-"+b},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},getFormInputLabel:function(a,b){var c=this._super(a);return c.style.fontWeight="bold",b&&(c.className+=" required"),c}}),f.defaults.themes.foundation5=f.defaults.themes.foundation.extend({getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8rem",b},setGridColumnSize:function(a,b){a.className="columns medium-"+b},getButton:function(a,b,c){var d=this._super(a,b,c);return d.className=d.className.replace(/\s*small/g,"")+" tiny",d},getTabHolder:function(){var a=document.createElement("div");return a.innerHTML="<dl class='tabs vertical'></dl><div class='tabs-content vertical'></div>",a},getTab:function(a){var b=document.createElement("dd"),c=document.createElement("a");return c.setAttribute("href","#"),c.appendChild(a),b.appendChild(c),b},getTabContentHolder:function(a){return a.children[1]},getTabContent:function(){var a=document.createElement("div");return a.className="content active",a.style.paddingLeft="5px",a},markTabActive:function(a){a.className+=" active"},markTabInactive:function(a){a.className=a.className.replace(/\s*active/g,"")},addTab:function(a,b){a.children[0].appendChild(b)}}),f.defaults.themes.html=f.AbstractTheme.extend({getFormInputLabel:function(a,b){var c=this._super(a);return c.style.display="block",c.style.marginBottom="3px",c.style.fontWeight="bold",b&&(c.className+=" required"),c},getFormInputDescription:function(a){var b=this._super(a);return b.style.fontSize=".8em",b.style.margin=0,b.style.display="inline-block",b.style.fontStyle="italic",b},getIndentedPanel:function(){var a=this._super();return a.style.border="1px solid #ddd",a.style.padding="5px",a.style.margin="5px",a.style.borderRadius="3px",a},getChildEditorHolder:function(){var a=this._super();return a.style.marginBottom="8px",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.display="inline-block",a.style.marginLeft="10px",a.style.fontSize=".8em",a.style.verticalAlign="middle",a},getTable:function(){var a=this._super();return a.style.borderBottom="1px solid #ccc",a.style.marginBottom="5px",a},addInputError:function(a,b){if(a.style.borderColor="red",a.errmsg)a.errmsg.style.display="block";else{var c=this.closest(a,".form-control");a.errmsg=document.createElement("div"),a.errmsg.setAttribute("class","errmsg"),a.errmsg.style=a.errmsg.style||{},a.errmsg.style.color="red",c.appendChild(a.errmsg)}a.errmsg.innerHTML="",a.errmsg.appendChild(document.createTextNode(b))},removeInputError:function(a){a.style.borderColor="",a.errmsg&&(a.errmsg.style.display="none")},getProgressBar:function(){var a=100,b=0,c=document.createElement("progress");return c.setAttribute("max",a),c.setAttribute("value",b),c},updateProgressBar:function(a,b){a&&a.setAttribute("value",b)},updateProgressBarUnknown:function(a){a&&a.removeAttribute("value")}}),f.defaults.themes.jqueryui=f.AbstractTheme.extend({getTable:function(){var a=this._super();return a.setAttribute("cellpadding",5),a.setAttribute("cellspacing",0),a},getTableHeaderCell:function(a){var b=this._super(a);return b.className="ui-state-active",b.style.fontWeight="bold",b},getTableCell:function(){var a=this._super();return a.className="ui-widget-content",a},getHeaderButtonHolder:function(){var a=this.getButtonHolder();return a.style.marginLeft="10px",a.style.fontSize=".6em",a.style.display="inline-block",a},getFormInputDescription:function(a){var b=this.getDescription(a);return b.style.marginLeft="10px",b.style.display="inline-block",b},getFormControl:function(a,b,c){var d=this._super(a,b,c);return"checkbox"===b.type?(d.style.lineHeight="25px",d.style.padding="3px 0"):d.style.padding="4px 0 8px 0",d},getDescription:function(a){var b=document.createElement("span");return b.style.fontSize=".8em",b.style.fontStyle="italic",b.textContent=a,b},getButtonHolder:function(){var a=document.createElement("div");return a.className="ui-buttonset",a.style.fontSize=".7em",a},getFormInputLabel:function(a,b){var c=document.createElement("label");return c.style.fontWeight="bold",c.style.display="block",b&&(c.className+=" required"),c.textContent=a,c},getButton:function(a,b,c){var d=document.createElement("button");d.className="ui-button ui-widget ui-state-default ui-corner-all",b&&!a?(d.className+=" ui-button-icon-only",b.className+=" ui-button-icon-primary ui-icon-primary",d.appendChild(b)):b?(d.className+=" ui-button-text-icon-primary",b.className+=" ui-button-icon-primary ui-icon-primary",d.appendChild(b)):d.className+=" ui-button-text-only";var e=document.createElement("span");return e.className="ui-button-text",e.textContent=a||c||".",d.appendChild(e),d.setAttribute("title",c),d},setButtonText:function(a,b,c,d){a.innerHTML="",a.className="ui-button ui-widget ui-state-default ui-corner-all",c&&!b?(a.className+=" ui-button-icon-only",c.className+=" ui-button-icon-primary ui-icon-primary",a.appendChild(c)):c?(a.className+=" ui-button-text-icon-primary",c.className+=" ui-button-icon-primary ui-icon-primary",a.appendChild(c)):a.className+=" ui-button-text-only";var e=document.createElement("span");e.className="ui-button-text",e.textContent=b||d||".",a.appendChild(e),a.setAttribute("title",d)},getIndentedPanel:function(){var a=document.createElement("div");return a.className="ui-widget-content ui-corner-all",a.style.padding="1em 1.4em",a.style.marginBottom="20px",a},afterInputReady:function(a){a.controls||(a.controls=this.closest(a,".form-control"))},addInputError:function(a,b){a.controls&&(a.errmsg?a.errmsg.style.display="":(a.errmsg=document.createElement("div"),a.errmsg.className="ui-state-error",a.controls.appendChild(a.errmsg)),a.errmsg.textContent=b)},removeInputError:function(a){a.errmsg&&(a.errmsg.style.display="none")},markTabActive:function(a){a.className=a.className.replace(/\s*ui-widget-header/g,"")+" ui-state-active"},markTabInactive:function(a){a.className=a.className.replace(/\s*ui-state-active/g,"")+" ui-widget-header"}}),f.AbstractIconLib=a.extend({mapping:{collapse:"",expand:"","delete":"",edit:"",add:"",cancel:"",save:"",moveup:"",movedown:""},icon_prefix:"",getIconClass:function(a){return this.mapping[a]?this.icon_prefix+this.mapping[a]:null},getIcon:function(a){var b=this.getIconClass(a);if(!b)return null;var c=document.createElement("i");return c.className=b,c}}),f.defaults.iconlibs.bootstrap2=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-up","delete":"trash",edit:"pencil",add:"plus",cancel:"ban-circle",save:"ok",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"icon-"}),f.defaults.iconlibs.bootstrap3=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-right","delete":"remove",edit:"pencil",add:"plus",cancel:"floppy-remove",save:"floppy-saved",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"glyphicon glyphicon-"}),f.defaults.iconlibs.fontawesome3=f.AbstractIconLib.extend({mapping:{collapse:"chevron-down",expand:"chevron-right","delete":"remove",edit:"pencil",add:"plus",cancel:"ban-circle",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"icon-"}),f.defaults.iconlibs.fontawesome4=f.AbstractIconLib.extend({mapping:{collapse:"caret-square-o-down",expand:"caret-square-o-right","delete":"times",edit:"pencil",add:"plus",cancel:"ban",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"fa fa-"}),f.defaults.iconlibs.foundation2=f.AbstractIconLib.extend({mapping:{collapse:"minus",expand:"plus","delete":"remove",edit:"edit",add:"add-doc",cancel:"error",save:"checkmark",moveup:"up-arrow",movedown:"down-arrow"},icon_prefix:"foundicon-"}),f.defaults.iconlibs.foundation3=f.AbstractIconLib.extend({mapping:{collapse:"minus",expand:"plus","delete":"x",edit:"pencil",add:"page-add",cancel:"x-circle",save:"save",moveup:"arrow-up",movedown:"arrow-down"},icon_prefix:"fi-"}),f.defaults.iconlibs.jqueryui=f.AbstractIconLib.extend({mapping:{collapse:"triangle-1-s",expand:"triangle-1-e","delete":"trash",edit:"pencil",add:"plusthick",cancel:"closethick",save:"disk",moveup:"arrowthick-1-n",movedown:"arrowthick-1-s"},icon_prefix:"ui-icon ui-icon-"}),f.defaults.templates["default"]=function(){return{compile:function(a){var b=a.match(/{{\s*([a-zA-Z0-9\-_ \.]+)\s*}}/g),c=b&&b.length;if(!c)return function(){return a};for(var d=[],e=function(a){var c,e=b[a].replace(/[{}]+/g,"").trim().split("."),f=e.length;if(f>1){var g;c=function(b){for(g=b,a=0;f>a&&(g=g[e[a]],g);a++);return g}}else e=e[0],c=function(a){return a[e]};d.push({s:b[a],r:c})},f=0;c>f;f++)e(f);return function(b){var e,g=a+"";for(f=0;c>f;f++)e=d[f],g=g.replace(e.s,e.r(b));return g}}}},f.defaults.templates.ejs=function(){return window.EJS?{compile:function(a){var b=new window.EJS({text:a});return function(a){return b.render(a)}}}:!1},f.defaults.templates.handlebars=function(){return window.Handlebars},f.defaults.templates.hogan=function(){return window.Hogan?{compile:function(a){var b=window.Hogan.compile(a);return function(a){return b.render(a)}}}:!1},f.defaults.templates.markup=function(){return window.Mark&&window.Mark.up?{compile:function(a){return function(b){return window.Mark.up(a,b)}}}:!1},f.defaults.templates.mustache=function(){return window.Mustache?{compile:function(a){return function(b){return window.Mustache.render(a,b)}}}:!1},f.defaults.templates.swig=function(){return window.swig},f.defaults.templates.underscore=function(){return window._?{compile:function(a){return function(b){return window._.template(a,b)}}}:!1},f.defaults.theme="html",f.defaults.template="default",f.defaults.options={},f.defaults.translate=function(a,b){var c=f.defaults.languages[f.defaults.language];if(!c)throw"Unknown language "+f.defaults.language;var d=c[a]||f.defaults.languages[f.defaults.default_language][a];if("undefined"==typeof d)throw"Unknown translate string "+a;if(b)for(var e=0;e<b.length;e++)d=d.replace(new RegExp("\\{\\{"+e+"}}","g"),b[e]);return d},f.defaults.default_language="en",f.defaults.language=f.defaults.default_language,f.defaults.languages.en={error_notset:"Property must be set",error_notempty:"Value required",error_enum:"Value must be one of the enumerated values",error_anyOf:"Value must validate against at least one of the provided schemas",error_oneOf:"Value must validate against exactly one of the provided schemas. It currently validates against {{0}} of the schemas.",error_not:"Value must not validate against the provided schema",error_type_union:"Value must be one of the provided types",error_type:"Value must be of type {{0}}",error_disallow_union:"Value must not be one of the provided disallowed types",error_disallow:"Value must not be of type {{0}}",error_multipleOf:"Value must be a multiple of {{0}}",error_maximum_excl:"Value must be less than {{0}}",error_maximum_incl:"Value must at most {{0}}",error_minimum_excl:"Value must be greater than {{0}}",error_minimum_incl:"Value must be at least {{0}}",error_maxLength:"Value must be at most {{0}} characters long",error_minLength:"Value must be at least {{0}} characters long",error_pattern:"Value must match the provided pattern",error_additionalItems:"No additional items allowed in this array",error_maxItems:"Value must have at most {{0}} items",error_minItems:"Value must have at least {{0}} items",error_uniqueItems:"Array must have unique items",error_maxProperties:"Object must have at most {{0}} properties",error_minProperties:"Object must have at least {{0}} properties",error_required:"Object is missing the required property '{{0}}'",error_additional_properties:"No additional properties allowed, but property {{0}} is set",error_dependency:"Must have property {{0}}"},f.plugins={ace:{theme:""},epiceditor:{},sceditor:{},select2:{}};for(var h in f.defaults.editors)f.defaults.editors.hasOwnProperty(h)&&(f.defaults.editors[h].options=f.defaults.editors.options||{});f.defaults.resolvers.unshift(function(a){return"string"!=typeof a.type?"multiple":void 0}),f.defaults.resolvers.unshift(function(a){return!a.type&&a.properties?"object":void 0}),f.defaults.resolvers.unshift(function(a){return"string"==typeof a.type?a.type:void 0}),f.defaults.resolvers.unshift(function(a){return"boolean"===a.type?"checkbox"===a.format||a.options&&a.options.checkbox?"checkbox":"select":void 0;
+}),f.defaults.resolvers.unshift(function(a){return"any"===a.type?"multiple":void 0}),f.defaults.resolvers.unshift(function(a){return"string"===a.type&&a.media&&"base64"===a.media.binaryEncoding?"base64":void 0}),f.defaults.resolvers.unshift(function(a){return"string"===a.type&&"url"===a.format&&a.options&&a.options.upload===!0&&window.FileReader?"upload":void 0}),f.defaults.resolvers.unshift(function(a){return"array"==a.type&&"table"==a.format?"table":void 0}),f.defaults.resolvers.unshift(function(a){return a.enumSource?"select":void 0}),f.defaults.resolvers.unshift(function(a){if(a["enum"]){if("array"===a.type||"object"===a.type)return"enum";if("number"===a.type||"integer"===a.type||"string"===a.type)return"select"}}),f.defaults.resolvers.unshift(function(a){return"array"===a.type&&a.items&&!Array.isArray(a.items)&&a.uniqueItems&&a.items["enum"]&&["string","number","integer"].indexOf(a.items.type)>=0?"multiselect":void 0}),f.defaults.resolvers.unshift(function(a){return a.oneOf?"multiple":void 0}),function(){if(window.jQuery||window.Zepto){var a=window.jQuery||window.Zepto;a.jsoneditor=f.defaults,a.fn.jsoneditor=function(a){var b=this,c=this.data("jsoneditor");if("value"===a){if(!c)throw"Must initialize jsoneditor before getting/setting the value";if(!(arguments.length>1))return c.getValue();c.setValue(arguments[1])}else{if("validate"===a){if(!c)throw"Must initialize jsoneditor before validating";return arguments.length>1?c.validate(arguments[1]):c.validate()}"destroy"===a?c&&(c.destroy(),this.data("jsoneditor",null)):(c&&c.destroy(),c=new f(this.get(0),a),this.data("jsoneditor",c),c.on("change",function(){b.trigger("change")}),c.on("ready",function(){b.trigger("ready")}))}return this}}}(),window.JSONEditor=f}();
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/js-yaml.min.js b/profiles/killpay/src/main/webapp/lib/js-yaml.min.js
new file mode 100644
index 0000000..c3d07ad
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/js-yaml.min.js
@@ -0,0 +1,3 @@
+/* js-yaml 3.4.6 https://github.com/nodeca/js-yaml */
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.jsyaml=t()}}(function(){var t;return function e(t,n,i){function r(a,s){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(o)return o(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};t[a][0].call(l.exports,function(e){var n=t[a][1][e];return r(n?n:e)},l,l.exports,e,t,n,i)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a<i.length;a++)r(i[a]);return r}({1:[function(t,e,n){"use strict";function i(t){return function(){throw new Error("Function "+t+" is deprecated and cannot be used.")}}var r=t("./js-yaml/loader"),o=t("./js-yaml/dumper");e.exports.Type=t("./js-yaml/type"),e.exports.Schema=t("./js-yaml/schema"),e.exports.FAILSAFE_SCHEMA=t("./js-yaml/schema/failsafe"),e.exports.JSON_SCHEMA=t("./js-yaml/schema/json"),e.exports.CORE_SCHEMA=t("./js-yaml/schema/core"),e.exports.DEFAULT_SAFE_SCHEMA=t("./js-yaml/schema/default_safe"),e.exports.DEFAULT_FULL_SCHEMA=t("./js-yaml/schema/default_full"),e.exports.load=r.load,e.exports.loadAll=r.loadAll,e.exports.safeLoad=r.safeLoad,e.exports.safeLoadAll=r.safeLoadAll,e.exports.dump=o.dump,e.exports.safeDump=o.safeDump,e.exports.YAMLException=t("./js-yaml/exception"),e.exports.MINIMAL_SCHEMA=t("./js-yaml/schema/failsafe"),e.exports.SAFE_SCHEMA=t("./js-yaml/schema/default_safe"),e.exports.DEFAULT_SCHEMA=t("./js-yaml/schema/default_full"),e.exports.scan=i("scan"),e.exports.parse=i("parse"),e.exports.compose=i("compose"),e.exports.addConstructor=i("addConstructor")},{"./js-yaml/dumper":3,"./js-yaml/exception":4,"./js-yaml/loader":5,"./js-yaml/schema":7,"./js-yaml/schema/core":8,"./js-yaml/schema/default_full":9,"./js-yaml/schema/default_safe":10,"./js-yaml/schema/failsafe":11,"./js-yaml/schema/json":12,"./js-yaml/type":13}],2:[function(t,e,n){"use strict";function i(t){return"undefined"==typeof t||null===t}function r(t){return"object"==typeof t&&null!==t}function o(t){return Array.isArray(t)?t:i(t)?[]:[t]}function a(t,e){var n,i,r,o;if(e)for(o=Object.keys(e),n=0,i=o.length;i>n;n+=1)r=o[n],t[r]=e[r];return t}function s(t,e){var n,i="";for(n=0;e>n;n+=1)i+=t;return i}function c(t){return 0===t&&Number.NEGATIVE_INFINITY===1/t}e.exports.isNothing=i,e.exports.isObject=r,e.exports.toArray=o,e.exports.repeat=s,e.exports.isNegativeZero=c,e.exports.extend=a},{}],3:[function(t,e,n){"use strict";function i(t,e){var n,i,r,o,a,s,c;if(null===e)return{};for(n={},i=Object.keys(e),r=0,o=i.length;o>r;r+=1)a=i[r],s=String(e[a]),"!!"===a.slice(0,2)&&(a="tag:yaml.org,2002:"+a.slice(2)),c=t.compiledTypeMap[a],c&&E.call(c.styleAliases,s)&&(s=c.styleAliases[s]),n[a]=s;return n}function r(t){var e,n,i;if(e=t.toString(16).toUpperCase(),255>=t)n="x",i=2;else if(65535>=t)n="u",i=4;else{if(!(4294967295>=t))throw new O("code point within a string may not be greater than 0xFFFFFFFF");n="U",i=8}return"\\"+n+j.repeat("0",i-e.length)+e}function o(t){this.schema=t.schema||S,this.indent=Math.max(1,t.indent||2),this.skipInvalid=t.skipInvalid||!1,this.flowLevel=j.isNothing(t.flowLevel)?-1:t.flowLevel,this.styleMap=i(this.schema,t.styles||null),this.sortKeys=t.sortKeys||!1,this.lineWidth=t.lineWidth||80,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function a(t,e){for(var n,i=j.repeat(" ",e),r=0,o=-1,a="",s=t.length;s>r;)o=t.indexOf("\n",r),-1===o?(n=t.slice(r),r=s):(n=t.slice(r,o+1),r=o+1),n.length&&"\n"!==n&&(a+=i),a+=n;return a}function s(t,e){return"\n"+j.repeat(" ",t.indent*e)}function c(t,e){var n,i,r;for(n=0,i=t.implicitTypes.length;i>n;n+=1)if(r=t.implicitTypes[n],r.resolve(e))return!0;return!1}function u(t){this.source=t,this.result="",this.checkpoint=0}function l(t,e,n,i){var r,o,s,l,f,m,g,y,v,x,A,b,w,k,C,j,O,S,_,I,E;if(0===e.length)return void(t.dump="''");if(-1!==et.indexOf(e))return void(t.dump="'"+e+"'");for(r=!0,o=e.length?e.charCodeAt(0):0,s=M===o||M===e.charCodeAt(e.length-1),(K===o||W===o||G===o||z===o)&&(r=!1),s?(r=!1,l=!1,f=!1):(l=!i,f=!i),m=!0,g=new u(e),y=!1,v=0,x=0,A=t.indent*n,b=t.lineWidth,-1===b&&(b=9007199254740991),40>A?b-=A:b=40,k=0;k<e.length;k++){if(w=e.charCodeAt(k),r){if(h(w))continue;r=!1}m&&w===P&&(m=!1),C=tt[w],j=d(w),(C||j)&&(w!==N&&w!==D&&w!==P?(l=!1,f=!1):w===N&&(y=!0,m=!1,k>0&&(O=e.charCodeAt(k-1),O===M&&(f=!1,l=!1)),l&&(S=k-v,v=k,S>x&&(x=S))),w!==D&&(m=!1),g.takeUpTo(k),g.escapeChar())}if(r&&c(t,e)&&(r=!1),_="",(l||f)&&(I=0,e.charCodeAt(e.length-1)===N&&(I+=1,e.charCodeAt(e.length-2)===N&&(I+=1)),0===I?_="-":2===I&&(_="+")),f&&b>x&&(l=!1),y||(f=!1),r)t.dump=e;else if(m)t.dump="'"+e+"'";else if(l)E=p(e,b),t.dump=">"+_+"\n"+a(E,A);else if(f)_||(e=e.replace(/\n$/,"")),t.dump="|"+_+"\n"+a(e,A);else{if(!g)throw new Error("Failed to dump scalar value");g.finish(),t.dump='"'+g.result+'"'}}function p(t,e){var n,i="",r=0,o=t.length,a=/\n+$/.exec(t);for(a&&(o=a.index+1);o>r;)n=t.indexOf("\n",r),n>o||-1===n?(i&&(i+="\n\n"),i+=f(t.slice(r,o),e),r=o):(i&&(i+="\n\n"),i+=f(t.slice(r,n),e),r=n+1);return a&&"\n"!==a[0]&&(i+=a[0]),i}function f(t,e){if(""===t)return t;for(var n,i,r,o=/[^\s] [^\s]/g,a="",s=0,c=0,u=o.exec(t);u;)n=u.index,n-c>e&&(i=s!==c?s:n,a&&(a+="\n"),r=t.slice(c,i),a+=r,c=i+1),s=n+1,u=o.exec(t);return a&&(a+="\n"),a+=c!==s&&t.length-c>e?t.slice(c,s)+"\n"+t.slice(s+1):t.slice(c)}function h(t){return F!==t&&N!==t&&T!==t&&B!==t&&V!==t&&Z!==t&&J!==t&&X!==t&&U!==t&&Y!==t&&$!==t&&L!==t&&Q!==t&&R!==t&&P!==t&&D!==t&&q!==t&&H!==t&&!tt[t]&&!d(t)}function d(t){return!(t>=32&&126>=t||133===t||t>=160&&55295>=t||t>=57344&&65533>=t||t>=65536&&1114111>=t)}function m(t,e,n){var i,r,o="",a=t.tag;for(i=0,r=n.length;r>i;i+=1)A(t,e,n[i],!1,!1)&&(0!==i&&(o+=", "),o+=t.dump);t.tag=a,t.dump="["+o+"]"}function g(t,e,n,i){var r,o,a="",c=t.tag;for(r=0,o=n.length;o>r;r+=1)A(t,e+1,n[r],!0,!0)&&(i&&0===r||(a+=s(t,e)),a+="- "+t.dump);t.tag=c,t.dump=a||"[]"}function y(t,e,n){var i,r,o,a,s,c="",u=t.tag,l=Object.keys(n);for(i=0,r=l.length;r>i;i+=1)s="",0!==i&&(s+=", "),o=l[i],a=n[o],A(t,e,o,!1,!1)&&(t.dump.length>1024&&(s+="? "),s+=t.dump+": ",A(t,e,a,!1,!1)&&(s+=t.dump,c+=s));t.tag=u,t.dump="{"+c+"}"}function v(t,e,n,i){var r,o,a,c,u,l,p="",f=t.tag,h=Object.keys(n);if(t.sortKeys===!0)h.sort();else if("function"==typeof t.sortKeys)h.sort(t.sortKeys);else if(t.sortKeys)throw new O("sortKeys must be a boolean or a function");for(r=0,o=h.length;o>r;r+=1)l="",i&&0===r||(l+=s(t,e)),a=h[r],c=n[a],A(t,e+1,a,!0,!0,!0)&&(u=null!==t.tag&&"?"!==t.tag||t.dump&&t.dump.length>1024,u&&(l+=t.dump&&N===t.dump.charCodeAt(0)?"?":"? "),l+=t.dump,u&&(l+=s(t,e)),A(t,e+1,c,!0,u)&&(l+=t.dump&&N===t.dump.charCodeAt(0)?":":": ",l+=t.dump,p+=l));t.tag=f,t.dump=p||"{}"}function x(t,e,n){var i,r,o,a,s,c;for(r=n?t.explicitTypes:t.implicitTypes,o=0,a=r.length;a>o;o+=1)if(s=r[o],(s.instanceOf||s.predicate)&&(!s.instanceOf||"object"==typeof e&&e instanceof s.instanceOf)&&(!s.predicate||s.predicate(e))){if(t.tag=n?s.tag:"?",s.represent){if(c=t.styleMap[s.tag]||s.defaultStyle,"[object Function]"===I.call(s.represent))i=s.represent(e,c);else{if(!E.call(s.represent,c))throw new O("!<"+s.tag+'> tag resolver accepts not "'+c+'" style');i=s.represent[c](e,c)}t.dump=i}return!0}return!1}function A(t,e,n,i,r,o){t.tag=null,t.dump=n,x(t,n,!1)||x(t,n,!0);var a=I.call(t.dump);i&&(i=0>t.flowLevel||t.flowLevel>e);var s,c,u="[object Object]"===a||"[object Array]"===a;if(u&&(s=t.duplicates.indexOf(n),c=-1!==s),(null!==t.tag&&"?"!==t.tag||c||2!==t.indent&&e>0)&&(r=!1),c&&t.usedDuplicates[s])t.dump="*ref_"+s;else{if(u&&c&&!t.usedDuplicates[s]&&(t.usedDuplicates[s]=!0),"[object Object]"===a)i&&0!==Object.keys(t.dump).length?(v(t,e,t.dump,r),c&&(t.dump="&ref_"+s+t.dump)):(y(t,e,t.dump),c&&(t.dump="&ref_"+s+" "+t.dump));else if("[object Array]"===a)i&&0!==t.dump.length?(g(t,e,t.dump,r),c&&(t.dump="&ref_"+s+t.dump)):(m(t,e,t.dump),c&&(t.dump="&ref_"+s+" "+t.dump));else{if("[object String]"!==a){if(t.skipInvalid)return!1;throw new O("unacceptable kind of an object to dump "+a)}"?"!==t.tag&&l(t,t.dump,e,o)}null!==t.tag&&"?"!==t.tag&&(t.dump="!<"+t.tag+"> "+t.dump)}return!0}function b(t,e){var n,i,r=[],o=[];for(w(t,r,o),n=0,i=o.length;i>n;n+=1)e.duplicates.push(r[o[n]]);e.usedDuplicates=new Array(i)}function w(t,e,n){var i,r,o;if(null!==t&&"object"==typeof t)if(r=e.indexOf(t),-1!==r)-1===n.indexOf(r)&&n.push(r);else if(e.push(t),Array.isArray(t))for(r=0,o=t.length;o>r;r+=1)w(t[r],e,n);else for(i=Object.keys(t),r=0,o=i.length;o>r;r+=1)w(t[i[r]],e,n)}function k(t,e){e=e||{};var n=new o(e);return b(t,n),A(n,0,t,!0,!0)?n.dump+"\n":""}function C(t,e){return k(t,j.extend({schema:_},e))}var j=t("./common"),O=t("./exception"),S=t("./schema/default_full"),_=t("./schema/default_safe"),I=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=9,N=10,T=13,M=32,L=33,D=34,U=35,q=37,Y=38,P=39,$=42,B=44,K=45,H=58,R=62,W=63,G=64,V=91,Z=93,z=96,J=123,Q=124,X=125,tt={};tt[0]="\\0",tt[7]="\\a",tt[8]="\\b",tt[9]="\\t",tt[10]="\\n",tt[11]="\\v",tt[12]="\\f",tt[13]="\\r",tt[27]="\\e",tt[34]='\\"',tt[92]="\\\\",tt[133]="\\N",tt[160]="\\_",tt[8232]="\\L",tt[8233]="\\P";var et=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"];u.prototype.takeUpTo=function(t){var e;if(t<this.checkpoint)throw e=new Error("position should be > checkpoint"),e.position=t,e.checkpoint=this.checkpoint,e;return this.result+=this.source.slice(this.checkpoint,t),this.checkpoint=t,this},u.prototype.escapeChar=function(){var t,e;return t=this.source.charCodeAt(this.checkpoint),e=tt[t]||r(t),this.result+=e,this.checkpoint+=1,this},u.prototype.finish=function(){this.source.length>this.checkpoint&&this.takeUpTo(this.source.length)},e.exports.dump=k,e.exports.safeDump=C},{"./common":2,"./exception":4,"./schema/default_full":9,"./schema/default_safe":10}],4:[function(t,e,n){"use strict";function i(t,e){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||"",this.name="YAMLException",this.reason=t,this.mark=e,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():"")}var r=t("inherit");r(i,Error),i.prototype.toString=function(t){var e=this.name+": ";return e+=this.reason||"(unknown reason)",!t&&this.mark&&(e+=" "+this.mark.toString()),e},e.exports=i},{inherit:31}],5:[function(t,e,n){"use strict";function i(t){return 10===t||13===t}function r(t){return 9===t||32===t}function o(t){return 9===t||32===t||10===t||13===t}function a(t){return 44===t||91===t||93===t||123===t||125===t}function s(t){var e;return t>=48&&57>=t?t-48:(e=32|t,e>=97&&102>=e?e-97+10:-1)}function c(t){return 120===t?2:117===t?4:85===t?8:0}function u(t){return t>=48&&57>=t?t-48:-1}function l(t){return 48===t?"\x00":97===t?"":98===t?"\b":116===t?"	":9===t?"	":110===t?"\n":118===t?"":102===t?"\f":114===t?"\r":101===t?"":32===t?" ":34===t?'"':47===t?"/":92===t?"\\":78===t?"…":95===t?" ":76===t?"\u2028":80===t?"\u2029":""}function p(t){return 65535>=t?String.fromCharCode(t):String.fromCharCode((t-65536>>10)+55296,(t-65536&1023)+56320)}function f(t,e){this.input=t,this.filename=e.filename||null,this.schema=e.schema||H,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function h(t,e){return new $(e,new B(t.filename,t.input,t.position,t.line,t.position-t.lineStart))}function d(t,e){throw h(t,e)}function m(t,e){t.onWarning&&t.onWarning.call(null,h(t,e))}function g(t,e,n,i){var r,o,a,s;if(n>e){if(s=t.input.slice(e,n),i)for(r=0,o=s.length;o>r;r+=1)a=s.charCodeAt(r),9===a||a>=32&&1114111>=a||d(t,"expected valid JSON character");else X.test(s)&&d(t,"the stream contains non-printable characters");t.result+=s}}function y(t,e,n){var i,r,o,a;for(P.isObject(n)||d(t,"cannot merge mappings; the provided source object is unacceptable"),i=Object.keys(n),o=0,a=i.length;a>o;o+=1)r=i[o],R.call(e,r)||(e[r]=n[r])}function v(t,e,n,i,r){var o,a;if(i=String(i),null===e&&(e={}),"tag:yaml.org,2002:merge"===n)if(Array.isArray(r))for(o=0,a=r.length;a>o;o+=1)y(t,e,r[o]);else y(t,e,r);else e[i]=r;return e}function x(t){var e;e=t.input.charCodeAt(t.position),10===e?t.position++:13===e?(t.position++,10===t.input.charCodeAt(t.position)&&t.position++):d(t,"a line break is expected"),t.line+=1,t.lineStart=t.position}function A(t,e,n){for(var o=0,a=t.input.charCodeAt(t.position);0!==a;){for(;r(a);)a=t.input.charCodeAt(++t.position);if(e&&35===a)do a=t.input.charCodeAt(++t.position);while(10!==a&&13!==a&&0!==a);if(!i(a))break;for(x(t),a=t.input.charCodeAt(t.position),o++,t.lineIndent=0;32===a;)t.lineIndent++,a=t.input.charCodeAt(++t.position)}return-1!==n&&0!==o&&t.lineIndent<n&&m(t,"deficient indentation"),o}function b(t){var e,n=t.position;return e=t.input.charCodeAt(n),45!==e&&46!==e||t.input.charCodeAt(n+1)!==e||t.input.charCodeAt(n+2)!==e||(n+=3,e=t.input.charCodeAt(n),0!==e&&!o(e))?!1:!0}function w(t,e){1===e?t.result+=" ":e>1&&(t.result+=P.repeat("\n",e-1))}function k(t,e,n){var s,c,u,l,p,f,h,d,m,y=t.kind,v=t.result;if(m=t.input.charCodeAt(t.position),o(m)||a(m)||35===m||38===m||42===m||33===m||124===m||62===m||39===m||34===m||37===m||64===m||96===m)return!1;if((63===m||45===m)&&(c=t.input.charCodeAt(t.position+1),o(c)||n&&a(c)))return!1;for(t.kind="scalar",t.result="",u=l=t.position,p=!1;0!==m;){if(58===m){if(c=t.input.charCodeAt(t.position+1),o(c)||n&&a(c))break}else if(35===m){if(s=t.input.charCodeAt(t.position-1),o(s))break}else{if(t.position===t.lineStart&&b(t)||n&&a(m))break;if(i(m)){if(f=t.line,h=t.lineStart,d=t.lineIndent,A(t,!1,-1),t.lineIndent>=e){p=!0,m=t.input.charCodeAt(t.position);continue}t.position=l,t.line=f,t.lineStart=h,t.lineIndent=d;break}}p&&(g(t,u,l,!1),w(t,t.line-f),u=l=t.position,p=!1),r(m)||(l=t.position+1),m=t.input.charCodeAt(++t.position)}return g(t,u,l,!1),t.result?!0:(t.kind=y,t.result=v,!1)}function C(t,e){var n,r,o;if(n=t.input.charCodeAt(t.position),39!==n)return!1;for(t.kind="scalar",t.result="",t.position++,r=o=t.position;0!==(n=t.input.charCodeAt(t.position));)if(39===n){if(g(t,r,t.position,!0),n=t.input.charCodeAt(++t.position),39!==n)return!0;r=o=t.position,t.position++}else i(n)?(g(t,r,o,!0),w(t,A(t,!1,e)),r=o=t.position):t.position===t.lineStart&&b(t)?d(t,"unexpected end of the document within a single quoted scalar"):(t.position++,o=t.position);d(t,"unexpected end of the stream within a single quoted scalar")}function j(t,e){var n,r,o,a,u,l;if(l=t.input.charCodeAt(t.position),34!==l)return!1;for(t.kind="scalar",t.result="",t.position++,n=r=t.position;0!==(l=t.input.charCodeAt(t.position));){if(34===l)return g(t,n,t.position,!0),t.position++,!0;if(92===l){if(g(t,n,t.position,!0),l=t.input.charCodeAt(++t.position),i(l))A(t,!1,e);else if(256>l&&rt[l])t.result+=ot[l],t.position++;else if((u=c(l))>0){for(o=u,a=0;o>0;o--)l=t.input.charCodeAt(++t.position),(u=s(l))>=0?a=(a<<4)+u:d(t,"expected hexadecimal character");t.result+=p(a),t.position++}else d(t,"unknown escape sequence");n=r=t.position}else i(l)?(g(t,n,r,!0),w(t,A(t,!1,e)),n=r=t.position):t.position===t.lineStart&&b(t)?d(t,"unexpected end of the document within a double quoted scalar"):(t.position++,r=t.position)}d(t,"unexpected end of the stream within a double quoted scalar")}function O(t,e){var n,i,r,a,s,c,u,l,p,f,h,m=!0,g=t.tag,y=t.anchor;if(h=t.input.charCodeAt(t.position),91===h)a=93,u=!1,i=[];else{if(123!==h)return!1;a=125,u=!0,i={}}for(null!==t.anchor&&(t.anchorMap[t.anchor]=i),h=t.input.charCodeAt(++t.position);0!==h;){if(A(t,!0,e),h=t.input.charCodeAt(t.position),h===a)return t.position++,t.tag=g,t.anchor=y,t.kind=u?"mapping":"sequence",t.result=i,!0;m||d(t,"missed comma between flow collection entries"),p=l=f=null,s=c=!1,63===h&&(r=t.input.charCodeAt(t.position+1),o(r)&&(s=c=!0,t.position++,A(t,!0,e))),n=t.line,T(t,e,W,!1,!0),p=t.tag,l=t.result,A(t,!0,e),h=t.input.charCodeAt(t.position),!c&&t.line!==n||58!==h||(s=!0,h=t.input.charCodeAt(++t.position),A(t,!0,e),T(t,e,W,!1,!0),f=t.result),u?v(t,i,p,l,f):i.push(s?v(t,null,p,l,f):l),A(t,!0,e),h=t.input.charCodeAt(t.position),44===h?(m=!0,h=t.input.charCodeAt(++t.position)):m=!1}d(t,"unexpected end of the stream within a flow collection")}function S(t,e){var n,o,a,s,c=z,l=!1,p=e,f=0,h=!1;if(s=t.input.charCodeAt(t.position),124===s)o=!1;else{if(62!==s)return!1;o=!0}for(t.kind="scalar",t.result="";0!==s;)if(s=t.input.charCodeAt(++t.position),43===s||45===s)z===c?c=43===s?Q:J:d(t,"repeat of a chomping mode identifier");else{if(!((a=u(s))>=0))break;0===a?d(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):l?d(t,"repeat of an indentation width identifier"):(p=e+a-1,l=!0)}if(r(s)){do s=t.input.charCodeAt(++t.position);while(r(s));if(35===s)do s=t.input.charCodeAt(++t.position);while(!i(s)&&0!==s)}for(;0!==s;){for(x(t),t.lineIndent=0,s=t.input.charCodeAt(t.position);(!l||t.lineIndent<p)&&32===s;)t.lineIndent++,s=t.input.charCodeAt(++t.position);if(!l&&t.lineIndent>p&&(p=t.lineIndent),i(s))f++;else{if(t.lineIndent<p){c===Q?t.result+=P.repeat("\n",f):c===z&&l&&(t.result+="\n");break}for(o?r(s)?(h=!0,t.result+=P.repeat("\n",f+1)):h?(h=!1,t.result+=P.repeat("\n",f+1)):0===f?l&&(t.result+=" "):t.result+=P.repeat("\n",f):l?t.result+=P.repeat("\n",f+1):t.result+=P.repeat("\n",f),l=!0,f=0,n=t.position;!i(s)&&0!==s;)s=t.input.charCodeAt(++t.position);g(t,n,t.position,!1)}}return!0}function _(t,e){var n,i,r,a=t.tag,s=t.anchor,c=[],u=!1;for(null!==t.anchor&&(t.anchorMap[t.anchor]=c),r=t.input.charCodeAt(t.position);0!==r&&45===r&&(i=t.input.charCodeAt(t.position+1),o(i));)if(u=!0,t.position++,A(t,!0,-1)&&t.lineIndent<=e)c.push(null),r=t.input.charCodeAt(t.position);else if(n=t.line,T(t,e,V,!1,!0),c.push(t.result),A(t,!0,-1),r=t.input.charCodeAt(t.position),(t.line===n||t.lineIndent>e)&&0!==r)d(t,"bad indentation of a sequence entry");else if(t.lineIndent<e)break;return u?(t.tag=a,t.anchor=s,t.kind="sequence",t.result=c,!0):!1}function I(t,e,n){var i,a,s,c,u=t.tag,l=t.anchor,p={},f=null,h=null,m=null,g=!1,y=!1;for(null!==t.anchor&&(t.anchorMap[t.anchor]=p),c=t.input.charCodeAt(t.position);0!==c;){if(i=t.input.charCodeAt(t.position+1),s=t.line,63!==c&&58!==c||!o(i)){if(!T(t,n,G,!1,!0))break;if(t.line===s){for(c=t.input.charCodeAt(t.position);r(c);)c=t.input.charCodeAt(++t.position);if(58===c)c=t.input.charCodeAt(++t.position),o(c)||d(t,"a whitespace character is expected after the key-value separator within a block mapping"),g&&(v(t,p,f,h,null),f=h=m=null),y=!0,g=!1,a=!1,f=t.tag,h=t.result;else{if(!y)return t.tag=u,t.anchor=l,!0;d(t,"can not read an implicit mapping pair; a colon is missed")}}else{if(!y)return t.tag=u,t.anchor=l,!0;d(t,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===c?(g&&(v(t,p,f,h,null),f=h=m=null),y=!0,g=!0,a=!0):g?(g=!1,a=!0):d(t,"incomplete explicit mapping pair; a key node is missed"),t.position+=1,c=i;if((t.line===s||t.lineIndent>e)&&(T(t,e,Z,!0,a)&&(g?h=t.result:m=t.result),g||(v(t,p,f,h,m),f=h=m=null),A(t,!0,-1),c=t.input.charCodeAt(t.position)),t.lineIndent>e&&0!==c)d(t,"bad indentation of a mapping entry");else if(t.lineIndent<e)break}return g&&v(t,p,f,h,null),y&&(t.tag=u,t.anchor=l,t.kind="mapping",t.result=p),y}function E(t){var e,n,i,r,a=!1,s=!1;if(r=t.input.charCodeAt(t.position),33!==r)return!1;if(null!==t.tag&&d(t,"duplication of a tag property"),r=t.input.charCodeAt(++t.position),60===r?(a=!0,r=t.input.charCodeAt(++t.position)):33===r?(s=!0,n="!!",r=t.input.charCodeAt(++t.position)):n="!",e=t.position,a){do r=t.input.charCodeAt(++t.position);while(0!==r&&62!==r);t.position<t.length?(i=t.input.slice(e,t.position),r=t.input.charCodeAt(++t.position)):d(t,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!o(r);)33===r&&(s?d(t,"tag suffix cannot contain exclamation marks"):(n=t.input.slice(e-1,t.position+1),nt.test(n)||d(t,"named tag handle cannot contain such characters"),s=!0,e=t.position+1)),r=t.input.charCodeAt(++t.position);i=t.input.slice(e,t.position),et.test(i)&&d(t,"tag suffix cannot contain flow indicator characters")}return i&&!it.test(i)&&d(t,"tag name cannot contain such characters: "+i),a?t.tag=i:R.call(t.tagMap,n)?t.tag=t.tagMap[n]+i:"!"===n?t.tag="!"+i:"!!"===n?t.tag="tag:yaml.org,2002:"+i:d(t,'undeclared tag handle "'+n+'"'),!0}function F(t){var e,n;if(n=t.input.charCodeAt(t.position),38!==n)return!1;for(null!==t.anchor&&d(t,"duplication of an anchor property"),n=t.input.charCodeAt(++t.position),e=t.position;0!==n&&!o(n)&&!a(n);)n=t.input.charCodeAt(++t.position);return t.position===e&&d(t,"name of an anchor node must contain at least one character"),t.anchor=t.input.slice(e,t.position),!0}function N(t){var e,n,i;if(i=t.input.charCodeAt(t.position),42!==i)return!1;for(i=t.input.charCodeAt(++t.position),e=t.position;0!==i&&!o(i)&&!a(i);)i=t.input.charCodeAt(++t.position);return t.position===e&&d(t,"name of an alias node must contain at least one character"),n=t.input.slice(e,t.position),t.anchorMap.hasOwnProperty(n)||d(t,'unidentified alias "'+n+'"'),t.result=t.anchorMap[n],A(t,!0,-1),!0}function T(t,e,n,i,r){var o,a,s,c,u,l,p,f,h=1,m=!1,g=!1;if(t.tag=null,t.anchor=null,t.kind=null,t.result=null,o=a=s=Z===n||V===n,i&&A(t,!0,-1)&&(m=!0,t.lineIndent>e?h=1:t.lineIndent===e?h=0:t.lineIndent<e&&(h=-1)),1===h)for(;E(t)||F(t);)A(t,!0,-1)?(m=!0,s=o,t.lineIndent>e?h=1:t.lineIndent===e?h=0:t.lineIndent<e&&(h=-1)):s=!1;if(s&&(s=m||r),(1===h||Z===n)&&(p=W===n||G===n?e:e+1,f=t.position-t.lineStart,1===h?s&&(_(t,f)||I(t,f,p))||O(t,p)?g=!0:(a&&S(t,p)||C(t,p)||j(t,p)?g=!0:N(t)?(g=!0,(null!==t.tag||null!==t.anchor)&&d(t,"alias node should not have any properties")):k(t,p,W===n)&&(g=!0,null===t.tag&&(t.tag="?")),null!==t.anchor&&(t.anchorMap[t.anchor]=t.result)):0===h&&(g=s&&_(t,f))),null!==t.tag&&"!"!==t.tag)if("?"===t.tag){for(c=0,u=t.implicitTypes.length;u>c;c+=1)if(l=t.implicitTypes[c],l.resolve(t.result)){t.result=l.construct(t.result),t.tag=l.tag,null!==t.anchor&&(t.anchorMap[t.anchor]=t.result);break}}else R.call(t.typeMap,t.tag)?(l=t.typeMap[t.tag],null!==t.result&&l.kind!==t.kind&&d(t,"unacceptable node kind for !<"+t.tag+'> tag; it should be "'+l.kind+'", not "'+t.kind+'"'),l.resolve(t.result)?(t.result=l.construct(t.result),null!==t.anchor&&(t.anchorMap[t.anchor]=t.result)):d(t,"cannot resolve a node with !<"+t.tag+"> explicit tag")):d(t,"unknown tag !<"+t.tag+">");return null!==t.tag||null!==t.anchor||g}function M(t){var e,n,a,s,c=t.position,u=!1;for(t.version=null,t.checkLineBreaks=t.legacy,t.tagMap={},t.anchorMap={};0!==(s=t.input.charCodeAt(t.position))&&(A(t,!0,-1),s=t.input.charCodeAt(t.position),!(t.lineIndent>0||37!==s));){for(u=!0,s=t.input.charCodeAt(++t.position),e=t.position;0!==s&&!o(s);)s=t.input.charCodeAt(++t.position);for(n=t.input.slice(e,t.position),a=[],n.length<1&&d(t,"directive name must not be less than one character in length");0!==s;){for(;r(s);)s=t.input.charCodeAt(++t.position);if(35===s){do s=t.input.charCodeAt(++t.position);while(0!==s&&!i(s));break}if(i(s))break;for(e=t.position;0!==s&&!o(s);)s=t.input.charCodeAt(++t.position);a.push(t.input.slice(e,t.position))}0!==s&&x(t),R.call(st,n)?st[n](t,n,a):m(t,'unknown document directive "'+n+'"')}return A(t,!0,-1),0===t.lineIndent&&45===t.input.charCodeAt(t.position)&&45===t.input.charCodeAt(t.position+1)&&45===t.input.charCodeAt(t.position+2)?(t.position+=3,A(t,!0,-1)):u&&d(t,"directives end mark is expected"),T(t,t.lineIndent-1,Z,!1,!0),A(t,!0,-1),t.checkLineBreaks&&tt.test(t.input.slice(c,t.position))&&m(t,"non-ASCII line breaks are interpreted as content"),t.documents.push(t.result),t.position===t.lineStart&&b(t)?void(46===t.input.charCodeAt(t.position)&&(t.position+=3,A(t,!0,-1))):void(t.position<t.length-1&&d(t,"end of the stream or a document separator is expected"))}function L(t,e){t=String(t),e=e||{},0!==t.length&&(10!==t.charCodeAt(t.length-1)&&13!==t.charCodeAt(t.length-1)&&(t+="\n"),65279===t.charCodeAt(0)&&(t=t.slice(1)));var n=new f(t,e);for(n.input+="\x00";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)M(n);return n.documents}function D(t,e,n){var i,r,o=L(t,n);for(i=0,r=o.length;r>i;i+=1)e(o[i])}function U(t,e){var n=L(t,e);if(0===n.length)return void 0;if(1===n.length)return n[0];throw new $("expected a single document in the stream, but found more")}function q(t,e,n){D(t,e,P.extend({schema:K},n))}function Y(t,e){return U(t,P.extend({schema:K},e))}for(var P=t("./common"),$=t("./exception"),B=t("./mark"),K=t("./schema/default_safe"),H=t("./schema/default_full"),R=Object.prototype.hasOwnProperty,W=1,G=2,V=3,Z=4,z=1,J=2,Q=3,X=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,tt=/[\x85\u2028\u2029]/,et=/[,\[\]\{\}]/,nt=/^(?:!|!!|![a-z\-]+!)$/i,it=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i,rt=new Array(256),ot=new Array(256),at=0;256>at;at++)rt[at]=l(at)?1:0,ot[at]=l(at);var st={YAML:function(t,e,n){var i,r,o;null!==t.version&&d(t,"duplication of %YAML directive"),1!==n.length&&d(t,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===i&&d(t,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&d(t,"unacceptable YAML version of the document"),t.version=n[0],t.checkLineBreaks=2>o,1!==o&&2!==o&&m(t,"unsupported YAML version of the document")},TAG:function(t,e,n){var i,r;2!==n.length&&d(t,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],nt.test(i)||d(t,"ill-formed tag handle (first argument) of the TAG directive"),R.call(t.tagMap,i)&&d(t,'there is a previously declared suffix for "'+i+'" tag handle'),it.test(r)||d(t,"ill-formed tag prefix (second argument) of the TAG directive"),t.tagMap[i]=r}};e.exports.loadAll=D,e.exports.load=U,e.exports.safeLoadAll=q,e.exports.safeLoad=Y},{"./common":2,"./exception":4,"./mark":6,"./schema/default_full":9,"./schema/default_safe":10}],6:[function(t,e,n){"use strict";function i(t,e,n,i,r){this.name=t,this.buffer=e,this.position=n,this.line=i,this.column=r}var r=t("./common");i.prototype.getSnippet=function(t,e){var n,i,o,a,s;if(!this.buffer)return null;for(t=t||4,e=e||75,n="",i=this.position;i>0&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(i-1));)if(i-=1,this.position-i>e/2-1){n=" ... ",i+=5;break}for(o="",a=this.position;a<this.buffer.length&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(a));)if(a+=1,a-this.position>e/2-1){o=" ... ",a-=5;break}return s=this.buffer.slice(i,a),r.repeat(" ",t)+n+s+o+"\n"+r.repeat(" ",t+this.position-i+n.length)+"^"},i.prototype.toString=function(t){var e,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),t||(e=this.getSnippet(),e&&(n+=":\n"+e)),n},e.exports=i},{"./common":2}],7:[function(t,e,n){"use strict";function i(t,e,n){var r=[];return t.include.forEach(function(t){n=i(t,e,n)}),t[e].forEach(function(t){n.forEach(function(e,n){e.tag===t.tag&&r.push(n)}),n.push(t)}),n.filter(function(t,e){return-1===r.indexOf(e)})}function r(){function t(t){i[t.tag]=t}var e,n,i={};for(e=0,n=arguments.length;n>e;e+=1)arguments[e].forEach(t);return i}function o(t){this.include=t.include||[],this.implicit=t.implicit||[],this.explicit=t.explicit||[],this.implicit.forEach(function(t){if(t.loadKind&&"scalar"!==t.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=i(this,"implicit",[]),this.compiledExplicit=i(this,"explicit",[]),this.compiledTypeMap=r(this.compiledImplicit,this.compiledExplicit)}var a=t("./common"),s=t("./exception"),c=t("./type");o.DEFAULT=null,o.create=function(){var t,e;switch(arguments.length){case 1:t=o.DEFAULT,e=arguments[0];break;case 2:t=arguments[0],e=arguments[1];break;default:throw new s("Wrong number of arguments for Schema.create function")}if(t=a.toArray(t),e=a.toArray(e),!t.every(function(t){return t instanceof o}))throw new s("Specified list of super schemas (or a single Schema object) contains a non-Schema object.");if(!e.every(function(t){return t instanceof c}))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");return new o({include:t,explicit:e})},e.exports=o},{"./common":2,"./exception":4,"./type":13}],8:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./json")]})},{"../schema":7,"./json":12}],9:[function(t,e,n){"use strict";var i=t("../schema");e.exports=i.DEFAULT=new i({include:[t("./default_safe")],explicit:[t("../type/js/undefined"),t("../type/js/regexp"),t("../type/js/function")]})},{"../schema":7,"../type/js/function":18,"../type/js/regexp":19,"../type/js/undefined":20,"./default_safe":10}],10:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./core")],implicit:[t("../type/timestamp"),t("../type/merge")],explicit:[t("../type/binary"),t("../type/omap"),t("../type/pairs"),t("../type/set")]})},{"../schema":7,"../type/binary":14,"../type/merge":22,"../type/omap":24,"../type/pairs":25,"../type/set":27,"../type/timestamp":29,"./core":8}],11:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({explicit:[t("../type/str"),t("../type/seq"),t("../type/map")]})},{"../schema":7,"../type/map":21,"../type/seq":26,"../type/str":28}],12:[function(t,e,n){"use strict";var i=t("../schema");e.exports=new i({include:[t("./failsafe")],implicit:[t("../type/null"),t("../type/bool"),t("../type/int"),t("../type/float")]})},{"../schema":7,"../type/bool":15,"../type/float":16,"../type/int":17,"../type/null":23,"./failsafe":11}],13:[function(t,e,n){"use strict";function i(t){var e={};return null!==t&&Object.keys(t).forEach(function(n){t[n].forEach(function(t){e[String(t)]=n})}),e}function r(t,e){if(e=e||{},Object.keys(e).forEach(function(e){if(-1===a.indexOf(e))throw new o('Unknown option "'+e+'" is met in definition of "'+t+'" YAML type.')}),this.tag=t,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(t){return t},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.defaultStyle=e.defaultStyle||null,this.styleAliases=i(e.styleAliases||null),-1===s.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}var o=t("./exception"),a=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],s=["scalar","sequence","mapping"];e.exports=r},{"./exception":4}],14:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;var e,n,i=0,r=t.length,o=u;for(n=0;r>n;n++)if(e=o.indexOf(t.charAt(n)),!(e>64)){if(0>e)return!1;i+=6}return i%8===0}function r(t){var e,n,i=t.replace(/[\r\n=]/g,""),r=i.length,o=u,a=0,c=[];for(e=0;r>e;e++)e%4===0&&e&&(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)),a=a<<6|o.indexOf(i.charAt(e));return n=r%4*6,0===n?(c.push(a>>16&255),c.push(a>>8&255),c.push(255&a)):18===n?(c.push(a>>10&255),c.push(a>>2&255)):12===n&&c.push(a>>4&255),s?new s(c):c}function o(t){var e,n,i="",r=0,o=t.length,a=u;for(e=0;o>e;e++)e%3===0&&e&&(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+t[e];return n=o%3,0===n?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}function a(t){return s&&s.isBuffer(t)}var s=t("buffer").Buffer,c=t("../type"),u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";e.exports=new c("tag:yaml.org,2002:binary",{kind:"scalar",resolve:i,
+construct:r,predicate:a,represent:o})},{"../type":13,buffer:30}],15:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;var e=t.length;return 4===e&&("true"===t||"True"===t||"TRUE"===t)||5===e&&("false"===t||"False"===t||"FALSE"===t)}function r(t){return"true"===t||"True"===t||"TRUE"===t}function o(t){return"[object Boolean]"===Object.prototype.toString.call(t)}var a=t("../type");e.exports=new a("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{lowercase:function(t){return t?"true":"false"},uppercase:function(t){return t?"TRUE":"FALSE"},camelcase:function(t){return t?"True":"False"}},defaultStyle:"lowercase"})},{"../type":13}],16:[function(t,e,n){"use strict";function i(t){return null===t?!1:u.test(t)?!0:!1}function r(t){var e,n,i,r;return e=t.replace(/_/g,"").toLowerCase(),n="-"===e[0]?-1:1,r=[],0<="+-".indexOf(e[0])&&(e=e.slice(1)),".inf"===e?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===e?NaN:0<=e.indexOf(":")?(e.split(":").forEach(function(t){r.unshift(parseFloat(t,10))}),e=0,i=1,r.forEach(function(t){e+=t*i,i*=60}),n*e):n*parseFloat(e,10)}function o(t,e){var n;if(isNaN(t))switch(e){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===t)switch(e){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===t)switch(e){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(t))return"-0.0";return n=t.toString(10),l.test(n)?n.replace("e",".e"):n}function a(t){return"[object Number]"===Object.prototype.toString.call(t)&&(0!==t%1||s.isNegativeZero(t))}var s=t("../common"),c=t("../type"),u=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),l=/^[-+]?[0-9]+e/;e.exports=new c("tag:yaml.org,2002:float",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o,defaultStyle:"lowercase"})},{"../common":2,"../type":13}],17:[function(t,e,n){"use strict";function i(t){return t>=48&&57>=t||t>=65&&70>=t||t>=97&&102>=t}function r(t){return t>=48&&55>=t}function o(t){return t>=48&&57>=t}function a(t){if(null===t)return!1;var e,n=t.length,a=0,s=!1;if(!n)return!1;if(e=t[a],("-"===e||"+"===e)&&(e=t[++a]),"0"===e){if(a+1===n)return!0;if(e=t[++a],"b"===e){for(a++;n>a;a++)if(e=t[a],"_"!==e){if("0"!==e&&"1"!==e)return!1;s=!0}return s}if("x"===e){for(a++;n>a;a++)if(e=t[a],"_"!==e){if(!i(t.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(e=t[a],"_"!==e){if(!r(t.charCodeAt(a)))return!1;s=!0}return s}for(;n>a;a++)if(e=t[a],"_"!==e){if(":"===e)break;if(!o(t.charCodeAt(a)))return!1;s=!0}return s?":"!==e?!0:/^(:[0-5]?[0-9])+$/.test(t.slice(a)):!1}function s(t){var e,n,i=t,r=1,o=[];return-1!==i.indexOf("_")&&(i=i.replace(/_/g,"")),e=i[0],("-"===e||"+"===e)&&("-"===e&&(r=-1),i=i.slice(1),e=i[0]),"0"===i?0:"0"===e?"b"===i[1]?r*parseInt(i.slice(2),2):"x"===i[1]?r*parseInt(i,16):r*parseInt(i,8):-1!==i.indexOf(":")?(i.split(":").forEach(function(t){o.unshift(parseInt(t,10))}),i=0,n=1,o.forEach(function(t){i+=t*n,n*=60}),r*i):r*parseInt(i,10)}function c(t){return"[object Number]"===Object.prototype.toString.call(t)&&0===t%1&&!u.isNegativeZero(t)}var u=t("../common"),l=t("../type");e.exports=new l("tag:yaml.org,2002:int",{kind:"scalar",resolve:a,construct:s,predicate:c,represent:{binary:function(t){return"0b"+t.toString(2)},octal:function(t){return"0"+t.toString(8)},decimal:function(t){return t.toString(10)},hexadecimal:function(t){return"0x"+t.toString(16).toUpperCase()}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},{"../common":2,"../type":13}],18:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;try{var e="("+t+")",n=s.parse(e,{range:!0});return"Program"!==n.type||1!==n.body.length||"ExpressionStatement"!==n.body[0].type||"FunctionExpression"!==n.body[0].expression.type?!1:!0}catch(i){return!1}}function r(t){var e,n="("+t+")",i=s.parse(n,{range:!0}),r=[];if("Program"!==i.type||1!==i.body.length||"ExpressionStatement"!==i.body[0].type||"FunctionExpression"!==i.body[0].expression.type)throw new Error("Failed to resolve function");return i.body[0].expression.params.forEach(function(t){r.push(t.name)}),e=i.body[0].expression.body.range,new Function(r,n.slice(e[0]+1,e[1]-1))}function o(t){return t.toString()}function a(t){return"[object Function]"===Object.prototype.toString.call(t)}var s;try{s=t("esprima")}catch(c){"undefined"!=typeof window&&(s=window.esprima)}var u=t("../../type");e.exports=new u("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13,esprima:"esprima"}],19:[function(t,e,n){"use strict";function i(t){if(null===t)return!1;if(0===t.length)return!1;var e=t,n=/\/([gim]*)$/.exec(t),i="";if("/"===e[0]){if(n&&(i=n[1]),i.length>3)return!1;if("/"!==e[e.length-i.length-1])return!1;e=e.slice(1,e.length-i.length-1)}try{return!0}catch(r){return!1}}function r(t){var e=t,n=/\/([gim]*)$/.exec(t),i="";return"/"===e[0]&&(n&&(i=n[1]),e=e.slice(1,e.length-i.length-1)),new RegExp(e,i)}function o(t){var e="/"+t.source+"/";return t.global&&(e+="g"),t.multiline&&(e+="m"),t.ignoreCase&&(e+="i"),e}function a(t){return"[object RegExp]"===Object.prototype.toString.call(t)}var s=t("../../type");e.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],20:[function(t,e,n){"use strict";function i(){return!0}function r(){return void 0}function o(){return""}function a(t){return"undefined"==typeof t}var s=t("../../type");e.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:o})},{"../../type":13}],21:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:map",{kind:"mapping",construct:function(t){return null!==t?t:{}}})},{"../type":13}],22:[function(t,e,n){"use strict";function i(t){return"<<"===t||null===t}var r=t("../type");e.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},{"../type":13}],23:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e=t.length;return 1===e&&"~"===t||4===e&&("null"===t||"Null"===t||"NULL"===t)}function r(){return null}function o(t){return null===t}var a=t("../type");e.exports=new a("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},{"../type":13}],24:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n,i,r,o,c=[],u=t;for(e=0,n=u.length;n>e;e+=1){if(i=u[e],o=!1,"[object Object]"!==s.call(i))return!1;for(r in i)if(a.call(i,r)){if(o)return!1;o=!0}if(!o)return!1;if(-1!==c.indexOf(r))return!1;c.push(r)}return!0}function r(t){return null!==t?t:[]}var o=t("../type"),a=Object.prototype.hasOwnProperty,s=Object.prototype.toString;e.exports=new o("tag:yaml.org,2002:omap",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],25:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n,i,r,o,s=t;for(o=new Array(s.length),e=0,n=s.length;n>e;e+=1){if(i=s[e],"[object Object]"!==a.call(i))return!1;if(r=Object.keys(i),1!==r.length)return!1;o[e]=[r[0],i[r[0]]]}return!0}function r(t){if(null===t)return[];var e,n,i,r,o,a=t;for(o=new Array(a.length),e=0,n=a.length;n>e;e+=1)i=a[e],r=Object.keys(i),o[e]=[r[0],i[r[0]]];return o}var o=t("../type"),a=Object.prototype.toString;e.exports=new o("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:i,construct:r})},{"../type":13}],26:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(t){return null!==t?t:[]}})},{"../type":13}],27:[function(t,e,n){"use strict";function i(t){if(null===t)return!0;var e,n=t;for(e in n)if(a.call(n,e)&&null!==n[e])return!1;return!0}function r(t){return null!==t?t:{}}var o=t("../type"),a=Object.prototype.hasOwnProperty;e.exports=new o("tag:yaml.org,2002:set",{kind:"mapping",resolve:i,construct:r})},{"../type":13}],28:[function(t,e,n){"use strict";var i=t("../type");e.exports=new i("tag:yaml.org,2002:str",{kind:"scalar",construct:function(t){return null!==t?t:""}})},{"../type":13}],29:[function(t,e,n){"use strict";function i(t){return null===t?!1:null===s.exec(t)?!1:!0}function r(t){var e,n,i,r,o,a,c,u,l,p,f=0,h=null;if(e=s.exec(t),null===e)throw new Error("Date resolve error");if(n=+e[1],i=+e[2]-1,r=+e[3],!e[4])return new Date(Date.UTC(n,i,r));if(o=+e[4],a=+e[5],c=+e[6],e[7]){for(f=e[7].slice(0,3);f.length<3;)f+="0";f=+f}return e[9]&&(u=+e[10],l=+(e[11]||0),h=6e4*(60*u+l),"-"===e[9]&&(h=-h)),p=new Date(Date.UTC(n,i,r,o,a,c,f)),h&&p.setTime(p.getTime()-h),p}function o(t){return t.toISOString()}var a=t("../type"),s=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?)?$");e.exports=new a("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:r,instanceOf:Date,represent:o})},{"../type":13}],30:[function(t,e,n){},{}],31:[function(t,e,n){e.exports=t("./lib/inherit")},{"./lib/inherit":32}],32:[function(e,n,i){!function(e){function r(t){var e=f(t);if(v)for(var n,i=0;n=b[i++];)t.hasOwnProperty(n)&&e.push(n);return e}function o(t,e,n){for(var i,o,a=r(n),s=0,u=a.length;u>s;)"__self"!==(i=a[s++])&&(o=n[i],g(o)&&(!c||o.toString().indexOf(".__base")>-1)?e[i]=function(n,i){var r=t[n]?t[n]:"__constructor"===n?e.__self.__parent:y;return function(){var t=this.__base;this.__base=r;var e=i.apply(this,arguments);return this.__base=t,e}}(i,o):e[i]=o)}function a(t,e){for(var n,i=1;n=t[i++];)e?g(n)?s.self(e,n.prototype,n):s.self(e,n):e=g(n)?s(t[0],n.prototype,n):s(t[0],n);return e||t[0]}function s(){var t=arguments,e=m(t[0]),n=e||g(t[0]),i=n?e?a(t[0]):t[0]:u,r=t[n?1:0]||{},s=t[n?2:1],c=r.__constructor||n&&i.prototype.__constructor?function(){return this.__constructor.apply(this,arguments)}:n?function(){return i.apply(this,arguments)}:function(){};if(!n)return c.prototype=r,c.prototype.__self=c.prototype.constructor=c,h(c,s);h(c,i),c.__parent=i;var l=i.prototype,f=c.prototype=p(l);return f.__self=f.constructor=c,r&&o(l,f,r),s&&o(i,c,s),c}var c=function(){"_"}.toString().indexOf("_")>-1,u=function(){},l=Object.prototype.hasOwnProperty,p=Object.create||function(t){var e=function(){};return e.prototype=t,new e},f=Object.keys||function(t){var e=[];for(var n in t)l.call(t,n)&&e.push(n);return e},h=function(t,e){for(var n in e)l.call(e,n)&&(t[n]=e[n]);return t},d=Object.prototype.toString,m=Array.isArray||function(t){return"[object Array]"===d.call(t)},g=function(t){return"[object Function]"===d.call(t)},y=function(){},v=!0,x={toString:""};for(var A in x)x.hasOwnProperty(A)&&(v=!1);var b=v?["toString","valueOf"]:null;s.self=function(){var t=arguments,e=m(t[0]),n=e?a(t[0],t[0][0]):t[0],i=t[1],r=t[2],s=n.prototype;return i&&o(s,s,i),r&&o(n,n,r),n};var w=!0;"object"==typeof i&&(n.exports=s,w=!1),"object"==typeof modules&&(modules.define("inherit",function(t){t(s)}),w=!1),"function"==typeof t&&(t(function(t,e,n){n.exports=s}),w=!1),w&&(e.inherit=s)}(this)},{}],"/":[function(t,e,n){"use strict";var i=t("./lib/js-yaml.js");e.exports=i},{"./lib/js-yaml.js":1}]},{},[])("/")});
diff --git a/profiles/killpay/src/main/webapp/lib/lodash.min.js b/profiles/killpay/src/main/webapp/lib/lodash.min.js
new file mode 100644
index 0000000..05870d1
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/lodash.min.js
@@ -0,0 +1,102 @@
+/**
+ * @license
+ * lodash 3.10.1 (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
+ * Build: `lodash compat -o ./lodash.js`
+ */
+;(function(){function n(n,t){if(n!==t){var r=null===n,e=n===w,u=n===n,o=null===t,i=t===w,f=t===t;if(n>t&&!o||!u||r&&!i&&f||e&&f)return 1;if(n<t&&!r||!f||o&&!e&&u||i&&u)return-1}return 0}function t(n,t,r){for(var e=n.length,u=r?e:-1;r?u--:++u<e;)if(t(n[u],u,n))return u;return-1}function r(n,t,r){if(t!==t)return p(n,r);r-=1;for(var e=n.length;++r<e;)if(n[r]===t)return r;return-1}function e(n){return typeof n=="function"||false}function u(n){return null==n?"":n+""}function o(n,t){for(var r=-1,e=n.length;++r<e&&-1<t.indexOf(n.charAt(r)););
+return r}function i(n,t){for(var r=n.length;r--&&-1<t.indexOf(n.charAt(r)););return r}function f(t,r){return n(t.a,r.a)||t.b-r.b}function a(n){return Nn[n]}function c(n){return Tn[n]}function l(n,t,r){return t?n=Bn[n]:r&&(n=Dn[n]),"\\"+n}function s(n){return"\\"+Dn[n]}function p(n,t,r){var e=n.length;for(t+=r?0:-1;r?t--:++t<e;){var u=n[t];if(u!==u)return t}return-1}function h(n){return!!n&&typeof n=="object"}function _(n){return 160>=n&&9<=n&&13>=n||32==n||160==n||5760==n||6158==n||8192<=n&&(8202>=n||8232==n||8233==n||8239==n||8287==n||12288==n||65279==n);
+}function v(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;)n[r]===t&&(n[r]=P,o[++u]=r);return o}function g(n){for(var t=-1,r=n.length;++t<r&&_(n.charCodeAt(t)););return t}function y(n){for(var t=n.length;t--&&_(n.charCodeAt(t)););return t}function d(n){return Pn[n]}function m(_){function Nn(n){if(h(n)&&!(Wo(n)||n instanceof zn)){if(n instanceof Pn)return n;if(eu.call(n,"__chain__")&&eu.call(n,"__wrapped__"))return qr(n)}return new Pn(n)}function Tn(){}function Pn(n,t,r){this.__wrapped__=n,this.__actions__=r||[],
+this.__chain__=!!t}function zn(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=false,this.__iteratees__=[],this.__takeCount__=Cu,this.__views__=[]}function Bn(){this.__data__={}}function Dn(n){var t=n?n.length:0;for(this.data={hash:mu(null),set:new hu};t--;)this.push(n[t])}function Mn(n,t){var r=n.data;return(typeof t=="string"||de(t)?r.set.has(t):r.hash[t])?0:-1}function qn(n,t){var r=-1,e=n.length;for(t||(t=De(e));++r<e;)t[r]=n[r];return t}function Kn(n,t){for(var r=-1,e=n.length;++r<e&&false!==t(n[r],r,n););
+return n}function Vn(n,t){for(var r=-1,e=n.length;++r<e;)if(!t(n[r],r,n))return false;return true}function Zn(n,t){for(var r=-1,e=n.length,u=-1,o=[];++r<e;){var i=n[r];t(i,r,n)&&(o[++u]=i)}return o}function Xn(n,t){for(var r=-1,e=n.length,u=De(e);++r<e;)u[r]=t(n[r],r,n);return u}function Hn(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];return n}function Qn(n,t,r,e){var u=-1,o=n.length;for(e&&o&&(r=n[++u]);++u<o;)r=t(r,n[u],u,n);return r}function nt(n,t){for(var r=-1,e=n.length;++r<e;)if(t(n[r],r,n))return true;
+return false}function tt(n,t,r,e){return n!==w&&eu.call(e,r)?n:t}function rt(n,t,r){for(var e=-1,u=Ko(t),o=u.length;++e<o;){var i=u[e],f=n[i],a=r(f,t[i],i,n,t);(a===a?a===f:f!==f)&&(f!==w||i in n)||(n[i]=a)}return n}function et(n,t){return null==t?n:ot(t,Ko(t),n)}function ut(n,t){for(var r=-1,e=null==n,u=!e&&Sr(n),o=u?n.length:0,i=t.length,f=De(i);++r<i;){var a=t[r];f[r]=u?Ur(a,o)?n[a]:w:e?w:n[a]}return f}function ot(n,t,r){r||(r={});for(var e=-1,u=t.length;++e<u;){var o=t[e];r[o]=n[o]}return r}function it(n,t,r){
+var e=typeof n;return"function"==e?t===w?n:Dt(n,t,r):null==n?Ne:"object"==e?At(n):t===w?Be(n):jt(n,t)}function ft(n,t,r,e,u,o,i){var f;if(r&&(f=u?r(n,e,u):r(n)),f!==w)return f;if(!de(n))return n;if(e=Wo(n)){if(f=Ir(n),!t)return qn(n,f)}else{var a=ou.call(n),c=a==K;if(a!=Z&&a!=z&&(!c||u))return Ln[a]?Er(n,a,t):u?n:{};if(Gn(n))return u?n:{};if(f=Rr(c?{}:n),!t)return et(f,n)}for(o||(o=[]),i||(i=[]),u=o.length;u--;)if(o[u]==n)return i[u];return o.push(n),i.push(f),(e?Kn:gt)(n,function(e,u){f[u]=ft(e,t,r,u,n,o,i);
+}),f}function at(n,t,r){if(typeof n!="function")throw new Xe(T);return _u(function(){n.apply(w,r)},t)}function ct(n,t){var e=n?n.length:0,u=[];if(!e)return u;var o=-1,i=jr(),f=i===r,a=f&&t.length>=F&&mu&&hu?new Dn(t):null,c=t.length;a&&(i=Mn,f=false,t=a);n:for(;++o<e;)if(a=n[o],f&&a===a){for(var l=c;l--;)if(t[l]===a)continue n;u.push(a)}else 0>i(t,a,0)&&u.push(a);return u}function lt(n,t){var r=true;return zu(n,function(n,e,u){return r=!!t(n,e,u)}),r}function st(n,t,r,e){var u=e,o=u;return zu(n,function(n,i,f){
+i=+t(n,i,f),(r(i,u)||i===e&&i===o)&&(u=i,o=n)}),o}function pt(n,t){var r=[];return zu(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function ht(n,t,r,e){var u;return r(n,function(n,r,o){return t(n,r,o)?(u=e?r:n,false):void 0}),u}function _t(n,t,r,e){e||(e=[]);for(var u=-1,o=n.length;++u<o;){var i=n[u];h(i)&&Sr(i)&&(r||Wo(i)||_e(i))?t?_t(i,t,r,e):Hn(e,i):r||(e[e.length]=i)}return e}function vt(n,t){return Du(n,t,Ee)}function gt(n,t){return Du(n,t,Ko)}function yt(n,t){return Mu(n,t,Ko)}function dt(n,t){for(var r=-1,e=t.length,u=-1,o=[];++r<e;){
+var i=t[r];ye(n[i])&&(o[++u]=i)}return o}function mt(n,t,r){if(null!=n){n=Dr(n),r!==w&&r in n&&(t=[r]),r=0;for(var e=t.length;null!=n&&r<e;)n=Dr(n)[t[r++]];return r&&r==e?n:w}}function wt(n,t,r,e,u,o){if(n===t)return true;if(null==n||null==t||!de(n)&&!h(t))return n!==n&&t!==t;n:{var i=wt,f=Wo(n),a=Wo(t),c=B,l=B;f||(c=ou.call(n),c==z?c=Z:c!=Z&&(f=je(n))),a||(l=ou.call(t),l==z?l=Z:l!=Z&&je(t));var s=c==Z&&!Gn(n),a=l==Z&&!Gn(t),l=c==l;if(!l||f||s){if(!e&&(c=s&&eu.call(n,"__wrapped__"),a=a&&eu.call(t,"__wrapped__"),
+c||a)){n=i(c?n.value():n,a?t.value():t,r,e,u,o);break n}if(l){for(u||(u=[]),o||(o=[]),c=u.length;c--;)if(u[c]==n){n=o[c]==t;break n}u.push(n),o.push(t),n=(f?mr:xr)(n,t,i,r,e,u,o),u.pop(),o.pop()}else n=false}else n=wr(n,t,c)}return n}function xt(n,t,r){var e=t.length,u=e,o=!r;if(null==n)return!u;for(n=Dr(n);e--;){var i=t[e];if(o&&i[2]?i[1]!==n[i[0]]:!(i[0]in n))return false}for(;++e<u;){var i=t[e],f=i[0],a=n[f],c=i[1];if(o&&i[2]){if(a===w&&!(f in n))return false}else if(i=r?r(a,c,f):w,i===w?!wt(c,a,r,true):!i)return false;
+}return true}function bt(n,t){var r=-1,e=Sr(n)?De(n.length):[];return zu(n,function(n,u,o){e[++r]=t(n,u,o)}),e}function At(n){var t=kr(n);if(1==t.length&&t[0][2]){var r=t[0][0],e=t[0][1];return function(n){return null==n?false:(n=Dr(n),n[r]===e&&(e!==w||r in n))}}return function(n){return xt(n,t)}}function jt(n,t){var r=Wo(n),e=Wr(n)&&t===t&&!de(t),u=n+"";return n=Mr(n),function(o){if(null==o)return false;var i=u;if(o=Dr(o),!(!r&&e||i in o)){if(o=1==n.length?o:mt(o,St(n,0,-1)),null==o)return false;i=Gr(n),o=Dr(o);
+}return o[i]===t?t!==w||i in o:wt(t,o[i],w,true)}}function kt(n,t,r,e,u){if(!de(n))return n;var o=Sr(t)&&(Wo(t)||je(t)),i=o?w:Ko(t);return Kn(i||t,function(f,a){if(i&&(a=f,f=t[a]),h(f)){e||(e=[]),u||(u=[]);n:{for(var c=a,l=e,s=u,p=l.length,_=t[c];p--;)if(l[p]==_){n[c]=s[p];break n}var p=n[c],v=r?r(p,_,c,n,t):w,g=v===w;g&&(v=_,Sr(_)&&(Wo(_)||je(_))?v=Wo(p)?p:Sr(p)?qn(p):[]:xe(_)||_e(_)?v=_e(p)?Ie(p):xe(p)?p:{}:g=false),l.push(_),s.push(v),g?n[c]=kt(v,_,r,l,s):(v===v?v!==p:p===p)&&(n[c]=v)}}else c=n[a],
+l=r?r(c,f,a,n,t):w,(s=l===w)&&(l=f),l===w&&(!o||a in n)||!s&&(l===l?l===c:c!==c)||(n[a]=l)}),n}function Ot(n){return function(t){return null==t?w:Dr(t)[n]}}function It(n){var t=n+"";return n=Mr(n),function(r){return mt(r,n,t)}}function Rt(n,t){for(var r=n?t.length:0;r--;){var e=t[r];if(e!=u&&Ur(e)){var u=e;vu.call(n,e,1)}}return n}function Et(n,t){return n+wu(Ru()*(t-n+1))}function Ct(n,t,r,e,u){return u(n,function(n,u,o){r=e?(e=false,n):t(r,n,u,o)}),r}function St(n,t,r){var e=-1,u=n.length;for(t=null==t?0:+t||0,
+0>t&&(t=-t>u?0:u+t),r=r===w||r>u?u:+r||0,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=De(u);++e<u;)r[e]=n[e+t];return r}function Ut(n,t){var r;return zu(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function $t(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].c;return n}function Wt(t,r,e){var u=br(),o=-1;return r=Xn(r,function(n){return u(n)}),t=bt(t,function(n){return{a:Xn(r,function(t){return t(n)}),b:++o,c:n}}),$t(t,function(t,r){var u;n:{for(var o=-1,i=t.a,f=r.a,a=i.length,c=e.length;++o<a;)if(u=n(i[o],f[o])){
+if(o>=c)break n;o=e[o],u*="asc"===o||true===o?1:-1;break n}u=t.b-r.b}return u})}function Ft(n,t){var r=0;return zu(n,function(n,e,u){r+=+t(n,e,u)||0}),r}function Lt(n,t){var e=-1,u=jr(),o=n.length,i=u===r,f=i&&o>=F,a=f&&mu&&hu?new Dn(void 0):null,c=[];a?(u=Mn,i=false):(f=false,a=t?[]:c);n:for(;++e<o;){var l=n[e],s=t?t(l,e,n):l;if(i&&l===l){for(var p=a.length;p--;)if(a[p]===s)continue n;t&&a.push(s),c.push(l)}else 0>u(a,s,0)&&((t||f)&&a.push(s),c.push(l))}return c}function Nt(n,t){for(var r=-1,e=t.length,u=De(e);++r<e;)u[r]=n[t[r]];
+return u}function Tt(n,t,r,e){for(var u=n.length,o=e?u:-1;(e?o--:++o<u)&&t(n[o],o,n););return r?St(n,e?0:o,e?o+1:u):St(n,e?o+1:0,e?u:o)}function Pt(n,t){var r=n;r instanceof zn&&(r=r.value());for(var e=-1,u=t.length;++e<u;)var o=t[e],r=o.func.apply(o.thisArg,Hn([r],o.args));return r}function zt(n,t,r){var e=0,u=n?n.length:e;if(typeof t=="number"&&t===t&&u<=Uu){for(;e<u;){var o=e+u>>>1,i=n[o];(r?i<=t:i<t)&&null!==i?e=o+1:u=o}return u}return Bt(n,t,Ne,r)}function Bt(n,t,r,e){t=r(t);for(var u=0,o=n?n.length:0,i=t!==t,f=null===t,a=t===w;u<o;){
+var c=wu((u+o)/2),l=r(n[c]),s=l!==w,p=l===l;(i?p||e:f?p&&s&&(e||null!=l):a?p&&(e||s):null==l?0:e?l<=t:l<t)?u=c+1:o=c}return ku(o,Su)}function Dt(n,t,r){if(typeof n!="function")return Ne;if(t===w)return n;switch(r){case 1:return function(r){return n.call(t,r)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,o){return n.call(t,r,e,u,o)};case 5:return function(r,e,u,o,i){return n.call(t,r,e,u,o,i)}}return function(){return n.apply(t,arguments)}}function Mt(n){var t=new au(n.byteLength);
+return new gu(t).set(new gu(n)),t}function qt(n,t,r){for(var e=r.length,u=-1,o=ju(n.length-e,0),i=-1,f=t.length,a=De(f+o);++i<f;)a[i]=t[i];for(;++u<e;)a[r[u]]=n[u];for(;o--;)a[i++]=n[u++];return a}function Kt(n,t,r){for(var e=-1,u=r.length,o=-1,i=ju(n.length-u,0),f=-1,a=t.length,c=De(i+a);++o<i;)c[o]=n[o];for(i=o;++f<a;)c[i+f]=t[f];for(;++e<u;)c[i+r[e]]=n[o++];return c}function Vt(n,t){return function(r,e,u){var o=t?t():{};if(e=br(e,u,3),Wo(r)){u=-1;for(var i=r.length;++u<i;){var f=r[u];n(o,f,e(f,u,r),r);
+}}else zu(r,function(t,r,u){n(o,t,e(t,r,u),u)});return o}}function Zt(n){return pe(function(t,r){var e=-1,u=null==t?0:r.length,o=2<u?r[u-2]:w,i=2<u?r[2]:w,f=1<u?r[u-1]:w;for(typeof o=="function"?(o=Dt(o,f,5),u-=2):(o=typeof f=="function"?f:w,u-=o?1:0),i&&$r(r[0],r[1],i)&&(o=3>u?w:o,u=1);++e<u;)(i=r[e])&&n(t,i,o);return t})}function Yt(n,t){return function(r,e){var u=r?Vu(r):0;if(!Lr(u))return n(r,e);for(var o=t?u:-1,i=Dr(r);(t?o--:++o<u)&&false!==e(i[o],o,i););return r}}function Gt(n){return function(t,r,e){
+var u=Dr(t);e=e(t);for(var o=e.length,i=n?o:-1;n?i--:++i<o;){var f=e[i];if(false===r(u[f],f,u))break}return t}}function Jt(n,t){function r(){return(this&&this!==Yn&&this instanceof r?e:n).apply(t,arguments)}var e=Ht(n);return r}function Xt(n){return function(t){var r=-1;t=Fe(Ue(t));for(var e=t.length,u="";++r<e;)u=n(u,t[r],r);return u}}function Ht(n){return function(){var t=arguments;switch(t.length){case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:return new n(t[0],t[1],t[2]);
+case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=Pu(n.prototype),t=n.apply(r,t);return de(t)?t:r}}function Qt(n){function t(r,e,u){return u&&$r(r,e,u)&&(e=w),r=dr(r,n,w,w,w,w,w,e),r.placeholder=t.placeholder,r}return t}function nr(n,t){return pe(function(r){var e=r[0];return null==e?e:(r.push(t),n.apply(w,r))})}function tr(n,t){return function(r,e,u){
+if(u&&$r(r,e,u)&&(e=w),e=br(e,u,3),1==e.length){u=r=Wo(r)?r:Br(r);for(var o=e,i=-1,f=u.length,a=t,c=a;++i<f;){var l=u[i],s=+o(l);n(s,a)&&(a=s,c=l)}if(u=c,!r.length||u!==t)return u}return st(r,e,n,t)}}function rr(n,r){return function(e,u,o){return u=br(u,o,3),Wo(e)?(u=t(e,u,r),-1<u?e[u]:w):ht(e,u,n)}}function er(n){return function(r,e,u){return r&&r.length?(e=br(e,u,3),t(r,e,n)):-1}}function ur(n){return function(t,r,e){return r=br(r,e,3),ht(t,r,n,true)}}function or(n){return function(){for(var t,r=arguments.length,e=n?r:-1,u=0,o=De(r);n?e--:++e<r;){
+var i=o[u++]=arguments[e];if(typeof i!="function")throw new Xe(T);!t&&Pn.prototype.thru&&"wrapper"==Ar(i)&&(t=new Pn([],true))}for(e=t?-1:r;++e<r;){var i=o[e],u=Ar(i),f="wrapper"==u?Ku(i):w;t=f&&Fr(f[0])&&f[1]==(E|k|I|C)&&!f[4].length&&1==f[9]?t[Ar(f[0])].apply(t,f[3]):1==i.length&&Fr(i)?t[u]():t.thru(i)}return function(){var n=arguments,e=n[0];if(t&&1==n.length&&Wo(e)&&e.length>=F)return t.plant(e).value();for(var u=0,n=r?o[u].apply(this,n):e;++u<r;)n=o[u].call(this,n);return n}}}function ir(n,t){
+return function(r,e,u){return typeof e=="function"&&u===w&&Wo(r)?n(r,e):t(r,Dt(e,u,3))}}function fr(n){return function(t,r,e){return(typeof r!="function"||e!==w)&&(r=Dt(r,e,3)),n(t,r,Ee)}}function ar(n){return function(t,r,e){return(typeof r!="function"||e!==w)&&(r=Dt(r,e,3)),n(t,r)}}function cr(n){return function(t,r,e){var u={};return r=br(r,e,3),gt(t,function(t,e,o){o=r(t,e,o),e=n?o:e,t=n?t:o,u[e]=t}),u}}function lr(n){return function(t,r,e){return t=u(t),(n?t:"")+_r(t,r,e)+(n?"":t)}}function sr(n){
+var t=pe(function(r,e){var u=v(e,t.placeholder);return dr(r,n,w,e,u)});return t}function pr(n,t){return function(r,e,u,o){var i=3>arguments.length;return typeof e=="function"&&o===w&&Wo(r)?n(r,e,u,i):Ct(r,br(e,o,4),u,i,t)}}function hr(n,t,r,e,u,o,i,f,a,c){function l(){for(var m=arguments.length,x=m,j=De(m);x--;)j[x]=arguments[x];if(e&&(j=qt(j,e,u)),o&&(j=Kt(j,o,i)),_||y){var x=l.placeholder,k=v(j,x),m=m-k.length;if(m<c){var O=f?qn(f):w,m=ju(c-m,0),E=_?k:w,k=_?w:k,C=_?j:w,j=_?w:j;return t|=_?I:R,t&=~(_?R:I),
+g||(t&=~(b|A)),j=[n,t,r,C,E,j,k,O,a,m],O=hr.apply(w,j),Fr(n)&&Zu(O,j),O.placeholder=x,O}}if(x=p?r:this,O=h?x[n]:n,f)for(m=j.length,E=ku(f.length,m),k=qn(j);E--;)C=f[E],j[E]=Ur(C,m)?k[C]:w;return s&&a<j.length&&(j.length=a),this&&this!==Yn&&this instanceof l&&(O=d||Ht(n)),O.apply(x,j)}var s=t&E,p=t&b,h=t&A,_=t&k,g=t&j,y=t&O,d=h?w:Ht(n);return l}function _r(n,t,r){return n=n.length,t=+t,n<t&&bu(t)?(t-=n,r=null==r?" ":r+"",$e(r,du(t/r.length)).slice(0,t)):""}function vr(n,t,r,e){function u(){for(var t=-1,f=arguments.length,a=-1,c=e.length,l=De(c+f);++a<c;)l[a]=e[a];
+for(;f--;)l[a++]=arguments[++t];return(this&&this!==Yn&&this instanceof u?i:n).apply(o?r:this,l)}var o=t&b,i=Ht(n);return u}function gr(n){var t=Ve[n];return function(n,r){return(r=r===w?0:+r||0)?(r=su(10,r),t(n*r)/r):t(n)}}function yr(n){return function(t,r,e,u){var o=br(e);return null==e&&o===it?zt(t,r,n):Bt(t,r,o(e,u,1),n)}}function dr(n,t,r,e,u,o,i,f){var a=t&A;if(!a&&typeof n!="function")throw new Xe(T);var c=e?e.length:0;if(c||(t&=~(I|R),e=u=w),c-=u?u.length:0,t&R){var l=e,s=u;e=u=w}var p=a?w:Ku(n);
+return r=[n,t,r,e,u,l,s,o,i,f],p&&(e=r[1],t=p[1],f=e|t,u=t==E&&e==k||t==E&&e==C&&r[7].length<=p[8]||t==(E|C)&&e==k,(f<E||u)&&(t&b&&(r[2]=p[2],f|=e&b?0:j),(e=p[3])&&(u=r[3],r[3]=u?qt(u,e,p[4]):qn(e),r[4]=u?v(r[3],P):qn(p[4])),(e=p[5])&&(u=r[5],r[5]=u?Kt(u,e,p[6]):qn(e),r[6]=u?v(r[5],P):qn(p[6])),(e=p[7])&&(r[7]=qn(e)),t&E&&(r[8]=null==r[8]?p[8]:ku(r[8],p[8])),null==r[9]&&(r[9]=p[9]),r[0]=p[0],r[1]=f),t=r[1],f=r[9]),r[9]=null==f?a?0:n.length:ju(f-c,0)||0,n=t==b?Jt(r[0],r[2]):t!=I&&t!=(b|I)||r[4].length?hr.apply(w,r):vr.apply(w,r),
+(p?qu:Zu)(n,r)}function mr(n,t,r,e,u,o,i){var f=-1,a=n.length,c=t.length;if(a!=c&&(!u||c<=a))return false;for(;++f<a;){var l=n[f],c=t[f],s=e?e(u?c:l,u?l:c,f):w;if(s!==w){if(s)continue;return false}if(u){if(!nt(t,function(n){return l===n||r(l,n,e,u,o,i)}))return false}else if(l!==c&&!r(l,c,e,u,o,i))return false}return true}function wr(n,t,r){switch(r){case D:case M:return+n==+t;case q:return n.name==t.name&&n.message==t.message;case V:return n!=+n?t!=+t:n==+t;case Y:case G:return n==t+""}return false}function xr(n,t,r,e,u,o,i){
+var f=Ko(n),a=f.length,c=Ko(t).length;if(a!=c&&!u)return false;for(c=a;c--;){var l=f[c];if(!(u?l in t:eu.call(t,l)))return false}for(var s=u;++c<a;){var l=f[c],p=n[l],h=t[l],_=e?e(u?h:p,u?p:h,l):w;if(_===w?!r(p,h,e,u,o,i):!_)return false;s||(s="constructor"==l)}return s||(r=n.constructor,e=t.constructor,!(r!=e&&"constructor"in n&&"constructor"in t)||typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)?true:false}function br(n,t,r){var e=Nn.callback||Le,e=e===Le?it:e;return r?e(n,t,r):e}function Ar(n){
+for(var t=n.name+"",r=Fu[t],e=r?r.length:0;e--;){var u=r[e],o=u.func;if(null==o||o==n)return u.name}return t}function jr(n,t,e){var u=Nn.indexOf||Yr,u=u===Yr?r:u;return n?u(n,t,e):u}function kr(n){n=Ce(n);for(var t=n.length;t--;){var r,e=n[t];r=n[t][1],r=r===r&&!de(r),e[2]=r}return n}function Or(n,t){var r=null==n?w:n[t];return me(r)?r:w}function Ir(n){var t=n.length,r=new n.constructor(t);return t&&"string"==typeof n[0]&&eu.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function Rr(n){return n=n.constructor,
+typeof n=="function"&&n instanceof n||(n=Ye),new n}function Er(n,t,r){var e=n.constructor;switch(t){case J:return Mt(n);case D:case M:return new e(+n);case X:case H:case Q:case nn:case tn:case rn:case en:case un:case on:return e instanceof e&&(e=Lu[t]),t=n.buffer,new e(r?Mt(t):t,n.byteOffset,n.length);case V:case G:return new e(n);case Y:var u=new e(n.source,kn.exec(n));u.lastIndex=n.lastIndex}return u}function Cr(n,t,r){return null==n||Wr(t,n)||(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),t=Gr(t)),
+t=null==n?n:n[t],null==t?w:t.apply(n,r)}function Sr(n){return null!=n&&Lr(Vu(n))}function Ur(n,t){return n=typeof n=="number"||Rn.test(n)?+n:-1,t=null==t?$u:t,-1<n&&0==n%1&&n<t}function $r(n,t,r){if(!de(r))return false;var e=typeof t;return("number"==e?Sr(r)&&Ur(t,r.length):"string"==e&&t in r)?(t=r[t],n===n?n===t:t!==t):false}function Wr(n,t){var r=typeof n;return"string"==r&&dn.test(n)||"number"==r?true:Wo(n)?false:!yn.test(n)||null!=t&&n in Dr(t)}function Fr(n){var t=Ar(n),r=Nn[t];return typeof r=="function"&&t in zn.prototype?n===r?true:(t=Ku(r),
+!!t&&n===t[0]):false}function Lr(n){return typeof n=="number"&&-1<n&&0==n%1&&n<=$u}function Nr(n,t){return n===w?t:Fo(n,t,Nr)}function Tr(n,t){n=Dr(n);for(var r=-1,e=t.length,u={};++r<e;){var o=t[r];o in n&&(u[o]=n[o])}return u}function Pr(n,t){var r={};return vt(n,function(n,e,u){t(n,e,u)&&(r[e]=n)}),r}function zr(n){for(var t=Ee(n),r=t.length,e=r&&n.length,u=!!e&&Lr(e)&&(Wo(n)||_e(n)||Ae(n)),o=-1,i=[];++o<r;){var f=t[o];(u&&Ur(f,e)||eu.call(n,f))&&i.push(f)}return i}function Br(n){return null==n?[]:Sr(n)?Nn.support.unindexedChars&&Ae(n)?n.split(""):de(n)?n:Ye(n):Se(n);
+}function Dr(n){if(Nn.support.unindexedChars&&Ae(n)){for(var t=-1,r=n.length,e=Ye(n);++t<r;)e[t]=n.charAt(t);return e}return de(n)?n:Ye(n)}function Mr(n){if(Wo(n))return n;var t=[];return u(n).replace(mn,function(n,r,e,u){t.push(e?u.replace(An,"$1"):r||n)}),t}function qr(n){return n instanceof zn?n.clone():new Pn(n.__wrapped__,n.__chain__,qn(n.__actions__))}function Kr(n,t,r){return n&&n.length?((r?$r(n,t,r):null==t)&&(t=1),St(n,0>t?0:t)):[]}function Vr(n,t,r){var e=n?n.length:0;return e?((r?$r(n,t,r):null==t)&&(t=1),
+t=e-(+t||0),St(n,0,0>t?0:t)):[]}function Zr(n){return n?n[0]:w}function Yr(n,t,e){var u=n?n.length:0;if(!u)return-1;if(typeof e=="number")e=0>e?ju(u+e,0):e;else if(e)return e=zt(n,t),e<u&&(t===t?t===n[e]:n[e]!==n[e])?e:-1;return r(n,t,e||0)}function Gr(n){var t=n?n.length:0;return t?n[t-1]:w}function Jr(n){return Kr(n,1)}function Xr(n,t,e,u){if(!n||!n.length)return[];null!=t&&typeof t!="boolean"&&(u=e,e=$r(n,t,u)?w:t,t=false);var o=br();if((null!=e||o!==it)&&(e=o(e,u,3)),t&&jr()===r){t=e;var i;e=-1,
+u=n.length;for(var o=-1,f=[];++e<u;){var a=n[e],c=t?t(a,e,n):a;e&&i===c||(i=c,f[++o]=a)}n=f}else n=Lt(n,e);return n}function Hr(n){if(!n||!n.length)return[];var t=-1,r=0;n=Zn(n,function(n){return Sr(n)?(r=ju(n.length,r),true):void 0});for(var e=De(r);++t<r;)e[t]=Xn(n,Ot(t));return e}function Qr(n,t,r){return n&&n.length?(n=Hr(n),null==t?n:(t=Dt(t,r,4),Xn(n,function(n){return Qn(n,t,w,true)}))):[]}function ne(n,t){var r=-1,e=n?n.length:0,u={};for(!e||t||Wo(n[0])||(t=[]);++r<e;){var o=n[r];t?u[o]=t[r]:o&&(u[o[0]]=o[1]);
+}return u}function te(n){return n=Nn(n),n.__chain__=true,n}function re(n,t,r){return t.call(r,n)}function ee(n,t,r){var e=Wo(n)?Vn:lt;return r&&$r(n,t,r)&&(t=w),(typeof t!="function"||r!==w)&&(t=br(t,r,3)),e(n,t)}function ue(n,t,r){var e=Wo(n)?Zn:pt;return t=br(t,r,3),e(n,t)}function oe(n,t,r,e){var u=n?Vu(n):0;return Lr(u)||(n=Se(n),u=n.length),r=typeof r!="number"||e&&$r(t,r,e)?0:0>r?ju(u+r,0):r||0,typeof n=="string"||!Wo(n)&&Ae(n)?r<=u&&-1<n.indexOf(t,r):!!u&&-1<jr(n,t,r)}function ie(n,t,r){var e=Wo(n)?Xn:bt;
+return t=br(t,r,3),e(n,t)}function fe(n,t,r){if(r?$r(n,t,r):null==t){n=Br(n);var e=n.length;return 0<e?n[Et(0,e-1)]:w}r=-1,n=Oe(n);var e=n.length,u=e-1;for(t=ku(0>t?0:+t||0,e);++r<t;){var e=Et(r,u),o=n[e];n[e]=n[r],n[r]=o}return n.length=t,n}function ae(n,t,r){var e=Wo(n)?nt:Ut;return r&&$r(n,t,r)&&(t=w),(typeof t!="function"||r!==w)&&(t=br(t,r,3)),e(n,t)}function ce(n,t){var r;if(typeof t!="function"){if(typeof n!="function")throw new Xe(T);var e=n;n=t,t=e}return function(){return 0<--n&&(r=t.apply(this,arguments)),
+1>=n&&(t=w),r}}function le(n,t,r){function e(t,r){r&&cu(r),a=p=h=w,t&&(_=wo(),c=n.apply(s,f),p||a||(f=s=w))}function u(){var n=t-(wo()-l);0>=n||n>t?e(h,a):p=_u(u,n)}function o(){e(g,p)}function i(){if(f=arguments,l=wo(),s=this,h=g&&(p||!y),false===v)var r=y&&!p;else{a||y||(_=l);var e=v-(l-_),i=0>=e||e>v;i?(a&&(a=cu(a)),_=l,c=n.apply(s,f)):a||(a=_u(o,e))}return i&&p?p=cu(p):p||t===v||(p=_u(u,t)),r&&(i=true,c=n.apply(s,f)),!i||p||a||(f=s=w),c}var f,a,c,l,s,p,h,_=0,v=false,g=true;if(typeof n!="function")throw new Xe(T);
+if(t=0>t?0:+t||0,true===r)var y=true,g=false;else de(r)&&(y=!!r.leading,v="maxWait"in r&&ju(+r.maxWait||0,t),g="trailing"in r?!!r.trailing:g);return i.cancel=function(){p&&cu(p),a&&cu(a),_=0,a=p=h=w},i}function se(n,t){if(typeof n!="function"||t&&typeof t!="function")throw new Xe(T);var r=function(){var e=arguments,u=t?t.apply(this,e):e[0],o=r.cache;return o.has(u)?o.get(u):(e=n.apply(this,e),r.cache=o.set(u,e),e)};return r.cache=new se.Cache,r}function pe(n,t){if(typeof n!="function")throw new Xe(T);return t=ju(t===w?n.length-1:+t||0,0),
+function(){for(var r=arguments,e=-1,u=ju(r.length-t,0),o=De(u);++e<u;)o[e]=r[t+e];switch(t){case 0:return n.call(this,o);case 1:return n.call(this,r[0],o);case 2:return n.call(this,r[0],r[1],o)}for(u=De(t+1),e=-1;++e<t;)u[e]=r[e];return u[t]=o,n.apply(this,u)}}function he(n,t){return n>t}function _e(n){return h(n)&&Sr(n)&&eu.call(n,"callee")&&!pu.call(n,"callee")}function ve(n,t,r,e){return e=(r=typeof r=="function"?Dt(r,e,3):w)?r(n,t):w,e===w?wt(n,t,r):!!e}function ge(n){return h(n)&&typeof n.message=="string"&&ou.call(n)==q;
+}function ye(n){return de(n)&&ou.call(n)==K}function de(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}function me(n){return null==n?false:ye(n)?fu.test(ru.call(n)):h(n)&&(Gn(n)?fu:In).test(n)}function we(n){return typeof n=="number"||h(n)&&ou.call(n)==V}function xe(n){var t;if(!h(n)||ou.call(n)!=Z||Gn(n)||_e(n)||!(eu.call(n,"constructor")||(t=n.constructor,typeof t!="function"||t instanceof t)))return false;var r;return Nn.support.ownLast?(vt(n,function(n,t,e){return r=eu.call(e,t),false}),false!==r):(vt(n,function(n,t){
+r=t}),r===w||eu.call(n,r))}function be(n){return de(n)&&ou.call(n)==Y}function Ae(n){return typeof n=="string"||h(n)&&ou.call(n)==G}function je(n){return h(n)&&Lr(n.length)&&!!Fn[ou.call(n)]}function ke(n,t){return n<t}function Oe(n){var t=n?Vu(n):0;return Lr(t)?t?Nn.support.unindexedChars&&Ae(n)?n.split(""):qn(n):[]:Se(n)}function Ie(n){return ot(n,Ee(n))}function Re(n){return dt(n,Ee(n))}function Ee(n){if(null==n)return[];de(n)||(n=Ye(n));for(var t=n.length,r=Nn.support,t=t&&Lr(t)&&(Wo(n)||_e(n)||Ae(n))&&t||0,e=n.constructor,u=-1,e=ye(e)&&e.prototype||nu,o=e===n,i=De(t),f=0<t,a=r.enumErrorProps&&(n===Qe||n instanceof qe),c=r.enumPrototypes&&ye(n);++u<t;)i[u]=u+"";
+for(var l in n)c&&"prototype"==l||a&&("message"==l||"name"==l)||f&&Ur(l,t)||"constructor"==l&&(o||!eu.call(n,l))||i.push(l);if(r.nonEnumShadows&&n!==nu)for(t=n===tu?G:n===Qe?q:ou.call(n),r=Nu[t]||Nu[Z],t==Z&&(e=nu),t=Wn.length;t--;)l=Wn[t],u=r[l],o&&u||(u?!eu.call(n,l):n[l]===e[l])||i.push(l);return i}function Ce(n){n=Dr(n);for(var t=-1,r=Ko(n),e=r.length,u=De(e);++t<e;){var o=r[t];u[t]=[o,n[o]]}return u}function Se(n){return Nt(n,Ko(n))}function Ue(n){return(n=u(n))&&n.replace(En,a).replace(bn,"");
+}function $e(n,t){var r="";if(n=u(n),t=+t,1>t||!n||!bu(t))return r;do t%2&&(r+=n),t=wu(t/2),n+=n;while(t);return r}function We(n,t,r){var e=n;return(n=u(n))?(r?$r(e,t,r):null==t)?n.slice(g(n),y(n)+1):(t+="",n.slice(o(n,t),i(n,t)+1)):n}function Fe(n,t,r){return r&&$r(n,t,r)&&(t=w),n=u(n),n.match(t||Un)||[]}function Le(n,t,r){return r&&$r(n,t,r)&&(t=w),h(n)?Te(n):it(n,t)}function Ne(n){return n}function Te(n){return At(ft(n,true))}function Pe(n,t,r){if(null==r){var e=de(t),u=e?Ko(t):w;((u=u&&u.length?dt(t,u):w)?u.length:e)||(u=false,
+r=t,t=n,n=this)}u||(u=dt(t,Ko(t)));var o=true,e=-1,i=ye(n),f=u.length;false===r?o=false:de(r)&&"chain"in r&&(o=r.chain);for(;++e<f;){r=u[e];var a=t[r];n[r]=a,i&&(n.prototype[r]=function(t){return function(){var r=this.__chain__;if(o||r){var e=n(this.__wrapped__);return(e.__actions__=qn(this.__actions__)).push({func:t,args:arguments,thisArg:n}),e.__chain__=r,e}return t.apply(n,Hn([this.value()],arguments))}}(a))}return n}function ze(){}function Be(n){return Wr(n)?Ot(n):It(n)}_=_?Jn.defaults(Yn.Object(),_,Jn.pick(Yn,$n)):Yn;
+var De=_.Array,Me=_.Date,qe=_.Error,Ke=_.Function,Ve=_.Math,Ze=_.Number,Ye=_.Object,Ge=_.RegExp,Je=_.String,Xe=_.TypeError,He=De.prototype,Qe=qe.prototype,nu=Ye.prototype,tu=Je.prototype,ru=Ke.prototype.toString,eu=nu.hasOwnProperty,uu=0,ou=nu.toString,iu=Yn._,fu=Ge("^"+ru.call(eu).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),au=_.ArrayBuffer,cu=_.clearTimeout,lu=_.parseFloat,su=Ve.pow,pu=nu.propertyIsEnumerable,hu=Or(_,"Set"),_u=_.setTimeout,vu=He.splice,gu=_.Uint8Array,yu=Or(_,"WeakMap"),du=Ve.ceil,mu=Or(Ye,"create"),wu=Ve.floor,xu=Or(De,"isArray"),bu=_.isFinite,Au=Or(Ye,"keys"),ju=Ve.max,ku=Ve.min,Ou=Or(Me,"now"),Iu=_.parseInt,Ru=Ve.random,Eu=Ze.NEGATIVE_INFINITY,Cu=Ze.POSITIVE_INFINITY,Su=4294967294,Uu=2147483647,$u=9007199254740991,Wu=yu&&new yu,Fu={},Lu={};
+Lu[X]=_.Float32Array,Lu[H]=_.Float64Array,Lu[Q]=_.Int8Array,Lu[nn]=_.Int16Array,Lu[tn]=_.Int32Array,Lu[rn]=gu,Lu[en]=_.Uint8ClampedArray,Lu[un]=_.Uint16Array,Lu[on]=_.Uint32Array;var Nu={};Nu[B]=Nu[M]=Nu[V]={constructor:true,toLocaleString:true,toString:true,valueOf:true},Nu[D]=Nu[G]={constructor:true,toString:true,valueOf:true},Nu[q]=Nu[K]=Nu[Y]={constructor:true,toString:true},Nu[Z]={constructor:true},Kn(Wn,function(n){for(var t in Nu)if(eu.call(Nu,t)){var r=Nu[t];r[n]=eu.call(r,n)}});var Tu=Nn.support={};!function(n){
+var t=function(){this.x=n},r={0:n,length:n},e=[];t.prototype={valueOf:n,y:n};for(var u in new t)e.push(u);Tu.enumErrorProps=pu.call(Qe,"message")||pu.call(Qe,"name"),Tu.enumPrototypes=pu.call(t,"prototype"),Tu.nonEnumShadows=!/valueOf/.test(e),Tu.ownLast="x"!=e[0],Tu.spliceObjects=(vu.call(r,0,1),!r[0]),Tu.unindexedChars="xx"!="x"[0]+Ye("x")[0]}(1,0),Nn.templateSettings={escape:_n,evaluate:vn,interpolate:gn,variable:"",imports:{_:Nn}};var Pu=function(){function n(){}return function(t){if(de(t)){n.prototype=t;
+var r=new n;n.prototype=w}return r||{}}}(),zu=Yt(gt),Bu=Yt(yt,true),Du=Gt(),Mu=Gt(true),qu=Wu?function(n,t){return Wu.set(n,t),n}:Ne,Ku=Wu?function(n){return Wu.get(n)}:ze,Vu=Ot("length"),Zu=function(){var n=0,t=0;return function(r,e){var u=wo(),o=W-(u-t);if(t=u,0<o){if(++n>=$)return r}else n=0;return qu(r,e)}}(),Yu=pe(function(n,t){return h(n)&&Sr(n)?ct(n,_t(t,false,true)):[]}),Gu=er(),Ju=er(true),Xu=pe(function(n){for(var t=n.length,e=t,u=De(l),o=jr(),i=o===r,f=[];e--;){var a=n[e]=Sr(a=n[e])?a:[];u[e]=i&&120<=a.length&&mu&&hu?new Dn(e&&a):null;
+}var i=n[0],c=-1,l=i?i.length:0,s=u[0];n:for(;++c<l;)if(a=i[c],0>(s?Mn(s,a):o(f,a,0))){for(e=t;--e;){var p=u[e];if(0>(p?Mn(p,a):o(n[e],a,0)))continue n}s&&s.push(a),f.push(a)}return f}),Hu=pe(function(t,r){r=_t(r);var e=ut(t,r);return Rt(t,r.sort(n)),e}),Qu=yr(),no=yr(true),to=pe(function(n){return Lt(_t(n,false,true))}),ro=pe(function(n,t){return Sr(n)?ct(n,t):[]}),eo=pe(Hr),uo=pe(function(n){var t=n.length,r=2<t?n[t-2]:w,e=1<t?n[t-1]:w;return 2<t&&typeof r=="function"?t-=2:(r=1<t&&typeof e=="function"?(--t,
+e):w,e=w),n.length=t,Qr(n,r,e)}),oo=pe(function(n){return n=_t(n),this.thru(function(t){t=Wo(t)?t:[Dr(t)];for(var r=n,e=-1,u=t.length,o=-1,i=r.length,f=De(u+i);++e<u;)f[e]=t[e];for(;++o<i;)f[e++]=r[o];return f})}),io=pe(function(n,t){return Sr(n)&&(n=Br(n)),ut(n,_t(t))}),fo=Vt(function(n,t,r){eu.call(n,r)?++n[r]:n[r]=1}),ao=rr(zu),co=rr(Bu,true),lo=ir(Kn,zu),so=ir(function(n,t){for(var r=n.length;r--&&false!==t(n[r],r,n););return n},Bu),po=Vt(function(n,t,r){eu.call(n,r)?n[r].push(t):n[r]=[t]}),ho=Vt(function(n,t,r){
+n[r]=t}),_o=pe(function(n,t,r){var e=-1,u=typeof t=="function",o=Wr(t),i=Sr(n)?De(n.length):[];return zu(n,function(n){var f=u?t:o&&null!=n?n[t]:w;i[++e]=f?f.apply(n,r):Cr(n,t,r)}),i}),vo=Vt(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),go=pr(Qn,zu),yo=pr(function(n,t,r,e){var u=n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r},Bu),mo=pe(function(n,t){if(null==n)return[];var r=t[2];return r&&$r(t[0],t[1],r)&&(t.length=1),Wt(n,_t(t),[])}),wo=Ou||function(){return(new Me).getTime();
+},xo=pe(function(n,t,r){var e=b;if(r.length)var u=v(r,xo.placeholder),e=e|I;return dr(n,e,t,r,u)}),bo=pe(function(n,t){t=t.length?_t(t):Re(n);for(var r=-1,e=t.length;++r<e;){var u=t[r];n[u]=dr(n[u],b,n)}return n}),Ao=pe(function(n,t,r){var e=b|A;if(r.length)var u=v(r,Ao.placeholder),e=e|I;return dr(t,e,n,r,u)}),jo=Qt(k),ko=Qt(O),Oo=pe(function(n,t){return at(n,1,t)}),Io=pe(function(n,t,r){return at(n,t,r)}),Ro=or(),Eo=or(true),Co=pe(function(n,t){if(t=_t(t),typeof n!="function"||!Vn(t,e))throw new Xe(T);
+var r=t.length;return pe(function(e){for(var u=ku(e.length,r);u--;)e[u]=t[u](e[u]);return n.apply(this,e)})}),So=sr(I),Uo=sr(R),$o=pe(function(n,t){return dr(n,C,w,w,w,_t(t))}),Wo=xu||function(n){return h(n)&&Lr(n.length)&&ou.call(n)==B},Fo=Zt(kt),Lo=Zt(function(n,t,r){return r?rt(n,t,r):et(n,t)}),No=nr(Lo,function(n,t){return n===w?t:n}),To=nr(Fo,Nr),Po=ur(gt),zo=ur(yt),Bo=fr(Du),Do=fr(Mu),Mo=ar(gt),qo=ar(yt),Ko=Au?function(n){var t=null==n?w:n.constructor;return typeof t=="function"&&t.prototype===n||(typeof n=="function"?Nn.support.enumPrototypes:Sr(n))?zr(n):de(n)?Au(n):[];
+}:zr,Vo=cr(true),Zo=cr(),Yo=pe(function(n,t){if(null==n)return{};if("function"!=typeof t[0])return t=Xn(_t(t),Je),Tr(n,ct(Ee(n),t));var r=Dt(t[0],t[1],3);return Pr(n,function(n,t,e){return!r(n,t,e)})}),Go=pe(function(n,t){return null==n?{}:"function"==typeof t[0]?Pr(n,Dt(t[0],t[1],3)):Tr(n,_t(t))}),Jo=Xt(function(n,t,r){return t=t.toLowerCase(),n+(r?t.charAt(0).toUpperCase()+t.slice(1):t)}),Xo=Xt(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Ho=lr(),Qo=lr(true),ni=Xt(function(n,t,r){return n+(r?"_":"")+t.toLowerCase();
+}),ti=Xt(function(n,t,r){return n+(r?" ":"")+(t.charAt(0).toUpperCase()+t.slice(1))}),ri=pe(function(n,t){try{return n.apply(w,t)}catch(r){return ge(r)?r:new qe(r)}}),ei=pe(function(n,t){return function(r){return Cr(r,n,t)}}),ui=pe(function(n,t){return function(r){return Cr(n,r,t)}}),oi=gr("ceil"),ii=gr("floor"),fi=tr(he,Eu),ai=tr(ke,Cu),ci=gr("round");return Nn.prototype=Tn.prototype,Pn.prototype=Pu(Tn.prototype),Pn.prototype.constructor=Pn,zn.prototype=Pu(Tn.prototype),zn.prototype.constructor=zn,
+Bn.prototype["delete"]=function(n){return this.has(n)&&delete this.__data__[n]},Bn.prototype.get=function(n){return"__proto__"==n?w:this.__data__[n]},Bn.prototype.has=function(n){return"__proto__"!=n&&eu.call(this.__data__,n)},Bn.prototype.set=function(n,t){return"__proto__"!=n&&(this.__data__[n]=t),this},Dn.prototype.push=function(n){var t=this.data;typeof n=="string"||de(n)?t.set.add(n):t.hash[n]=true},se.Cache=Bn,Nn.after=function(n,t){if(typeof t!="function"){if(typeof n!="function")throw new Xe(T);
+var r=n;n=t,t=r}return n=bu(n=+n)?n:0,function(){return 1>--n?t.apply(this,arguments):void 0}},Nn.ary=function(n,t,r){return r&&$r(n,t,r)&&(t=w),t=n&&null==t?n.length:ju(+t||0,0),dr(n,E,w,w,w,w,t)},Nn.assign=Lo,Nn.at=io,Nn.before=ce,Nn.bind=xo,Nn.bindAll=bo,Nn.bindKey=Ao,Nn.callback=Le,Nn.chain=te,Nn.chunk=function(n,t,r){t=(r?$r(n,t,r):null==t)?1:ju(wu(t)||1,1),r=0;for(var e=n?n.length:0,u=-1,o=De(du(e/t));r<e;)o[++u]=St(n,r,r+=t);return o},Nn.compact=function(n){for(var t=-1,r=n?n.length:0,e=-1,u=[];++t<r;){
+var o=n[t];o&&(u[++e]=o)}return u},Nn.constant=function(n){return function(){return n}},Nn.countBy=fo,Nn.create=function(n,t,r){var e=Pu(n);return r&&$r(n,t,r)&&(t=w),t?et(e,t):e},Nn.curry=jo,Nn.curryRight=ko,Nn.debounce=le,Nn.defaults=No,Nn.defaultsDeep=To,Nn.defer=Oo,Nn.delay=Io,Nn.difference=Yu,Nn.drop=Kr,Nn.dropRight=Vr,Nn.dropRightWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),true,true):[]},Nn.dropWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),true):[]},Nn.fill=function(n,t,r,e){
+var u=n?n.length:0;if(!u)return[];for(r&&typeof r!="number"&&$r(n,t,r)&&(r=0,e=u),u=n.length,r=null==r?0:+r||0,0>r&&(r=-r>u?0:u+r),e=e===w||e>u?u:+e||0,0>e&&(e+=u),u=r>e?0:e>>>0,r>>>=0;r<u;)n[r++]=t;return n},Nn.filter=ue,Nn.flatten=function(n,t,r){var e=n?n.length:0;return r&&$r(n,t,r)&&(t=false),e?_t(n,t):[]},Nn.flattenDeep=function(n){return n&&n.length?_t(n,true):[]},Nn.flow=Ro,Nn.flowRight=Eo,Nn.forEach=lo,Nn.forEachRight=so,Nn.forIn=Bo,Nn.forInRight=Do,Nn.forOwn=Mo,Nn.forOwnRight=qo,Nn.functions=Re,
+Nn.groupBy=po,Nn.indexBy=ho,Nn.initial=function(n){return Vr(n,1)},Nn.intersection=Xu,Nn.invert=function(n,t,r){r&&$r(n,t,r)&&(t=w),r=-1;for(var e=Ko(n),u=e.length,o={};++r<u;){var i=e[r],f=n[i];t?eu.call(o,f)?o[f].push(i):o[f]=[i]:o[f]=i}return o},Nn.invoke=_o,Nn.keys=Ko,Nn.keysIn=Ee,Nn.map=ie,Nn.mapKeys=Vo,Nn.mapValues=Zo,Nn.matches=Te,Nn.matchesProperty=function(n,t){return jt(n,ft(t,true))},Nn.memoize=se,Nn.merge=Fo,Nn.method=ei,Nn.methodOf=ui,Nn.mixin=Pe,Nn.modArgs=Co,Nn.negate=function(n){if(typeof n!="function")throw new Xe(T);
+return function(){return!n.apply(this,arguments)}},Nn.omit=Yo,Nn.once=function(n){return ce(2,n)},Nn.pairs=Ce,Nn.partial=So,Nn.partialRight=Uo,Nn.partition=vo,Nn.pick=Go,Nn.pluck=function(n,t){return ie(n,Be(t))},Nn.property=Be,Nn.propertyOf=function(n){return function(t){return mt(n,Mr(t),t+"")}},Nn.pull=function(){var n=arguments,t=n[0];if(!t||!t.length)return t;for(var r=0,e=jr(),u=n.length;++r<u;)for(var o=0,i=n[r];-1<(o=e(t,i,o));)vu.call(t,o,1);return t},Nn.pullAt=Hu,Nn.range=function(n,t,r){
+r&&$r(n,t,r)&&(t=r=w),n=+n||0,r=null==r?1:+r||0,null==t?(t=n,n=0):t=+t||0;var e=-1;t=ju(du((t-n)/(r||1)),0);for(var u=De(t);++e<t;)u[e]=n,n+=r;return u},Nn.rearg=$o,Nn.reject=function(n,t,r){var e=Wo(n)?Zn:pt;return t=br(t,r,3),e(n,function(n,r,e){return!t(n,r,e)})},Nn.remove=function(n,t,r){var e=[];if(!n||!n.length)return e;var u=-1,o=[],i=n.length;for(t=br(t,r,3);++u<i;)r=n[u],t(r,u,n)&&(e.push(r),o.push(u));return Rt(n,o),e},Nn.rest=Jr,Nn.restParam=pe,Nn.set=function(n,t,r){if(null==n)return n;
+var e=t+"";t=null!=n[e]||Wr(t,n)?[e]:Mr(t);for(var e=-1,u=t.length,o=u-1,i=n;null!=i&&++e<u;){var f=t[e];de(i)&&(e==o?i[f]=r:null==i[f]&&(i[f]=Ur(t[e+1])?[]:{})),i=i[f]}return n},Nn.shuffle=function(n){return fe(n,Cu)},Nn.slice=function(n,t,r){var e=n?n.length:0;return e?(r&&typeof r!="number"&&$r(n,t,r)&&(t=0,r=e),St(n,t,r)):[]},Nn.sortBy=function(n,t,r){if(null==n)return[];r&&$r(n,t,r)&&(t=w);var e=-1;return t=br(t,r,3),n=bt(n,function(n,r,u){return{a:t(n,r,u),b:++e,c:n}}),$t(n,f)},Nn.sortByAll=mo,
+Nn.sortByOrder=function(n,t,r,e){return null==n?[]:(e&&$r(t,r,e)&&(r=w),Wo(t)||(t=null==t?[]:[t]),Wo(r)||(r=null==r?[]:[r]),Wt(n,t,r))},Nn.spread=function(n){if(typeof n!="function")throw new Xe(T);return function(t){return n.apply(this,t)}},Nn.take=function(n,t,r){return n&&n.length?((r?$r(n,t,r):null==t)&&(t=1),St(n,0,0>t?0:t)):[]},Nn.takeRight=function(n,t,r){var e=n?n.length:0;return e?((r?$r(n,t,r):null==t)&&(t=1),t=e-(+t||0),St(n,0>t?0:t)):[]},Nn.takeRightWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3),false,true):[];
+},Nn.takeWhile=function(n,t,r){return n&&n.length?Tt(n,br(t,r,3)):[]},Nn.tap=function(n,t,r){return t.call(r,n),n},Nn.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new Xe(T);return false===r?e=false:de(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),le(n,t,{leading:e,maxWait:+t,trailing:u})},Nn.thru=re,Nn.times=function(n,t,r){if(n=wu(n),1>n||!bu(n))return[];var e=-1,u=De(ku(n,4294967295));for(t=Dt(t,r,1);++e<n;)4294967295>e?u[e]=t(e):t(e);return u},Nn.toArray=Oe,
+Nn.toPlainObject=Ie,Nn.transform=function(n,t,r,e){var u=Wo(n)||je(n);return t=br(t,e,4),null==r&&(u||de(n)?(e=n.constructor,r=u?Wo(n)?new e:[]:Pu(ye(e)?e.prototype:w)):r={}),(u?Kn:gt)(n,function(n,e,u){return t(r,n,e,u)}),r},Nn.union=to,Nn.uniq=Xr,Nn.unzip=Hr,Nn.unzipWith=Qr,Nn.values=Se,Nn.valuesIn=function(n){return Nt(n,Ee(n))},Nn.where=function(n,t){return ue(n,At(t))},Nn.without=ro,Nn.wrap=function(n,t){return t=null==t?Ne:t,dr(t,I,w,[n],[])},Nn.xor=function(){for(var n=-1,t=arguments.length;++n<t;){
+var r=arguments[n];if(Sr(r))var e=e?Hn(ct(e,r),ct(r,e)):r}return e?Lt(e):[]},Nn.zip=eo,Nn.zipObject=ne,Nn.zipWith=uo,Nn.backflow=Eo,Nn.collect=ie,Nn.compose=Eo,Nn.each=lo,Nn.eachRight=so,Nn.extend=Lo,Nn.iteratee=Le,Nn.methods=Re,Nn.object=ne,Nn.select=ue,Nn.tail=Jr,Nn.unique=Xr,Pe(Nn,Nn),Nn.add=function(n,t){return(+n||0)+(+t||0)},Nn.attempt=ri,Nn.camelCase=Jo,Nn.capitalize=function(n){return(n=u(n))&&n.charAt(0).toUpperCase()+n.slice(1)},Nn.ceil=oi,Nn.clone=function(n,t,r,e){return t&&typeof t!="boolean"&&$r(n,t,r)?t=false:typeof t=="function"&&(e=r,
+r=t,t=false),typeof r=="function"?ft(n,t,Dt(r,e,3)):ft(n,t)},Nn.cloneDeep=function(n,t,r){return typeof t=="function"?ft(n,true,Dt(t,r,3)):ft(n,true)},Nn.deburr=Ue,Nn.endsWith=function(n,t,r){n=u(n),t+="";var e=n.length;return r=r===w?e:ku(0>r?0:+r||0,e),r-=t.length,0<=r&&n.indexOf(t,r)==r},Nn.escape=function(n){return(n=u(n))&&hn.test(n)?n.replace(sn,c):n},Nn.escapeRegExp=function(n){return(n=u(n))&&xn.test(n)?n.replace(wn,l):n||"(?:)"},Nn.every=ee,Nn.find=ao,Nn.findIndex=Gu,Nn.findKey=Po,Nn.findLast=co,
+Nn.findLastIndex=Ju,Nn.findLastKey=zo,Nn.findWhere=function(n,t){return ao(n,At(t))},Nn.first=Zr,Nn.floor=ii,Nn.get=function(n,t,r){return n=null==n?w:mt(n,Mr(t),t+""),n===w?r:n},Nn.gt=he,Nn.gte=function(n,t){return n>=t},Nn.has=function(n,t){if(null==n)return false;var r=eu.call(n,t);if(!r&&!Wr(t)){if(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),null==n)return false;t=Gr(t),r=eu.call(n,t)}return r||Lr(n.length)&&Ur(t,n.length)&&(Wo(n)||_e(n)||Ae(n))},Nn.identity=Ne,Nn.includes=oe,Nn.indexOf=Yr,Nn.inRange=function(n,t,r){
+return t=+t||0,r===w?(r=t,t=0):r=+r||0,n>=ku(t,r)&&n<ju(t,r)},Nn.isArguments=_e,Nn.isArray=Wo,Nn.isBoolean=function(n){return true===n||false===n||h(n)&&ou.call(n)==D},Nn.isDate=function(n){return h(n)&&ou.call(n)==M},Nn.isElement=function(n){return!!n&&1===n.nodeType&&h(n)&&!xe(n)},Nn.isEmpty=function(n){return null==n?true:Sr(n)&&(Wo(n)||Ae(n)||_e(n)||h(n)&&ye(n.splice))?!n.length:!Ko(n).length},Nn.isEqual=ve,Nn.isError=ge,Nn.isFinite=function(n){return typeof n=="number"&&bu(n)},Nn.isFunction=ye,Nn.isMatch=function(n,t,r,e){
+return r=typeof r=="function"?Dt(r,e,3):w,xt(n,kr(t),r)},Nn.isNaN=function(n){return we(n)&&n!=+n},Nn.isNative=me,Nn.isNull=function(n){return null===n},Nn.isNumber=we,Nn.isObject=de,Nn.isPlainObject=xe,Nn.isRegExp=be,Nn.isString=Ae,Nn.isTypedArray=je,Nn.isUndefined=function(n){return n===w},Nn.kebabCase=Xo,Nn.last=Gr,Nn.lastIndexOf=function(n,t,r){var e=n?n.length:0;if(!e)return-1;var u=e;if(typeof r=="number")u=(0>r?ju(e+r,0):ku(r||0,e-1))+1;else if(r)return u=zt(n,t,true)-1,n=n[u],(t===t?t===n:n!==n)?u:-1;
+if(t!==t)return p(n,u,true);for(;u--;)if(n[u]===t)return u;return-1},Nn.lt=ke,Nn.lte=function(n,t){return n<=t},Nn.max=fi,Nn.min=ai,Nn.noConflict=function(){return Yn._=iu,this},Nn.noop=ze,Nn.now=wo,Nn.pad=function(n,t,r){n=u(n),t=+t;var e=n.length;return e<t&&bu(t)?(e=(t-e)/2,t=wu(e),e=du(e),r=_r("",e,r),r.slice(0,t)+n+r):n},Nn.padLeft=Ho,Nn.padRight=Qo,Nn.parseInt=function(n,t,r){return(r?$r(n,t,r):null==t)?t=0:t&&(t=+t),n=We(n),Iu(n,t||(On.test(n)?16:10))},Nn.random=function(n,t,r){r&&$r(n,t,r)&&(t=r=w);
+var e=null==n,u=null==t;return null==r&&(u&&typeof n=="boolean"?(r=n,n=1):typeof t=="boolean"&&(r=t,u=true)),e&&u&&(t=1,u=false),n=+n||0,u?(t=n,n=0):t=+t||0,r||n%1||t%1?(r=Ru(),ku(n+r*(t-n+lu("1e-"+((r+"").length-1))),t)):Et(n,t)},Nn.reduce=go,Nn.reduceRight=yo,Nn.repeat=$e,Nn.result=function(n,t,r){var e=null==n?w:Dr(n)[t];return e===w&&(null==n||Wr(t,n)||(t=Mr(t),n=1==t.length?n:mt(n,St(t,0,-1)),e=null==n?w:Dr(n)[Gr(t)]),e=e===w?r:e),ye(e)?e.call(n):e},Nn.round=ci,Nn.runInContext=m,Nn.size=function(n){
+var t=n?Vu(n):0;return Lr(t)?t:Ko(n).length},Nn.snakeCase=ni,Nn.some=ae,Nn.sortedIndex=Qu,Nn.sortedLastIndex=no,Nn.startCase=ti,Nn.startsWith=function(n,t,r){return n=u(n),r=null==r?0:ku(0>r?0:+r||0,n.length),n.lastIndexOf(t,r)==r},Nn.sum=function(n,t,r){if(r&&$r(n,t,r)&&(t=w),t=br(t,r,3),1==t.length){n=Wo(n)?n:Br(n),r=n.length;for(var e=0;r--;)e+=+t(n[r])||0;n=e}else n=Ft(n,t);return n},Nn.template=function(n,t,r){var e=Nn.templateSettings;r&&$r(n,t,r)&&(t=r=w),n=u(n),t=rt(et({},r||t),e,tt),r=rt(et({},t.imports),e.imports,tt);
+var o,i,f=Ko(r),a=Nt(r,f),c=0;r=t.interpolate||Cn;var l="__p+='";r=Ge((t.escape||Cn).source+"|"+r.source+"|"+(r===gn?jn:Cn).source+"|"+(t.evaluate||Cn).source+"|$","g");var p="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";if(n.replace(r,function(t,r,e,u,f,a){return e||(e=u),l+=n.slice(c,a).replace(Sn,s),r&&(o=true,l+="'+__e("+r+")+'"),f&&(i=true,l+="';"+f+";\n__p+='"),e&&(l+="'+((__t=("+e+"))==null?'':__t)+'"),c=a+t.length,t}),l+="';",(t=t.variable)||(l="with(obj){"+l+"}"),l=(i?l.replace(fn,""):l).replace(an,"$1").replace(cn,"$1;"),
+l="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(o?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}",t=ri(function(){return Ke(f,p+"return "+l).apply(w,a)}),t.source=l,ge(t))throw t;return t},Nn.trim=We,Nn.trimLeft=function(n,t,r){var e=n;return(n=u(n))?n.slice((r?$r(e,t,r):null==t)?g(n):o(n,t+"")):n},Nn.trimRight=function(n,t,r){var e=n;return(n=u(n))?(r?$r(e,t,r):null==t)?n.slice(0,y(n)+1):n.slice(0,i(n,t+"")+1):n;
+},Nn.trunc=function(n,t,r){r&&$r(n,t,r)&&(t=w);var e=S;if(r=U,null!=t)if(de(t)){var o="separator"in t?t.separator:o,e="length"in t?+t.length||0:e;r="omission"in t?u(t.omission):r}else e=+t||0;if(n=u(n),e>=n.length)return n;if(e-=r.length,1>e)return r;if(t=n.slice(0,e),null==o)return t+r;if(be(o)){if(n.slice(e).search(o)){var i,f=n.slice(0,e);for(o.global||(o=Ge(o.source,(kn.exec(o)||"")+"g")),o.lastIndex=0;n=o.exec(f);)i=n.index;t=t.slice(0,null==i?e:i)}}else n.indexOf(o,e)!=e&&(o=t.lastIndexOf(o),
+-1<o&&(t=t.slice(0,o)));return t+r},Nn.unescape=function(n){return(n=u(n))&&pn.test(n)?n.replace(ln,d):n},Nn.uniqueId=function(n){var t=++uu;return u(n)+t},Nn.words=Fe,Nn.all=ee,Nn.any=ae,Nn.contains=oe,Nn.eq=ve,Nn.detect=ao,Nn.foldl=go,Nn.foldr=yo,Nn.head=Zr,Nn.include=oe,Nn.inject=go,Pe(Nn,function(){var n={};return gt(Nn,function(t,r){Nn.prototype[r]||(n[r]=t)}),n}(),false),Nn.sample=fe,Nn.prototype.sample=function(n){return this.__chain__||null!=n?this.thru(function(t){return fe(t,n)}):fe(this.value());
+},Nn.VERSION=x,Kn("bind bindKey curry curryRight partial partialRight".split(" "),function(n){Nn[n].placeholder=Nn}),Kn(["drop","take"],function(n,t){zn.prototype[n]=function(r){var e=this.__filtered__;if(e&&!t)return new zn(this);r=null==r?1:ju(wu(r)||0,0);var u=this.clone();return e?u.__takeCount__=ku(u.__takeCount__,r):u.__views__.push({size:r,type:n+(0>u.__dir__?"Right":"")}),u},zn.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),Kn(["filter","map","takeWhile"],function(n,t){
+var r=t+1,e=r!=N;zn.prototype[n]=function(n,t){var u=this.clone();return u.__iteratees__.push({iteratee:br(n,t,1),type:r}),u.__filtered__=u.__filtered__||e,u}}),Kn(["first","last"],function(n,t){var r="take"+(t?"Right":"");zn.prototype[n]=function(){return this[r](1).value()[0]}}),Kn(["initial","rest"],function(n,t){var r="drop"+(t?"":"Right");zn.prototype[n]=function(){return this.__filtered__?new zn(this):this[r](1)}}),Kn(["pluck","where"],function(n,t){var r=t?"filter":"map",e=t?At:Be;zn.prototype[n]=function(n){
+return this[r](e(n))}}),zn.prototype.compact=function(){return this.filter(Ne)},zn.prototype.reject=function(n,t){return n=br(n,t,1),this.filter(function(t){return!n(t)})},zn.prototype.slice=function(n,t){n=null==n?0:+n||0;var r=this;return r.__filtered__&&(0<n||0>t)?new zn(r):(0>n?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==w&&(t=+t||0,r=0>t?r.dropRight(-t):r.take(t-n)),r)},zn.prototype.takeRightWhile=function(n,t){return this.reverse().takeWhile(n,t).reverse()},zn.prototype.toArray=function(){return this.take(Cu);
+},gt(zn.prototype,function(n,t){var r=/^(?:filter|map|reject)|While$/.test(t),e=/^(?:first|last)$/.test(t),u=Nn[e?"take"+("last"==t?"Right":""):t];u&&(Nn.prototype[t]=function(){var t=e?[1]:arguments,o=this.__chain__,i=this.__wrapped__,f=!!this.__actions__.length,a=i instanceof zn,c=t[0],l=a||Wo(i);l&&r&&typeof c=="function"&&1!=c.length&&(a=l=false);var s=function(n){return e&&o?u(n,1)[0]:u.apply(w,Hn([n],t))},c={func:re,args:[s],thisArg:w},f=a&&!f;return e&&!o?f?(i=i.clone(),i.__actions__.push(c),
+n.call(i)):u.call(w,this.value())[0]:!e&&l?(i=f?i:new zn(this),i=n.apply(i,t),i.__actions__.push(c),new Pn(i,o)):this.thru(s)})}),Kn("join pop push replace shift sort splice split unshift".split(" "),function(n){var t=(/^(?:replace|split)$/.test(n)?tu:He)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=!Tu.spliceObjects&&/^(?:pop|shift|splice)$/.test(n),u=/^(?:join|pop|replace|shift)$/.test(n),o=e?function(){var n=t.apply(this,arguments);return 0===this.length&&delete this[0],n}:t;Nn.prototype[n]=function(){
+var n=arguments;return u&&!this.__chain__?o.apply(this.value(),n):this[r](function(t){return o.apply(t,n)})}}),gt(zn.prototype,function(n,t){var r=Nn[t];if(r){var e=r.name+"";(Fu[e]||(Fu[e]=[])).push({name:t,func:r})}}),Fu[hr(w,A).name]=[{name:"wrapper",func:w}],zn.prototype.clone=function(){var n=new zn(this.__wrapped__);return n.__actions__=qn(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=qn(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=qn(this.__views__),
+n},zn.prototype.reverse=function(){if(this.__filtered__){var n=new zn(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},zn.prototype.value=function(){var n,t=this.__wrapped__.value(),r=this.__dir__,e=Wo(t),u=0>r,o=e?t.length:0;n=0;for(var i=o,f=this.__views__,a=-1,c=f.length;++a<c;){var l=f[a],s=l.size;switch(l.type){case"drop":n+=s;break;case"dropRight":i-=s;break;case"take":i=ku(i,n+s);break;case"takeRight":n=ju(n,i-s)}}if(n={start:n,end:i},i=n.start,f=n.end,n=f-i,
+u=u?f:i-1,i=this.__iteratees__,f=i.length,a=0,c=ku(n,this.__takeCount__),!e||o<F||o==n&&c==n)return Pt(t,this.__actions__);e=[];n:for(;n--&&a<c;){for(u+=r,o=-1,l=t[u];++o<f;){var p=i[o],s=p.type,p=p.iteratee(l);if(s==N)l=p;else if(!p){if(s==L)continue n;break n}}e[a++]=l}return e},Nn.prototype.chain=function(){return te(this)},Nn.prototype.commit=function(){return new Pn(this.value(),this.__chain__)},Nn.prototype.concat=oo,Nn.prototype.plant=function(n){for(var t,r=this;r instanceof Tn;){var e=qr(r);
+t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},Nn.prototype.reverse=function(){var n=this.__wrapped__,t=function(n){return n.reverse()};return n instanceof zn?(this.__actions__.length&&(n=new zn(this)),n=n.reverse(),n.__actions__.push({func:re,args:[t],thisArg:w}),new Pn(n,this.__chain__)):this.thru(t)},Nn.prototype.toString=function(){return this.value()+""},Nn.prototype.run=Nn.prototype.toJSON=Nn.prototype.valueOf=Nn.prototype.value=function(){return Pt(this.__wrapped__,this.__actions__);
+},Nn.prototype.collect=Nn.prototype.map,Nn.prototype.head=Nn.prototype.first,Nn.prototype.select=Nn.prototype.filter,Nn.prototype.tail=Nn.prototype.rest,Nn}var w,x="3.10.1",b=1,A=2,j=4,k=8,O=16,I=32,R=64,E=128,C=256,S=30,U="...",$=150,W=16,F=200,L=1,N=2,T="Expected a function",P="__lodash_placeholder__",z="[object Arguments]",B="[object Array]",D="[object Boolean]",M="[object Date]",q="[object Error]",K="[object Function]",V="[object Number]",Z="[object Object]",Y="[object RegExp]",G="[object String]",J="[object ArrayBuffer]",X="[object Float32Array]",H="[object Float64Array]",Q="[object Int8Array]",nn="[object Int16Array]",tn="[object Int32Array]",rn="[object Uint8Array]",en="[object Uint8ClampedArray]",un="[object Uint16Array]",on="[object Uint32Array]",fn=/\b__p\+='';/g,an=/\b(__p\+=)''\+/g,cn=/(__e\(.*?\)|\b__t\))\+'';/g,ln=/&(?:amp|lt|gt|quot|#39|#96);/g,sn=/[&<>"'`]/g,pn=RegExp(ln.source),hn=RegExp(sn.source),_n=/<%-([\s\S]+?)%>/g,vn=/<%([\s\S]+?)%>/g,gn=/<%=([\s\S]+?)%>/g,yn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,dn=/^\w*$/,mn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,wn=/^[:!,]|[\\^$.*+?()[\]{}|\/]|(^[0-9a-fA-Fnrtuvx])|([\n\r\u2028\u2029])/g,xn=RegExp(wn.source),bn=/[\u0300-\u036f\ufe20-\ufe23]/g,An=/\\(\\)?/g,jn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,kn=/\w*$/,On=/^0[xX]/,In=/^\[object .+?Constructor\]$/,Rn=/^\d+$/,En=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,Cn=/($^)/,Sn=/['\n\r\u2028\u2029\\]/g,Un=RegExp("[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?=[A-Z\\xc0-\\xd6\\xd8-\\xde][a-z\\xdf-\\xf6\\xf8-\\xff]+)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+|[A-Z\\xc0-\\xd6\\xd8-\\xde]+|[0-9]+","g"),$n="Array ArrayBuffer Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Math Number Object RegExp Set String _ clearTimeout isFinite parseFloat parseInt setTimeout TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap".split(" "),Wn="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),Fn={};
+Fn[X]=Fn[H]=Fn[Q]=Fn[nn]=Fn[tn]=Fn[rn]=Fn[en]=Fn[un]=Fn[on]=true,Fn[z]=Fn[B]=Fn[J]=Fn[D]=Fn[M]=Fn[q]=Fn[K]=Fn["[object Map]"]=Fn[V]=Fn[Z]=Fn[Y]=Fn["[object Set]"]=Fn[G]=Fn["[object WeakMap]"]=false;var Ln={};Ln[z]=Ln[B]=Ln[J]=Ln[D]=Ln[M]=Ln[X]=Ln[H]=Ln[Q]=Ln[nn]=Ln[tn]=Ln[V]=Ln[Z]=Ln[Y]=Ln[G]=Ln[rn]=Ln[en]=Ln[un]=Ln[on]=true,Ln[q]=Ln[K]=Ln["[object Map]"]=Ln["[object Set]"]=Ln["[object WeakMap]"]=false;var Nn={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a",
+"\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y",
+"\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss"},Tn={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","`":"&#96;"},Pn={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'","&#96;":"`"},zn={"function":true,object:true},Bn={0:"x30",1:"x31",2:"x32",3:"x33",4:"x34",5:"x35",6:"x36",7:"x37",8:"x38",9:"x39",A:"x41",B:"x42",C:"x43",D:"x44",E:"x45",F:"x46",a:"x61",b:"x62",c:"x63",d:"x64",e:"x65",f:"x66",n:"x6e",r:"x72",t:"x74",u:"x75",v:"x76",x:"x78"},Dn={"\\":"\\",
+"'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Mn=zn[typeof exports]&&exports&&!exports.nodeType&&exports,qn=zn[typeof module]&&module&&!module.nodeType&&module,Kn=zn[typeof self]&&self&&self.Object&&self,Vn=zn[typeof window]&&window&&window.Object&&window,Zn=qn&&qn.exports===Mn&&Mn,Yn=Mn&&qn&&typeof global=="object"&&global&&global.Object&&global||Vn!==(this&&this.window)&&Vn||Kn||this,Gn=function(){try{Object({toString:0}+"")}catch(n){return function(){return false}}return function(n){
+return typeof n.toString!="function"&&typeof(n+"")=="string"}}(),Jn=m();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(Yn._=Jn, define(function(){return Jn})):Mn&&qn?Zn?(qn.exports=Jn)._=Jn:Mn._=Jn:Yn._=Jn}).call(this);
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/marked.js b/profiles/killpay/src/main/webapp/lib/marked.js
new file mode 100644
index 0000000..c2a678d
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/marked.js
@@ -0,0 +1,1272 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+;(function() {
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+  newline: /^\n+/,
+  code: /^( {4}[^\n]+\n*)+/,
+  fences: noop,
+  hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+  heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+  nptable: noop,
+  lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+  blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+  list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+  html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+  def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+  table: noop,
+  paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+  text: /^[^\n]+/
+};
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = replace(block.item, 'gm')
+  (/bull/g, block.bullet)
+  ();
+
+block.list = replace(block.list)
+  (/bull/g, block.bullet)
+  ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+  ('def', '\\n+(?=' + block.def.source + ')')
+  ();
+
+block.blockquote = replace(block.blockquote)
+  ('def', block.def)
+  ();
+
+block._tag = '(?!(?:'
+  + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+  + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+  + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+
+block.html = replace(block.html)
+  ('comment', /<!--[\s\S]*?-->/)
+  ('closed', /<(tag)[\s\S]+?<\/\1>/)
+  ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
+  (/tag/g, block._tag)
+  ();
+
+block.paragraph = replace(block.paragraph)
+  ('hr', block.hr)
+  ('heading', block.heading)
+  ('lheading', block.lheading)
+  ('blockquote', block.blockquote)
+  ('tag', '<' + block._tag)
+  ('def', block.def)
+  ();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+  fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
+  paragraph: /^/
+});
+
+block.gfm.paragraph = replace(block.paragraph)
+  ('(?!', '(?!'
+    + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+    + block.list.source.replace('\\1', '\\3') + '|')
+  ();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+  nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+  table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+  this.tokens = [];
+  this.tokens.links = {};
+  this.options = options || marked.defaults;
+  this.rules = block.normal;
+
+  if (this.options.gfm) {
+    if (this.options.tables) {
+      this.rules = block.tables;
+    } else {
+      this.rules = block.gfm;
+    }
+  }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+  var lexer = new Lexer(options);
+  return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+  src = src
+    .replace(/\r\n|\r/g, '\n')
+    .replace(/\t/g, '    ')
+    .replace(/\u00a0/g, ' ')
+    .replace(/\u2424/g, '\n');
+
+  return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top, bq) {
+  var src = src.replace(/^ +$/gm, '')
+    , next
+    , loose
+    , cap
+    , bull
+    , b
+    , item
+    , space
+    , i
+    , l;
+
+  while (src) {
+    // newline
+    if (cap = this.rules.newline.exec(src)) {
+      src = src.substring(cap[0].length);
+      if (cap[0].length > 1) {
+        this.tokens.push({
+          type: 'space'
+        });
+      }
+    }
+
+    // code
+    if (cap = this.rules.code.exec(src)) {
+      src = src.substring(cap[0].length);
+      cap = cap[0].replace(/^ {4}/gm, '');
+      this.tokens.push({
+        type: 'code',
+        text: !this.options.pedantic
+          ? cap.replace(/\n+$/, '')
+          : cap
+      });
+      continue;
+    }
+
+    // fences (gfm)
+    if (cap = this.rules.fences.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'code',
+        lang: cap[2],
+        text: cap[3]
+      });
+      continue;
+    }
+
+    // heading
+    if (cap = this.rules.heading.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'heading',
+        depth: cap[1].length,
+        text: cap[2]
+      });
+      continue;
+    }
+
+    // table no leading pipe (gfm)
+    if (top && (cap = this.rules.nptable.exec(src))) {
+      src = src.substring(cap[0].length);
+
+      item = {
+        type: 'table',
+        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+        cells: cap[3].replace(/\n$/, '').split('\n')
+      };
+
+      for (i = 0; i < item.align.length; i++) {
+        if (/^ *-+: *$/.test(item.align[i])) {
+          item.align[i] = 'right';
+        } else if (/^ *:-+: *$/.test(item.align[i])) {
+          item.align[i] = 'center';
+        } else if (/^ *:-+ *$/.test(item.align[i])) {
+          item.align[i] = 'left';
+        } else {
+          item.align[i] = null;
+        }
+      }
+
+      for (i = 0; i < item.cells.length; i++) {
+        item.cells[i] = item.cells[i].split(/ *\| */);
+      }
+
+      this.tokens.push(item);
+
+      continue;
+    }
+
+    // lheading
+    if (cap = this.rules.lheading.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'heading',
+        depth: cap[2] === '=' ? 1 : 2,
+        text: cap[1]
+      });
+      continue;
+    }
+
+    // hr
+    if (cap = this.rules.hr.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'hr'
+      });
+      continue;
+    }
+
+    // blockquote
+    if (cap = this.rules.blockquote.exec(src)) {
+      src = src.substring(cap[0].length);
+
+      this.tokens.push({
+        type: 'blockquote_start'
+      });
+
+      cap = cap[0].replace(/^ *> ?/gm, '');
+
+      // Pass `top` to keep the current
+      // "toplevel" state. This is exactly
+      // how markdown.pl works.
+      this.token(cap, top, true);
+
+      this.tokens.push({
+        type: 'blockquote_end'
+      });
+
+      continue;
+    }
+
+    // list
+    if (cap = this.rules.list.exec(src)) {
+      src = src.substring(cap[0].length);
+      bull = cap[2];
+
+      this.tokens.push({
+        type: 'list_start',
+        ordered: bull.length > 1
+      });
+
+      // Get each top-level item.
+      cap = cap[0].match(this.rules.item);
+
+      next = false;
+      l = cap.length;
+      i = 0;
+
+      for (; i < l; i++) {
+        item = cap[i];
+
+        // Remove the list item's bullet
+        // so it is seen as the next token.
+        space = item.length;
+        item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+        // Outdent whatever the
+        // list item contains. Hacky.
+        if (~item.indexOf('\n ')) {
+          space -= item.length;
+          item = !this.options.pedantic
+            ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+            : item.replace(/^ {1,4}/gm, '');
+        }
+
+        // Determine whether the next list item belongs here.
+        // Backpedal if it does not belong in this list.
+        if (this.options.smartLists && i !== l - 1) {
+          b = block.bullet.exec(cap[i + 1])[0];
+          if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+            src = cap.slice(i + 1).join('\n') + src;
+            i = l - 1;
+          }
+        }
+
+        // Determine whether item is loose or not.
+        // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+        // for discount behavior.
+        loose = next || /\n\n(?!\s*$)/.test(item);
+        if (i !== l - 1) {
+          next = item.charAt(item.length - 1) === '\n';
+          if (!loose) loose = next;
+        }
+
+        this.tokens.push({
+          type: loose
+            ? 'loose_item_start'
+            : 'list_item_start'
+        });
+
+        // Recurse.
+        this.token(item, false, bq);
+
+        this.tokens.push({
+          type: 'list_item_end'
+        });
+      }
+
+      this.tokens.push({
+        type: 'list_end'
+      });
+
+      continue;
+    }
+
+    // html
+    if (cap = this.rules.html.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: this.options.sanitize
+          ? 'paragraph'
+          : 'html',
+        pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
+        text: cap[0]
+      });
+      continue;
+    }
+
+    // def
+    if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+      src = src.substring(cap[0].length);
+      this.tokens.links[cap[1].toLowerCase()] = {
+        href: cap[2],
+        title: cap[3]
+      };
+      continue;
+    }
+
+    // table (gfm)
+    if (top && (cap = this.rules.table.exec(src))) {
+      src = src.substring(cap[0].length);
+
+      item = {
+        type: 'table',
+        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+        cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+      };
+
+      for (i = 0; i < item.align.length; i++) {
+        if (/^ *-+: *$/.test(item.align[i])) {
+          item.align[i] = 'right';
+        } else if (/^ *:-+: *$/.test(item.align[i])) {
+          item.align[i] = 'center';
+        } else if (/^ *:-+ *$/.test(item.align[i])) {
+          item.align[i] = 'left';
+        } else {
+          item.align[i] = null;
+        }
+      }
+
+      for (i = 0; i < item.cells.length; i++) {
+        item.cells[i] = item.cells[i]
+          .replace(/^ *\| *| *\| *$/g, '')
+          .split(/ *\| */);
+      }
+
+      this.tokens.push(item);
+
+      continue;
+    }
+
+    // top-level paragraph
+    if (top && (cap = this.rules.paragraph.exec(src))) {
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'paragraph',
+        text: cap[1].charAt(cap[1].length - 1) === '\n'
+          ? cap[1].slice(0, -1)
+          : cap[1]
+      });
+      continue;
+    }
+
+    // text
+    if (cap = this.rules.text.exec(src)) {
+      // Top-level should never reach here.
+      src = src.substring(cap[0].length);
+      this.tokens.push({
+        type: 'text',
+        text: cap[0]
+      });
+      continue;
+    }
+
+    if (src) {
+      throw new
+        Error('Infinite loop on byte: ' + src.charCodeAt(0));
+    }
+  }
+
+  return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+  escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+  url: noop,
+  tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+  link: /^!?\[(inside)\]\(href\)/,
+  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+  strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+  em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+  code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+  br: /^ {2,}\n(?!\s*$)/,
+  del: noop,
+  text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
+};
+
+inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
+inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+inline.link = replace(inline.link)
+  ('inside', inline._inside)
+  ('href', inline._href)
+  ();
+
+inline.reflink = replace(inline.reflink)
+  ('inside', inline._inside)
+  ();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+  em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+  escape: replace(inline.escape)('])', '~|])')(),
+  url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+  del: /^~~(?=\S)([\s\S]*?\S)~~/,
+  text: replace(inline.text)
+    (']|', '~]|')
+    ('|', '|https?://|')
+    ()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+  br: replace(inline.br)('{2,}', '*')(),
+  text: replace(inline.gfm.text)('{2,}', '*')()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+  this.options = options || marked.defaults;
+  this.links = links;
+  this.rules = inline.normal;
+  this.renderer = this.options.renderer || new Renderer;
+  this.renderer.options = this.options;
+
+  if (!this.links) {
+    throw new
+      Error('Tokens array requires a `links` property.');
+  }
+
+  if (this.options.gfm) {
+    if (this.options.breaks) {
+      this.rules = inline.breaks;
+    } else {
+      this.rules = inline.gfm;
+    }
+  } else if (this.options.pedantic) {
+    this.rules = inline.pedantic;
+  }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+  var inline = new InlineLexer(links, options);
+  return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+  var out = ''
+    , link
+    , text
+    , href
+    , cap;
+
+  while (src) {
+    // escape
+    if (cap = this.rules.escape.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += cap[1];
+      continue;
+    }
+
+    // autolink
+    if (cap = this.rules.autolink.exec(src)) {
+      src = src.substring(cap[0].length);
+      if (cap[2] === '@') {
+        text = cap[1].charAt(6) === ':'
+          ? this.mangle(cap[1].substring(7))
+          : this.mangle(cap[1]);
+        href = this.mangle('mailto:') + text;
+      } else {
+        text = escape(cap[1]);
+        href = text;
+      }
+      out += this.renderer.link(href, null, text);
+      continue;
+    }
+
+    // url (gfm)
+    if (!this.inLink && (cap = this.rules.url.exec(src))) {
+      src = src.substring(cap[0].length);
+      text = escape(cap[1]);
+      href = text;
+      out += this.renderer.link(href, null, text);
+      continue;
+    }
+
+    // tag
+    if (cap = this.rules.tag.exec(src)) {
+      if (!this.inLink && /^<a /i.test(cap[0])) {
+        this.inLink = true;
+      } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
+        this.inLink = false;
+      }
+      src = src.substring(cap[0].length);
+      out += this.options.sanitize
+        ? escape(cap[0])
+        : cap[0];
+      continue;
+    }
+
+    // link
+    if (cap = this.rules.link.exec(src)) {
+      src = src.substring(cap[0].length);
+      this.inLink = true;
+      out += this.outputLink(cap, {
+        href: cap[2],
+        title: cap[3]
+      });
+      this.inLink = false;
+      continue;
+    }
+
+    // reflink, nolink
+    if ((cap = this.rules.reflink.exec(src))
+        || (cap = this.rules.nolink.exec(src))) {
+      src = src.substring(cap[0].length);
+      link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+      link = this.links[link.toLowerCase()];
+      if (!link || !link.href) {
+        out += cap[0].charAt(0);
+        src = cap[0].substring(1) + src;
+        continue;
+      }
+      this.inLink = true;
+      out += this.outputLink(cap, link);
+      this.inLink = false;
+      continue;
+    }
+
+    // strong
+    if (cap = this.rules.strong.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.strong(this.output(cap[2] || cap[1]));
+      continue;
+    }
+
+    // em
+    if (cap = this.rules.em.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.em(this.output(cap[2] || cap[1]));
+      continue;
+    }
+
+    // code
+    if (cap = this.rules.code.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.codespan(escape(cap[2], true));
+      continue;
+    }
+
+    // br
+    if (cap = this.rules.br.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.br();
+      continue;
+    }
+
+    // del (gfm)
+    if (cap = this.rules.del.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += this.renderer.del(this.output(cap[1]));
+      continue;
+    }
+
+    // text
+    if (cap = this.rules.text.exec(src)) {
+      src = src.substring(cap[0].length);
+      out += escape(this.smartypants(cap[0]));
+      continue;
+    }
+
+    if (src) {
+      throw new
+        Error('Infinite loop on byte: ' + src.charCodeAt(0));
+    }
+  }
+
+  return out;
+};
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+  var href = escape(link.href)
+    , title = link.title ? escape(link.title) : null;
+
+  return cap[0].charAt(0) !== '!'
+    ? this.renderer.link(href, title, this.output(cap[1]))
+    : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+  if (!this.options.smartypants) return text;
+  return text
+    // em-dashes
+    .replace(/--/g, '\u2014')
+    // opening singles
+    .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+    // closing singles & apostrophes
+    .replace(/'/g, '\u2019')
+    // opening doubles
+    .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+    // closing doubles
+    .replace(/"/g, '\u201d')
+    // ellipses
+    .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+  var out = ''
+    , l = text.length
+    , i = 0
+    , ch;
+
+  for (; i < l; i++) {
+    ch = text.charCodeAt(i);
+    if (Math.random() > 0.5) {
+      ch = 'x' + ch.toString(16);
+    }
+    out += '&#' + ch + ';';
+  }
+
+  return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+  this.options = options || {};
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+  if (this.options.highlight) {
+    var out = this.options.highlight(code, lang);
+    if (out != null && out !== code) {
+      escaped = true;
+      code = out;
+    }
+  }
+
+  if (!lang) {
+    return '<pre><code>'
+      + (escaped ? code : escape(code, true))
+      + '\n</code></pre>';
+  }
+
+  return '<pre><code class="'
+    + this.options.langPrefix
+    + escape(lang, true)
+    + '">'
+    + (escaped ? code : escape(code, true))
+    + '\n</code></pre>\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+  return '<blockquote>\n' + quote + '</blockquote>\n';
+};
+
+Renderer.prototype.html = function(html) {
+  return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+  return '<h'
+    + level
+    + ' id="'
+    + this.options.headerPrefix
+    + raw.toLowerCase().replace(/[^\w]+/g, '-')
+    + '">'
+    + text
+    + '</h'
+    + level
+    + '>\n';
+};
+
+Renderer.prototype.hr = function() {
+  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
+};
+
+Renderer.prototype.list = function(body, ordered) {
+  var type = ordered ? 'ol' : 'ul';
+  return '<' + type + '>\n' + body + '</' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+  return '<li>' + text + '</li>\n';
+};
+
+Renderer.prototype.paragraph = function(text) {
+  return '<p>' + text + '</p>\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+  return '<table>\n'
+    + '<thead>\n'
+    + header
+    + '</thead>\n'
+    + '<tbody>\n'
+    + body
+    + '</tbody>\n'
+    + '</table>\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+  return '<tr>\n' + content + '</tr>\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+  var type = flags.header ? 'th' : 'td';
+  var tag = flags.align
+    ? '<' + type + ' style="text-align:' + flags.align + '">'
+    : '<' + type + '>';
+  return tag + content + '</' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+  return '<strong>' + text + '</strong>';
+};
+
+Renderer.prototype.em = function(text) {
+  return '<em>' + text + '</em>';
+};
+
+Renderer.prototype.codespan = function(text) {
+  return '<code>' + text + '</code>';
+};
+
+Renderer.prototype.br = function() {
+  return this.options.xhtml ? '<br/>' : '<br>';
+};
+
+Renderer.prototype.del = function(text) {
+  return '<del>' + text + '</del>';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+  if (this.options.sanitize) {
+    try {
+      var prot = decodeURIComponent(unescape(href))
+        .replace(/[^\w:]/g, '')
+        .toLowerCase();
+    } catch (e) {
+      return '';
+    }
+    if (prot.indexOf('javascript:') === 0) {
+      return '';
+    }
+  }
+  var out = '<a href="' + href + '"';
+  if (title) {
+    out += ' title="' + title + '"';
+  }
+  out += '>' + text + '</a>';
+  return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+  var out = '<img src="' + href + '" alt="' + text + '"';
+  if (title) {
+    out += ' title="' + title + '"';
+  }
+  out += this.options.xhtml ? '/>' : '>';
+  return out;
+};
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+  this.tokens = [];
+  this.token = null;
+  this.options = options || marked.defaults;
+  this.options.renderer = this.options.renderer || new Renderer;
+  this.renderer = this.options.renderer;
+  this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options, renderer) {
+  var parser = new Parser(options, renderer);
+  return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+  this.inline = new InlineLexer(src.links, this.options, this.renderer);
+  this.tokens = src.reverse();
+
+  var out = '';
+  while (this.next()) {
+    out += this.tok();
+  }
+
+  return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+  return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+  return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+  var body = this.token.text;
+
+  while (this.peek().type === 'text') {
+    body += '\n' + this.next().text;
+  }
+
+  return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+  switch (this.token.type) {
+    case 'space': {
+      return '';
+    }
+    case 'hr': {
+      return this.renderer.hr();
+    }
+    case 'heading': {
+      return this.renderer.heading(
+        this.inline.output(this.token.text),
+        this.token.depth,
+        this.token.text);
+    }
+    case 'code': {
+      return this.renderer.code(this.token.text,
+        this.token.lang,
+        this.token.escaped);
+    }
+    case 'table': {
+      var header = ''
+        , body = ''
+        , i
+        , row
+        , cell
+        , flags
+        , j;
+
+      // header
+      cell = '';
+      for (i = 0; i < this.token.header.length; i++) {
+        flags = { header: true, align: this.token.align[i] };
+        cell += this.renderer.tablecell(
+          this.inline.output(this.token.header[i]),
+          { header: true, align: this.token.align[i] }
+        );
+      }
+      header += this.renderer.tablerow(cell);
+
+      for (i = 0; i < this.token.cells.length; i++) {
+        row = this.token.cells[i];
+
+        cell = '';
+        for (j = 0; j < row.length; j++) {
+          cell += this.renderer.tablecell(
+            this.inline.output(row[j]),
+            { header: false, align: this.token.align[j] }
+          );
+        }
+
+        body += this.renderer.tablerow(cell);
+      }
+      return this.renderer.table(header, body);
+    }
+    case 'blockquote_start': {
+      var body = '';
+
+      while (this.next().type !== 'blockquote_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.blockquote(body);
+    }
+    case 'list_start': {
+      var body = ''
+        , ordered = this.token.ordered;
+
+      while (this.next().type !== 'list_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.list(body, ordered);
+    }
+    case 'list_item_start': {
+      var body = '';
+
+      while (this.next().type !== 'list_item_end') {
+        body += this.token.type === 'text'
+          ? this.parseText()
+          : this.tok();
+      }
+
+      return this.renderer.listitem(body);
+    }
+    case 'loose_item_start': {
+      var body = '';
+
+      while (this.next().type !== 'list_item_end') {
+        body += this.tok();
+      }
+
+      return this.renderer.listitem(body);
+    }
+    case 'html': {
+      var html = !this.token.pre && !this.options.pedantic
+        ? this.inline.output(this.token.text)
+        : this.token.text;
+      return this.renderer.html(html);
+    }
+    case 'paragraph': {
+      return this.renderer.paragraph(this.inline.output(this.token.text));
+    }
+    case 'text': {
+      return this.renderer.paragraph(this.parseText());
+    }
+  }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+  return html
+    .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#39;');
+}
+
+function unescape(html) {
+  return html.replace(/&([#\w]+);/g, function(_, n) {
+    n = n.toLowerCase();
+    if (n === 'colon') return ':';
+    if (n.charAt(0) === '#') {
+      return n.charAt(1) === 'x'
+        ? String.fromCharCode(parseInt(n.substring(2), 16))
+        : String.fromCharCode(+n.substring(1));
+    }
+    return '';
+  });
+}
+
+function replace(regex, opt) {
+  regex = regex.source;
+  opt = opt || '';
+  return function self(name, val) {
+    if (!name) return new RegExp(regex, opt);
+    val = val.source || val;
+    val = val.replace(/(^|[^\[])\^/g, '$1');
+    regex = regex.replace(name, val);
+    return self;
+  };
+}
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+  var i = 1
+    , target
+    , key;
+
+  for (; i < arguments.length; i++) {
+    target = arguments[i];
+    for (key in target) {
+      if (Object.prototype.hasOwnProperty.call(target, key)) {
+        obj[key] = target[key];
+      }
+    }
+  }
+
+  return obj;
+}
+
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+  if (callback || typeof opt === 'function') {
+    if (!callback) {
+      callback = opt;
+      opt = null;
+    }
+
+    opt = merge({}, marked.defaults, opt || {});
+
+    var highlight = opt.highlight
+      , tokens
+      , pending
+      , i = 0;
+
+    try {
+      tokens = Lexer.lex(src, opt)
+    } catch (e) {
+      return callback(e);
+    }
+
+    pending = tokens.length;
+
+    var done = function(err) {
+      if (err) {
+        opt.highlight = highlight;
+        return callback(err);
+      }
+
+      var out;
+
+      try {
+        out = Parser.parse(tokens, opt);
+      } catch (e) {
+        err = e;
+      }
+
+      opt.highlight = highlight;
+
+      return err
+        ? callback(err)
+        : callback(null, out);
+    };
+
+    if (!highlight || highlight.length < 3) {
+      return done();
+    }
+
+    delete opt.highlight;
+
+    if (!pending) return done();
+
+    for (; i < tokens.length; i++) {
+      (function(token) {
+        if (token.type !== 'code') {
+          return --pending || done();
+        }
+        return highlight(token.text, token.lang, function(err, code) {
+          if (err) return done(err);
+          if (code == null || code === token.text) {
+            return --pending || done();
+          }
+          token.text = code;
+          token.escaped = true;
+          --pending || done();
+        });
+      })(tokens[i]);
+    }
+
+    return;
+  }
+  try {
+    if (opt) opt = merge({}, marked.defaults, opt);
+    return Parser.parse(Lexer.lex(src, opt), opt);
+  } catch (e) {
+    e.message += '\nPlease report this to https://github.com/chjj/marked.';
+    if ((opt || marked.defaults).silent) {
+      return '<p>An error occured:</p><pre>'
+        + escape(e.message + '', true)
+        + '</pre>';
+    }
+    throw e;
+  }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+  merge(marked.defaults, opt);
+  return marked;
+};
+
+marked.defaults = {
+  gfm: true,
+  tables: true,
+  breaks: false,
+  pedantic: false,
+  sanitize: false,
+  smartLists: false,
+  silent: false,
+  highlight: null,
+  langPrefix: 'lang-',
+  smartypants: false,
+  headerPrefix: '',
+  renderer: new Renderer,
+  xhtml: false
+};
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+  module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+  define(function() { return marked; });
+} else {
+  this.marked = marked;
+}
+
+}).call(function() {
+  return this || (typeof window !== 'undefined' ? window : global);
+}());
\ No newline at end of file
diff --git a/profiles/killpay/src/main/webapp/lib/object-assign-pollyfill.js b/profiles/killpay/src/main/webapp/lib/object-assign-pollyfill.js
new file mode 100644
index 0000000..5179920
--- /dev/null
+++ b/profiles/killpay/src/main/webapp/lib/object-assign-pollyfill.js
@@ -0,0 +1,23 @@
+if (typeof Object.assign != 'function') {
+  (function () {
+    Object.assign = function (target) {
+      'use strict';
+      if (target === undefined || target === null) {
+        throw new TypeError('Cannot convert undefined or null to object');
+      }
+
+      var output = Object(target);
+      for (var index = 1; index < arguments.length; index++) {
+        var source = arguments[index];
+        if (source !== undefined && source !== null) {
+          for (var nextKey in source) {
+            if (Object.prototype.hasOwnProperty.call(source, nextKey)) {
+              output[nextKey] = source[nextKey];
+            }
+          }
+        }
+      }
+      return output;
+    };
+  })();
+}
diff --git a/profiles/killpay/src/main/webapp/lib/swagger-oauth.js b/profiles/killpay/src/main/webapp/lib/swagger-oauth.js
index 8bb17fb..a35bda3 100644
--- a/profiles/killpay/src/main/webapp/lib/swagger-oauth.js
+++ b/profiles/killpay/src/main/webapp/lib/swagger-oauth.js
@@ -3,14 +3,37 @@ var popupMask;
 var popupDialog;
 var clientId;
 var realm;
+var redirect_uri;
+var clientSecret;
+var scopeSeparator;
+var additionalQueryStringParams;
 
 function handleLogin() {
   var scopes = [];
 
-  if(window.swaggerUi.api.authSchemes 
-    && window.swaggerUi.api.authSchemes.oauth2
-    && window.swaggerUi.api.authSchemes.oauth2.scopes) {
-    scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
+  var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
+  if(auths) {
+    var key;
+    var defs = auths;
+    for(key in defs) {
+      var auth = defs[key];
+      if(auth.type === 'oauth2' && auth.scopes) {
+        var scope;
+        if(Array.isArray(auth.scopes)) {
+          // 1.2 support
+          var i;
+          for(i = 0; i < auth.scopes.length; i++) {
+            scopes.push(auth.scopes[i]);
+          }
+        }
+        else {
+          // 2.0 support
+          for(scope in auth.scopes) {
+            scopes.push({scope: scope, description: auth.scopes[scope], OAuthSchemeKey: key});
+          }
+        }
+      }
+    }
   }
 
   if(window.swaggerUi.api
@@ -18,36 +41,37 @@ function handleLogin() {
     appName = window.swaggerUi.api.info.title;
   }
 
-  if(popupDialog.length > 0)
-    popupDialog = popupDialog.last();
-  else {
-    popupDialog = $(
-      [
-        '<div class="api-popup-dialog">',
-        '<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
-        '<div class="api-popup-content">',
-          '<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
-            '<a href="#">Learn how to use</a>',
-          '</p>',
-          '<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
-          '<ul class="api-popup-scopes">',
-          '</ul>',
-          '<p class="error-msg"></p>',
-          '<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
-        '</div>',
-        '</div>'].join(''));
-    $(document.body).append(popupDialog);
-
-    popup = popupDialog.find('ul.api-popup-scopes').empty();
-    for (i = 0; i < scopes.length; i ++) {
-      scope = scopes[i];
-      str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
-      if (scope.description) {
-        str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
-      }
-      str += '</label></li>';
-      popup.append(str);
+  $('.api-popup-dialog').remove(); 
+  popupDialog = $(
+    [
+      '<div class="api-popup-dialog">',
+      '<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
+      '<div class="api-popup-content">',
+        '<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
+          '<a href="#">Learn how to use</a>',
+        '</p>',
+        '<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
+        '<ul class="api-popup-scopes">',
+        '</ul>',
+        '<p class="error-msg"></p>',
+        '<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
+      '</div>',
+      '</div>'].join(''));
+  $(document.body).append(popupDialog);
+
+  //TODO: only display applicable scopes (will need to pass them into handleLogin)
+  popup = popupDialog.find('ul.api-popup-scopes').empty();
+  for (i = 0; i < scopes.length; i ++) {
+    scope = scopes[i];
+    str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"' +'" oauthtype="' + scope.OAuthSchemeKey +'"/>' + '<label for="scope_' + i + '">' + scope.scope ;
+    if (scope.description) {
+      if ($.map(auths, function(n, i) { return i; }).length > 1) //if we have more than one scheme, display schemes
+	    str += '<br/><span class="api-scope-desc">' + scope.description + ' ('+ scope.OAuthSchemeKey+')' +'</span>';
+	  else
+	    str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
     }
+    str += '</label></li>';
+    popup.append(str);
   }
 
   var $win = $(window),
@@ -67,7 +91,11 @@ function handleLogin() {
   popupDialog.find('button.api-popup-cancel').click(function() {
     popupMask.hide();
     popupDialog.hide();
+    popupDialog.empty();
+    popupDialog = [];
   });
+
+  $('button.api-popup-authbtn').unbind();
   popupDialog.find('button.api-popup-authbtn').click(function() {
     popupMask.hide();
     popupDialog.hide();
@@ -75,34 +103,74 @@ function handleLogin() {
     var authSchemes = window.swaggerUi.api.authSchemes;
     var host = window.location;
     var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
-    var redirectUrl = host.protocol + '//' + host.host + pathname + "/o2c.html";
+    var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
+    var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
     var url = null;
+    var scopes = []
+    var o = popup.find('input:checked'); 
+    var OAuthSchemeKeys = [];
+    var state;
+    for(k =0; k < o.length; k++) {
+      var scope = $(o[k]).attr('scope');
+      if (scopes.indexOf(scope) === -1)
+        scopes.push(scope);
+      var OAuthSchemeKey = $(o[k]).attr('oauthtype');      
+      if (OAuthSchemeKeys.indexOf(OAuthSchemeKey) === -1)
+          OAuthSchemeKeys.push(OAuthSchemeKey);
+    }
+    
+    //TODO: merge not replace if scheme is different from any existing 
+    //(needs to be aware of schemes to do so correctly)
+    window.enabledScopes=scopes;    
+    
+    for (var key in authSchemes) { 
+      if (authSchemes.hasOwnProperty(key) && OAuthSchemeKeys.indexOf(key) != -1) { //only look at keys that match this scope.
+        var flow = authSchemes[key].flow;
 
-    for (var key in authSchemes) {
-      if (authSchemes.hasOwnProperty(key)) {
-        var o = authSchemes[key].grantTypes;
-        for(var t in o) {
-          if(o.hasOwnProperty(t) && t === 'implicit') {
-            var dets = o[t];
-            url = dets.loginEndpoint.url + "?response_type=token";
-            window.swaggerUi.tokenName = dets.tokenName;
+        if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
+          var dets = authSchemes[key];
+          url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
+          window.swaggerUi.tokenName = dets.tokenName || 'access_token';
+          window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
+          state = key;
+        }
+        else if(authSchemes[key].type === 'oauth2' && flow && (flow === 'application')) {
+            var dets = authSchemes[key];
+            window.swaggerUi.tokenName = dets.tokenName || 'access_token';
+            clientCredentialsFlow(scopes, dets.tokenUrl, key);
+            return;
+        }        
+        else if(authSchemes[key].grantTypes) {
+          // 1.2 support
+          var o = authSchemes[key].grantTypes;
+          for(var t in o) {
+            if(o.hasOwnProperty(t) && t === 'implicit') {
+              var dets = o[t];
+              var ep = dets.loginEndpoint.url;
+              url = dets.loginEndpoint.url + '?response_type=token';
+              window.swaggerUi.tokenName = dets.tokenName;
+            }
+            else if (o.hasOwnProperty(t) && t === 'accessCode') {
+              var dets = o[t];
+              var ep = dets.tokenRequestEndpoint.url;
+              url = dets.tokenRequestEndpoint.url + '?response_type=code';
+              window.swaggerUi.tokenName = dets.tokenName;
+            }
           }
         }
       }
     }
-    var scopes = []
-    var o = $('.api-popup-scopes').find('input:checked');
 
-    for(k =0; k < o.length; k++) {
-      scopes.push($(o[k]).attr("scope"));
-    }
-
-    window.enabledScopes=scopes;
+    redirect_uri = redirectUrl;
 
     url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
     url += '&realm=' + encodeURIComponent(realm);
     url += '&client_id=' + encodeURIComponent(clientId);
-    url += '&scope=' + encodeURIComponent(scopes);
+    url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
+    url += '&state=' + encodeURIComponent(state);
+    for (var key in additionalQueryStringParams) {
+        url += '&' + key + '=' + encodeURIComponent(additionalQueryStringParams[key]);
+    }
 
     window.open(url);
   });
@@ -114,8 +182,8 @@ function handleLogin() {
 
 
 function handleLogout() {
-  for(key in window.authorizations.authz){
-    window.authorizations.remove(key)
+  for(key in window.swaggerUi.api.clientAuthorizations.authz){
+    window.swaggerUi.api.clientAuthorizations.remove(key)
   }
   window.enabledScopes = null;
   $('.api-ic.ic-on').addClass('ic-off');
@@ -130,18 +198,22 @@ function initOAuth(opts) {
   var o = (opts||{});
   var errors = [];
 
-  appName = (o.appName||errors.push("missing appName"));
+  appName = (o.appName||errors.push('missing appName'));
   popupMask = (o.popupMask||$('#api-common-mask'));
   popupDialog = (o.popupDialog||$('.api-popup-dialog'));
-  clientId = (o.clientId||errors.push("missing client id"));
-  realm = (o.realm||errors.push("missing realm"));
+  clientId = (o.clientId||errors.push('missing client id'));
+  clientSecret = (o.clientSecret||null);
+  realm = (o.realm||errors.push('missing realm'));
+  scopeSeparator = (o.scopeSeparator||' ');
+  additionalQueryStringParams = (o.additionalQueryStringParams||{});
 
   if(errors.length > 0){
-    log("auth unable initialize oauth: " + errors);
+    log('auth unable initialize oauth: ' + errors);
     return;
   }
 
   $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
+  $('.api-ic').unbind();
   $('.api-ic').click(function(s) {
     if($(s.target).hasClass('ic-off'))
       handleLogin();
@@ -152,7 +224,68 @@ function initOAuth(opts) {
   });
 }
 
-function onOAuthComplete(token) {
+function clientCredentialsFlow(scopes, tokenUrl, OAuthSchemeKey) {
+    var params = {
+      'client_id': clientId,
+      'client_secret': clientSecret,
+      'scope': scopes.join(' '),
+      'grant_type': 'client_credentials'
+    }
+    $.ajax(
+    {
+      url : tokenUrl,
+      type: "POST",
+      data: params,
+      success:function(data, textStatus, jqXHR)
+      {
+        onOAuthComplete(data,OAuthSchemeKey);
+      },
+      error: function(jqXHR, textStatus, errorThrown)
+      {
+        onOAuthComplete("");
+      }
+    });
+    
+  }
+
+window.processOAuthCode = function processOAuthCode(data) {
+  var OAuthSchemeKey = data.state;
+
+  // redirect_uri is required in auth code flow 
+  // see https://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.1.3
+  var host = window.location;
+  var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
+  var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
+  var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
+
+  var params = {
+    'client_id': clientId,
+    'code': data.code,
+    'grant_type': 'authorization_code',
+    'redirect_uri': redirectUrl
+  };
+
+  if (clientSecret) {
+    params.client_secret = clientSecret;
+  }
+
+  $.ajax(
+  {
+    url : window.swaggerUi.tokenUrl,
+    type: "POST",
+    data: params,
+    success:function(data, textStatus, jqXHR)
+    {
+      onOAuthComplete(data, OAuthSchemeKey);
+    },
+    error: function(jqXHR, textStatus, errorThrown)
+    {
+      onOAuthComplete("");
+    }
+  });
+};
+
+window.onOAuthComplete = function onOAuthComplete(token,OAuthSchemeKey) {
   if(token) {
     if(token.error) {
       var checkbox = $('input[type=checkbox],.secured')
@@ -162,11 +295,14 @@ function onOAuthComplete(token) {
       alert(token.error);
     }
     else {
-      var b = token[window.swaggerUi.tokenName];
+      var b = token[window.swaggerUi.tokenName];      
+      if (!OAuthSchemeKey){
+          OAuthSchemeKey = token.state;
+      }
       if(b){
         // if all roles are satisfied
         var o = null;
-        $.each($('.auth #api_information_panel'), function(k, v) {
+        $.each($('.auth .api-ic .api_information_panel'), function(k, v) { 
           var children = v;
           if(children && children.childNodes) {
             var requiredScopes = [];
@@ -183,7 +319,7 @@ function onOAuthComplete(token) {
               }
             }
             if(diff.length > 0){
-              o = v.parentNode;
+              o = v.parentNode.parentNode;
               $(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
               $(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
 
@@ -192,20 +328,20 @@ function onOAuthComplete(token) {
               $(o).find('.api-ic').removeClass('ic-error');
             }
             else {
-              o = v.parentNode;
+              o = v.parentNode.parentNode;
               $(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
               $(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
 
               // all scopes are satisfied
               $(o).find('.api-ic').addClass('ic-info');
               $(o).find('.api-ic').removeClass('ic-warning');
-              $(o).find('.api-ic').removeClass('ic-error');          
+              $(o).find('.api-ic').removeClass('ic-error');
             }
           }
         });
-
-        window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
+        window.swaggerUi.api.clientAuthorizations.add(window.OAuthSchemeKey, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
+        window.swaggerUi.load();
       }
     }
   }
-}
\ No newline at end of file
+};
diff --git a/profiles/killpay/src/main/webapp/lib/swagger-ui.min.js b/profiles/killpay/src/main/webapp/lib/swagger-ui.min.js
index 0d60523..d5ed02d 100644
--- a/profiles/killpay/src/main/webapp/lib/swagger-ui.min.js
+++ b/profiles/killpay/src/main/webapp/lib/swagger-ui.min.js
@@ -1 +1,10 @@
-$(function(){$.fn.vAlign=function(){return this.each(function(c){var a=$(this).height();var d=$(this).parent().height();var b=(d-a)/2;$(this).css("margin-top",b)})};$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(b){var d=$(this).closest("form").innerWidth();var c=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10);var a=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",d-c-a)})};$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent();$("ul.downplayed li div.content p").vAlign();$("form.sandbox").submit(function(){var a=true;$(this).find("input.required").each(function(){$(this).removeClass("error");if($(this).val()==""){$(this).addClass("error");$(this).wiggle();a=false}});return a})});function clippyCopiedCallback(b){$("#api_key_copied").fadeIn().delay(1000).fadeOut()}log=function(){log.history=log.history||[];log.history.push(arguments);if(this.console){console.log(Array.prototype.slice.call(arguments)[0])}};if(Function.prototype.bind&&console&&typeof console.log=="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(a){console[a]=this.bind(console[a],console)},Function.prototype.call)}var Docs={shebang:function(){var b=$.param.fragment().split("/");b.shift();switch(b.length){case 1:var d="resource_"+b[0];Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});break;case 2:Docs.expandEndpointListForResource(b[0]);$("#"+d).slideto({highlight:false});var c=b.join("_");var a=c+"_content";Docs.expandOperation($("#"+a));$("#"+c).slideto({highlight:false});break}},toggleEndpointListForResource:function(b){var a=$("li#resource_"+Docs.escapeResourceName(b)+" ul.endpoints");if(a.is(":visible")){Docs.collapseEndpointListForResource(b)}else{Docs.expandEndpointListForResource(b)}},expandEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);if(b==""){$(".resource ul.endpoints").slideDown();return}$("li#resource_"+b).addClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideDown()},collapseEndpointListForResource:function(b){var b=Docs.escapeResourceName(b);$("li#resource_"+b).removeClass("active");var a=$("li#resource_"+b+" ul.endpoints");a.slideUp()},expandOperationsForResource:function(a){Docs.expandEndpointListForResource(a);if(a==""){$(".resource ul.endpoints li.operation div.content").slideDown();return}$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(a){Docs.expandEndpointListForResource(a);$("li#resource_"+Docs.escapeResourceName(a)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(a){return a.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(a){a.slideDown()},collapseOperation:function(a){a.slideUp()}};(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.produces,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n	<option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n	";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="contentType"></label>\n<select name="contentType">\n';c=f["if"].call(l,l.produces,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.main=b(function(h,n,g,m,l){this.compilerInfo=[4,">= 1.0.0"];g=this.merge(g,h.helpers);l=l||{};var j="",c,s,i="function",k=this.escapeExpression,q=this;function f(x,w){var t="",v,u;t+='\n  <div class="info_title">'+k(((v=((v=x.info),v==null||v===false?v:v.title)),typeof v===i?v.apply(x):v))+'</div>\n  <div class="info_description">';u=((v=((v=x.info),v==null||v===false?v:v.description)),typeof v===i?v.apply(x):v);if(u||u===0){t+=u}t+="</div>\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.termsOfServiceUrl),{hash:{},inverse:q.noop,fn:q.program(2,d,w),data:w});if(u||u===0){t+=u}t+="\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.contact),{hash:{},inverse:q.noop,fn:q.program(4,r,w),data:w});if(u||u===0){t+=u}t+="\n  ";u=g["if"].call(x,((v=x.info),v==null||v===false?v:v.license),{hash:{},inverse:q.noop,fn:q.program(6,p,w),data:w});if(u||u===0){t+=u}t+="\n  ";return t}function d(w,v){var t="",u;t+='<div class="info_tos"><a href="'+k(((u=((u=w.info),u==null||u===false?u:u.termsOfServiceUrl)),typeof u===i?u.apply(w):u))+'">Terms of service</a></div>';return t}function r(w,v){var t="",u;t+="<div class='info_contact'><a href=\"mailto:"+k(((u=((u=((u=w.info),u==null||u===false?u:u.contact)),u==null||u===false?u:u.name)),typeof u===i?u.apply(w):u))+'">Contact the developer</a></div>';return t}function p(w,v){var t="",u;t+="<div class='info_license'><a href='"+k(((u=((u=((u=w.info),u==null||u===false?u:u.license)),u==null||u===false?u:u.url)),typeof u===i?u.apply(w):u))+"'>"+k(((u=((u=((u=w.info),u==null||u===false?u:u.license)),u==null||u===false?u:u.name)),typeof u===i?u.apply(w):u))+"</a></div>";return t}function o(w,v){var t="",u;t+='\n    , <span style="font-variant: small-caps">api version</span>: '+k(((u=((u=w.info),u==null||u===false?u:u.version)),typeof u===i?u.apply(w):u))+"\n    ";return t}function e(w,v){var t="",u;t+='\n    <span style="float:right"><a href="http://online.swagger.io/validator/debug?url=';if(u=g.url){u=u.call(w,{hash:{},data:v})}else{u=w.url;u=typeof u===i?u.apply(w):u}t+=k(u)+'"><img id="validator" src="http://online.swagger.io/validator?url=';if(u=g.url){u=u.call(w,{hash:{},data:v})}else{u=w.url;u=typeof u===i?u.apply(w):u}t+=k(u)+'"></a>\n    </span>\n    ';return t}j+="<div class='info' id='api_info'>\n  ";c=g["if"].call(n,n.info,{hash:{},inverse:q.noop,fn:q.program(1,f,l),data:l});if(c||c===0){j+=c}j+="\n</div>\n<div class='container' id='resources_container'>\n  <ul id='resources'>\n  </ul>\n\n  <div class=\"footer\">\n    <br>\n    <br>\n    <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: ";if(c=g.basePath){c=c.call(n,{hash:{},data:l})}else{c=n.basePath;c=typeof c===i?c.apply(n):c}j+=k(c)+"\n    ";s=g["if"].call(n,((c=n.info),c==null||c===false?c:c.version),{hash:{},inverse:q.noop,fn:q.program(8,o,l),data:l});if(s||s===0){j+=s}j+="]\n    ";s=g["if"].call(n,n.validatorUrl,{hash:{},inverse:q.noop,fn:q.program(10,e,l),data:l});if(s||s===0){j+=s}j+="\n    </h4>\n    </div>\n</div>\n";return j})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.operation=b(function(g,s,q,m,y){this.compilerInfo=[4,">= 1.0.0"];q=this.merge(q,g.helpers);y=y||{};var r="",i,f,e="function",d=this.escapeExpression,p=this,c=q.blockHelperMissing;function o(C,B){var z="",A;z+="\n        <h4>Implementation Notes</h4>\n        <p>";if(A=q.description){A=A.call(C,{hash:{},data:B})}else{A=C.description;A=typeof A===e?A.apply(C):A}if(A||A===0){z+=A}z+="</p>\n        ";return z}function n(A,z){return'\n        <div class="auth">\n        <span class="api-ic ic-error"></span>'}function l(C,B){var z="",A;z+='\n          <div id="api_information_panel" style="top: 526px; left: 776px; display: none;">\n          ';A=q.each.call(C,C,{hash:{},inverse:p.noop,fn:p.program(6,k,B),data:B});if(A||A===0){z+=A}z+="\n          </div>\n        ";return z}function k(D,C){var z="",B,A;z+="\n            <div title='";A=((B=D.description),typeof B===e?B.apply(D):B);if(A||A===0){z+=A}z+="'>"+d(((B=D.scope),typeof B===e?B.apply(D):B))+"</div>\n          ";return z}function h(A,z){return"</div>"}function x(A,z){return'\n        <div class=\'access\'>\n          <span class="api-ic ic-off" title="click to authenticate"></span>\n        </div>\n        '}function w(A,z){return'\n          <h4>Response Class</h4>\n          <p><span class="model-signature" /></p>\n          <br/>\n          <div class="response-content-type" />\n        '}function v(A,z){return'\n          <h4>Parameters</h4>\n          <table class=\'fullwidth\'>\n          <thead>\n            <tr>\n            <th style="width: 100px; max-width: 100px">Parameter</th>\n            <th style="width: 310px; max-width: 310px">Value</th>\n            <th style="width: 200px; max-width: 200px">Description</th>\n            <th style="width: 100px; max-width: 100px">Parameter Type</th>\n            <th style="width: 220px; max-width: 230px">Data Type</th>\n            </tr>\n          </thead>\n          <tbody class="operation-params">\n\n          </tbody>\n          </table>\n          '}function u(A,z){return"\n          <div style='margin:0;padding:0;display:inline'></div>\n          <h4>Response Messages</h4>\n          <table class='fullwidth'>\n            <thead>\n            <tr>\n              <th>HTTP Status Code</th>\n              <th>Reason</th>\n              <th>Response Model</th>\n            </tr>\n            </thead>\n            <tbody class=\"operation-status\">\n            \n            </tbody>\n          </table>\n          "}function t(A,z){return"\n          "}function j(A,z){return"\n          <div class='sandbox_header'>\n            <input class='submit' name='commit' type='button' value='Try it out!' />\n            <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n            <span class='response_throbber' style='display:none'></span>\n          </div>\n          "}r+="\n  <ul class='operations' >\n    <li class='";if(i=q.method){i=i.call(s,{hash:{},data:y})}else{i=s.method;i=typeof i===e?i.apply(s):i}r+=d(i)+" operation' id='";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"_";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+"'>\n      <div class='heading'>\n        <h3>\n          <span class='http_method'>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.method){i=i.call(s,{hash:{},data:y})}else{i=s.method;i=typeof i===e?i.apply(s):i}r+=d(i)+"</a>\n          </span>\n          <span class='path'>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.path){i=i.call(s,{hash:{},data:y})}else{i=s.path;i=typeof i===e?i.apply(s):i}r+=d(i)+"</a>\n          </span>\n        </h3>\n        <ul class='options'>\n          <li>\n          <a href='#!/";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"/";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+'\' class="toggleOperation">';if(i=q.summary){i=i.call(s,{hash:{},data:y})}else{i=s.summary;i=typeof i===e?i.apply(s):i}if(i||i===0){r+=i}r+="</a>\n          </li>\n        </ul>\n      </div>\n      <div class='content' id='";if(i=q.parentId){i=i.call(s,{hash:{},data:y})}else{i=s.parentId;i=typeof i===e?i.apply(s):i}r+=d(i)+"_";if(i=q.nickname){i=i.call(s,{hash:{},data:y})}else{i=s.nickname;i=typeof i===e?i.apply(s):i}r+=d(i)+"_content' style='display:none'>\n        ";i=q["if"].call(s,s.description,{hash:{},inverse:p.noop,fn:p.program(1,o,y),data:y});if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(3,n,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";i=q.each.call(s,s.oauth,{hash:{},inverse:p.noop,fn:p.program(5,l,y),data:y});if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(8,h,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";f={hash:{},inverse:p.noop,fn:p.program(10,x,y),data:y};if(i=q.oauth){i=i.call(s,f)}else{i=s.oauth;i=typeof i===e?i.apply(s):i}if(!q.oauth){i=c.call(s,i,f)}if(i||i===0){r+=i}r+="\n        ";i=q["if"].call(s,s.type,{hash:{},inverse:p.noop,fn:p.program(12,w,y),data:y});if(i||i===0){r+=i}r+="\n        <form accept-charset='UTF-8' class='sandbox'>\n          <div style='margin:0;padding:0;display:inline'></div>\n          ";i=q["if"].call(s,s.parameters,{hash:{},inverse:p.noop,fn:p.program(14,v,y),data:y});if(i||i===0){r+=i}r+="\n          ";i=q["if"].call(s,s.responseMessages,{hash:{},inverse:p.noop,fn:p.program(16,u,y),data:y});if(i||i===0){r+=i}r+="\n          ";i=q["if"].call(s,s.isReadOnly,{hash:{},inverse:p.program(20,j,y),fn:p.program(18,t,y),data:y});if(i||i===0){r+=i}r+="\n        </form>\n        <div class='response' style='display:none'>\n          <h4>Request URL</h4>\n          <div class='block request_url'></div>\n          <h4>Response Body</h4>\n          <div class='block response_body'></div>\n          <h4>Response Code</h4>\n          <div class='block response_code'></div>\n          <h4>Response Headers</h4>\n          <div class='block response_headers'></div>\n        </div>\n      </div>\n    </li>\n  </ul>\n";return r})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param=b(function(f,q,o,j,t){this.compilerInfo=[4,">= 1.0.0"];o=this.merge(o,f.helpers);t=t||{};var p="",g,d="function",c=this.escapeExpression,n=this;function m(y,x){var v="",w;v+="\n		";w=o["if"].call(y,y.isFile,{hash:{},inverse:n.program(4,k,x),fn:n.program(2,l,x),data:x});if(w||w===0){v+=w}v+="\n	";return v}function l(y,x){var v="",w;v+='\n			<input type="file" name=\'';if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+'\'/>\n			<div class="parameter-content-type" />\n		';return v}function k(y,x){var v="",w;v+="\n			";w=o["if"].call(y,y["default"],{hash:{},inverse:n.program(7,h,x),fn:n.program(5,i,x),data:x});if(w||w===0){v+=w}v+="\n		";return v}function i(y,x){var v="",w;v+="\n				<textarea class='body-textarea' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"'>";if(w=o["default"]){w=w.call(y,{hash:{},data:x})}else{w=y["default"];w=typeof w===d?w.apply(y):w}v+=c(w)+"</textarea>\n			";return v}function h(y,x){var v="",w;v+="\n				<textarea class='body-textarea' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+'\'></textarea>\n				<br />\n				<div class="parameter-content-type" />\n			';return v}function e(y,x){var v="",w;v+="\n		";w=o["if"].call(y,y.isFile,{hash:{},inverse:n.program(10,u,x),fn:n.program(2,l,x),data:x});if(w||w===0){v+=w}v+="\n	";return v}function u(y,x){var v="",w;v+="\n			";w=o["if"].call(y,y["default"],{hash:{},inverse:n.program(13,r,x),fn:n.program(11,s,x),data:x});if(w||w===0){v+=w}v+="\n		";return v}function s(y,x){var v="",w;v+="\n				<input class='parameter' minlength='0' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"' placeholder='' type='text' value='";if(w=o["default"]){w=w.call(y,{hash:{},data:x})}else{w=y["default"];w=typeof w===d?w.apply(y):w}v+=c(w)+"'/>\n			";return v}function r(y,x){var v="",w;v+="\n				<input class='parameter' minlength='0' name='";if(w=o.name){w=w.call(y,{hash:{},data:x})}else{w=y.name;w=typeof w===d?w.apply(y):w}v+=c(w)+"' placeholder='' type='text' value=''/>\n			";return v}p+="<td class='code'>";if(g=o.name){g=g.call(q,{hash:{},data:t})}else{g=q.name;g=typeof g===d?g.apply(q):g}p+=c(g)+"</td>\n<td>\n\n	";g=o["if"].call(q,q.isBody,{hash:{},inverse:n.program(9,e,t),fn:n.program(1,m,t),data:t});if(g||g===0){p+=g}p+="\n\n</td>\n<td>";if(g=o.description){g=g.call(q,{hash:{},data:t})}else{g=q.description;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+="</td>\n<td>";if(g=o.paramType){g=g.call(q,{hash:{},data:t})}else{g=q.paramType;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+='</td>\n<td>\n	<span class="model-signature"></span>\n</td>\n';return p})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_list=b(function(h,t,r,m,y){this.compilerInfo=[4,">= 1.0.0"];r=this.merge(r,h.helpers);y=y||{};var s="",j,g,e,p=this,q=r.helperMissing,d="function",c=this.escapeExpression;function o(A,z){return" multiple='multiple'"}function n(A,z){return"\n    "}function l(C,B){var z="",A;z+="\n      ";A=r["if"].call(C,C.defaultValue,{hash:{},inverse:p.program(8,i,B),fn:p.program(6,k,B),data:B});if(A||A===0){z+=A}z+="\n    ";return z}function k(A,z){return"\n      "}function i(E,D){var z="",C,B,A;z+="\n        ";A={hash:{},inverse:p.program(11,x,D),fn:p.program(9,f,D),data:D};B=((C=r.isArray||E.isArray),C?C.call(E,E,A):q.call(E,"isArray",E,A));if(B||B===0){z+=B}z+="\n      ";return z}function f(A,z){return"\n        "}function x(A,z){return"\n          <option selected=\"\" value=''></option>\n        "}function w(C,B){var z="",A;z+="\n      ";A=r["if"].call(C,C.isDefault,{hash:{},inverse:p.program(16,u,B),fn:p.program(14,v,B),data:B});if(A||A===0){z+=A}z+="\n    ";return z}function v(C,B){var z="",A;z+='\n        <option selected="" value=\'';if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"'>";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+" (default)</option>\n      ";return z}function u(C,B){var z="",A;z+="\n        <option value='";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"'>";if(A=r.value){A=A.call(C,{hash:{},data:B})}else{A=C.value;A=typeof A===d?A.apply(C):A}z+=c(A)+"</option>\n      ";return z}s+="<td class='code'>";if(j=r.name){j=j.call(t,{hash:{},data:y})}else{j=t.name;j=typeof j===d?j.apply(t):j}s+=c(j)+"</td>\n<td>\n  <select ";e={hash:{},inverse:p.noop,fn:p.program(1,o,y),data:y};g=((j=r.isArray||t.isArray),j?j.call(t,t,e):q.call(t,"isArray",t,e));if(g||g===0){s+=g}s+=" class='parameter' name='";if(g=r.name){g=g.call(t,{hash:{},data:y})}else{g=t.name;g=typeof g===d?g.apply(t):g}s+=c(g)+"'>\n    ";g=r["if"].call(t,t.required,{hash:{},inverse:p.program(5,l,y),fn:p.program(3,n,y),data:y});if(g||g===0){s+=g}s+="\n    ";g=r.each.call(t,((j=t.allowableValues),j==null||j===false?j:j.descriptiveValues),{hash:{},inverse:p.noop,fn:p.program(13,w,y),data:y});if(g||g===0){s+=g}s+="\n  </select>\n</td>\n<td>";if(g=r.description){g=g.call(t,{hash:{},data:y})}else{g=t.description;g=typeof g===d?g.apply(t):g}if(g||g===0){s+=g}s+="</td>\n<td>";if(g=r.paramType){g=g.call(t,{hash:{},data:y})}else{g=t.paramType;g=typeof g===d?g.apply(t):g}if(g||g===0){s+=g}s+='</td>\n<td><span class="model-signature"></span></td>';return s})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r;q+="\n        <textarea class='body-textarea' readonly='readonly' name='";if(r=f.name){r=r.call(t,{hash:{},data:s})}else{r=t.name;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"</textarea>\n    ";return q}function c(t,s){var q="",r;q+="\n        ";r=f["if"].call(t,t.defaultValue,{hash:{},inverse:o.program(6,n,s),fn:o.program(4,p,s),data:s});if(r||r===0){q+=r}q+="\n    ";return q}function p(t,s){var q="",r;q+="\n            ";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"\n        ";return q}function n(r,q){return"\n            (empty)\n        "}i+="<td class='code'>";if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</td>\n<td>\n    ";d=f["if"].call(m,m.isBody,{hash:{},inverse:o.program(3,c,k),fn:o.program(1,e,k),data:k});if(d||d===0){i+=d}i+="\n</td>\n<td>";if(d=f.description){d=d.call(m,{hash:{},data:k})}else{d=m.description;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="</td>\n<td>";if(d=f.paramType){d=d.call(m,{hash:{},data:k})}else{d=m.paramType;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+='</td>\n<td><span class="model-signature"></span></td>\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_readonly_required=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,h="function",j=this.escapeExpression,o=this;function e(t,s){var q="",r;q+="\n        <textarea class='body-textarea'  readonly='readonly' placeholder='(required)' name='";if(r=f.name){r=r.call(t,{hash:{},data:s})}else{r=t.name;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"</textarea>\n    ";return q}function c(t,s){var q="",r;q+="\n        ";r=f["if"].call(t,t.defaultValue,{hash:{},inverse:o.program(6,n,s),fn:o.program(4,p,s),data:s});if(r||r===0){q+=r}q+="\n    ";return q}function p(t,s){var q="",r;q+="\n            ";if(r=f.defaultValue){r=r.call(t,{hash:{},data:s})}else{r=t.defaultValue;r=typeof r===h?r.apply(t):r}q+=j(r)+"\n        ";return q}function n(r,q){return"\n            (empty)\n        "}i+="<td class='code required'>";if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</td>\n<td>\n    ";d=f["if"].call(m,m.isBody,{hash:{},inverse:o.program(3,c,k),fn:o.program(1,e,k),data:k});if(d||d===0){i+=d}i+="\n</td>\n<td>";if(d=f.description){d=d.call(m,{hash:{},data:k})}else{d=m.description;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="</td>\n<td>";if(d=f.paramType){d=d.call(m,{hash:{},data:k})}else{d=m.paramType;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+='</td>\n<td><span class="model-signature"></span></td>\n';return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.param_required=b(function(f,q,o,j,u){this.compilerInfo=[4,">= 1.0.0"];o=this.merge(o,f.helpers);u=u||{};var p="",g,d="function",c=this.escapeExpression,n=this;function m(z,y){var w="",x;w+="\n		";x=o["if"].call(z,z.isFile,{hash:{},inverse:n.program(4,k,y),fn:n.program(2,l,y),data:y});if(x||x===0){w+=x}w+="\n	";return w}function l(z,y){var w="",x;w+='\n			<input type="file" name=\'';if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n		";return w}function k(z,y){var w="",x;w+="\n			";x=o["if"].call(z,z.defaultValue,{hash:{},inverse:n.program(7,h,y),fn:n.program(5,i,y),data:y});if(x||x===0){w+=x}w+="\n		";return w}function i(z,y){var w="",x;w+="\n				<textarea class='body-textarea' placeholder='(required)' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'>";if(x=o.defaultValue){x=x.call(z,{hash:{},data:y})}else{x=z.defaultValue;x=typeof x===d?x.apply(z):x}w+=c(x)+"</textarea>\n			";return w}function h(z,y){var w="",x;w+="\n				<textarea class='body-textarea' placeholder='(required)' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+'\'></textarea>\n				<br />\n				<div class="parameter-content-type" />\n			';return w}function e(z,y){var w="",x;w+="\n		";x=o["if"].call(z,z.isFile,{hash:{},inverse:n.program(12,t,y),fn:n.program(10,v,y),data:y});if(x||x===0){w+=x}w+="\n	";return w}function v(z,y){var w="",x;w+="\n			<input class='parameter' class='required' type='file' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n		";return w}function t(z,y){var w="",x;w+="\n			";x=o["if"].call(z,z.defaultValue,{hash:{},inverse:n.program(15,r,y),fn:n.program(13,s,y),data:y});if(x||x===0){w+=x}w+="\n		";return w}function s(z,y){var w="",x;w+="\n				<input class='parameter required' minlength='1' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"' placeholder='(required)' type='text' value='";if(x=o.defaultValue){x=x.call(z,{hash:{},data:y})}else{x=z.defaultValue;x=typeof x===d?x.apply(z):x}w+=c(x)+"'/>\n			";return w}function r(z,y){var w="",x;w+="\n				<input class='parameter required' minlength='1' name='";if(x=o.name){x=x.call(z,{hash:{},data:y})}else{x=z.name;x=typeof x===d?x.apply(z):x}w+=c(x)+"' placeholder='(required)' type='text' value=''/>\n			";return w}p+="<td class='code required'>";if(g=o.name){g=g.call(q,{hash:{},data:u})}else{g=q.name;g=typeof g===d?g.apply(q):g}p+=c(g)+"</td>\n<td>\n	";g=o["if"].call(q,q.isBody,{hash:{},inverse:n.program(9,e,u),fn:n.program(1,m,u),data:u});if(g||g===0){p+=g}p+="\n</td>\n<td>\n	<strong>";if(g=o.description){g=g.call(q,{hash:{},data:u})}else{g=q.description;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+="</strong>\n</td>\n<td>";if(g=o.paramType){g=g.call(q,{hash:{},data:u})}else{g=q.paramType;g=typeof g===d?g.apply(q):g}if(g||g===0){p+=g}p+='</td>\n<td><span class="model-signature"></span></td>\n';return p})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.parameter_content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.consumes,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n  <option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n  ";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="parameterContentType"></label>\n<select name="parameterContentType">\n';c=f["if"].call(l,l.consumes,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.resource=b(function(g,m,f,l,k){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);k=k||{};var i="",d,p,h="function",j=this.escapeExpression,o=this,n=f.blockHelperMissing;function e(r,q){return" : "}function c(t,s){var q="",r;q+="<li>\n      <a href='";if(r=f.url){r=r.call(t,{hash:{},data:s})}else{r=t.url;r=typeof r===h?r.apply(t):r}q+=j(r)+"'>Raw</a>\n    </li>";return q}i+="<div class='heading'>\n  <h2>\n    <a href='#!/";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'\' class="toggleEndpointList" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">';if(d=f.name){d=d.call(m,{hash:{},data:k})}else{d=m.name;d=typeof d===h?d.apply(m):d}i+=j(d)+"</a> ";p={hash:{},inverse:o.noop,fn:o.program(1,e,k),data:k};if(d=f.summary){d=d.call(m,p)}else{d=m.summary;d=typeof d===h?d.apply(m):d}if(!f.summary){d=n.call(m,d,p)}if(d||d===0){i+=d}if(d=f.summary){d=d.call(m,{hash:{},data:k})}else{d=m.summary;d=typeof d===h?d.apply(m):d}if(d||d===0){i+=d}i+="\n  </h2>\n  <ul class='options'>\n    <li>\n      <a href='#!/";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+"' id='endpointListTogger_";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'\' class="toggleEndpointList" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">Show/Hide</a>\n    </li>\n    <li>\n      <a href=\'#\' class="collapseResource" data-id="';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+'">\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href=\'#\' class="expandResource" data-id=';if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+">\n        Expand Operations\n      </a>\n    </li>\n    ";p={hash:{},inverse:o.noop,fn:o.program(3,c,k),data:k};if(d=f.url){d=d.call(m,p)}else{d=m.url;d=typeof d===h?d.apply(m):d}if(!f.url){d=n.call(m,d,p)}if(d||d===0){i+=d}i+="\n  </ul>\n</div>\n<ul class='endpoints' id='";if(d=f.id){d=d.call(m,{hash:{},data:k})}else{d=m.id;d=typeof d===h?d.apply(m):d}i+=j(d)+"_endpoint_list' style='display:none'>\n\n</ul>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.response_content_type=b(function(g,l,f,k,j){this.compilerInfo=[4,">= 1.0.0"];f=this.merge(f,g.helpers);j=j||{};var i="",c,h="function",m=this;function e(r,q){var o="",p;o+="\n  ";p=f.each.call(r,r.produces,{hash:{},inverse:m.noop,fn:m.program(2,d,q),data:q});if(p||p===0){o+=p}o+="\n";return o}function d(r,q){var o="",p;o+='\n  <option value="';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+='">';p=(typeof r===h?r.apply(r):r);if(p||p===0){o+=p}o+="</option>\n  ";return o}function n(p,o){return'\n  <option value="application/json">application/json</option>\n'}i+='<label for="responseContentType"></label>\n<select name="responseContentType">\n';c=f["if"].call(l,l.produces,{hash:{},inverse:m.program(4,n,j),fn:m.program(1,e,j),data:j});if(c||c===0){i+=c}i+="\n</select>\n";return i})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.signature=b(function(e,k,d,j,i){this.compilerInfo=[4,">= 1.0.0"];d=this.merge(d,e.helpers);i=i||{};var g="",c,f="function",h=this.escapeExpression;g+='<div>\n<ul class="signature-nav">\n  <li><a class="description-link" href="#">Model</a></li>\n  <li><a class="snippet-link" href="#">Model Schema</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n  <div class="description">\n    ';if(c=d.signature){c=c.call(k,{hash:{},data:i})}else{c=k.signature;c=typeof c===f?c.apply(k):c}if(c||c===0){g+=c}g+='\n  </div>\n\n  <div class="snippet">\n    <pre><code>';if(c=d.sampleJSON){c=c.call(k,{hash:{},data:i})}else{c=k.sampleJSON;c=typeof c===f?c.apply(k):c}g+=h(c)+'</code></pre>\n    <small class="notice"></small>\n  </div>\n</div>\n\n';return g})})();(function(){var b=Handlebars.template,a=Handlebars.templates=Handlebars.templates||{};a.status_code=b(function(e,k,d,j,i){this.compilerInfo=[4,">= 1.0.0"];d=this.merge(d,e.helpers);i=i||{};var g="",c,f="function",h=this.escapeExpression;g+="<td width='15%' class='code'>";if(c=d.code){c=c.call(k,{hash:{},data:i})}else{c=k.code;c=typeof c===f?c.apply(k):c}g+=h(c)+"</td>\n<td>";if(c=d.message){c=c.call(k,{hash:{},data:i})}else{c=k.message;c=typeof c===f?c.apply(k):c}if(c||c===0){g+=c}g+="</td>\n<td width='50%'><span class=\"model-signature\" /></td>";return g})})();(function(){var j,r,u,o,l,k,n,m,i,p,s,q,h,c,g,f,e,d,b,a,x,w,t={}.hasOwnProperty,v=function(B,z){for(var y in z){if(t.call(z,y)){B[y]=z[y]}}function A(){this.constructor=B}A.prototype=z.prototype;B.prototype=new A();B.__super__=z.prototype;return B};s=(function(z){v(y,z);function y(){q=y.__super__.constructor.apply(this,arguments);return q}y.prototype.dom_id="swagger_ui";y.prototype.options=null;y.prototype.api=null;y.prototype.headerView=null;y.prototype.mainView=null;y.prototype.initialize=function(A){var B=this;if(A==null){A={}}if(A.dom_id!=null){this.dom_id=A.dom_id;delete A.dom_id}if($("#"+this.dom_id)==null){$("body").append('<div id="'+this.dom_id+'"></div>')}this.options=A;this.options.success=function(){return B.render()};this.options.progress=function(C){return B.showMessage(C)};this.options.failure=function(C){if(B.api&&B.api.isValid===false){log("not a valid 2.0 spec, loading legacy client");B.api=new SwaggerApi(B.options);return B.api.build()}else{return B.onLoadFailure(C)}};this.headerView=new r({el:$("#header")});return this.headerView.on("update-swagger-ui",function(C){return B.updateSwaggerUi(C)})};y.prototype.updateSwaggerUi=function(A){this.options.url=A.url;return this.load()};y.prototype.load=function(){var B,A;if((A=this.mainView)!=null){A.clear()}B=this.options.url;if(B.indexOf("http")!==0){B=this.buildUrl(window.location.href.toString(),B)}this.options.url=B;this.headerView.update(B);this.api=new SwaggerClient(this.options);return this.api.build()};y.prototype.render=function(){var A=this;this.showMessage("Finished Loading Resource Information. Rendering Swagger UI...");this.mainView=new u({model:this.api,el:$("#"+this.dom_id),swaggerOptions:this.options}).render();this.showMessage();switch(this.options.docExpansion){case"full":Docs.expandOperationsForResource("");break;case"list":Docs.collapseOperationsForResource("")}if(this.options.onComplete){this.options.onComplete(this.api,this)}return setTimeout(function(){return Docs.shebang()},400)};y.prototype.buildUrl=function(C,A){var B,D;log("base is "+C);if(A.indexOf("/")===0){D=C.split("/");C=D[0]+"//"+D[2];return C+A}else{B=C.length;if(C.indexOf("?")>-1){B=Math.min(B,C.indexOf("?"))}if(C.indexOf("#")>-1){B=Math.min(B,C.indexOf("#"))}C=C.substring(0,B);if(C.indexOf("/",C.length-1)!==-1){return C+A}return C+"/"+A}};y.prototype.showMessage=function(A){if(A==null){A=""}$("#message-bar").removeClass("message-fail");$("#message-bar").addClass("message-success");return $("#message-bar").html(A)};y.prototype.onLoadFailure=function(A){var B;if(A==null){A=""}$("#message-bar").removeClass("message-success");$("#message-bar").addClass("message-fail");B=$("#message-bar").html(A);if(this.options.onFailure!=null){this.options.onFailure(A)}return B};return y})(Backbone.Router);window.SwaggerUi=s;r=(function(z){v(y,z);function y(){h=y.__super__.constructor.apply(this,arguments);return h}y.prototype.events={"click #show-pet-store-icon":"showPetStore","click #show-wordnik-dev-icon":"showWordnikDev","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"};y.prototype.initialize=function(){};y.prototype.showPetStore=function(A){return this.trigger("update-swagger-ui",{url:"http://petstore.swagger.wordnik.com/api/api-docs"})};y.prototype.showWordnikDev=function(A){return this.trigger("update-swagger-ui",{url:"http://api.wordnik.com/v4/resources.json"})};y.prototype.showCustomOnKeyup=function(A){if(A.keyCode===13){return this.showCustom()}};y.prototype.showCustom=function(A){if(A!=null){A.preventDefault()}return this.trigger("update-swagger-ui",{url:$("#input_baseUrl").val(),apiKey:$("#input_apiKey").val()})};y.prototype.update=function(B,C,A){if(A==null){A=false}$("#input_baseUrl").val(B);if(A){return this.trigger("update-swagger-ui",{url:B})}};return y})(Backbone.View);u=(function(y){var z;v(A,y);function A(){g=A.__super__.constructor.apply(this,arguments);return g}z={alpha:function(C,B){return C.path.localeCompare(B.path)},method:function(C,B){return C.method.localeCompare(B.method)}};A.prototype.initialize=function(B){var C,I,J,E,D,G,H,F;if(B==null){B={}}if(B.swaggerOptions.sorter){E=B.swaggerOptions.sorter;J=z[E];if(this.model.apisArray){F=this.model.apisArray;for(G=0,H=F.length;G<H;G++){I=F[G];I.operationsArray.sort(J)}if(E==="alpha"){this.model.apisArray.sort(J)}}}if(this.model.info&&this.model.info.license&&typeof this.model.info.license==="string"){C=this.model.info.license;D=this.model.info.licenseUrl;this.model.info.license={};this.model.info.license.name=C;this.model.info.license.url=D}if(!this.model.info){this.model.info={}}if(!this.model.info.version){this.model.info.version=this.model.apiVersion}if(this.model.url.indexOf("http://localhost")===-1&&this.model.swaggerVersion===2){return this.model.validatorUrl=this.model.url}};A.prototype.render=function(){var C,H,E,F,D,B,G;$(this.el).html(Handlebars.templates.main(this.model));F={};C=0;G=this.model.apisArray;for(D=0,B=G.length;D<B;D++){E=G[D];H=E.name;while(typeof F[H]!=="undefined"){H=H+"_"+C;C+=1}E.id=H;F[H]=E;this.addResource(E)}return this};A.prototype.addResource=function(C){var B;C.id=C.id.replace(/\s/g,"_");B=new n({model:C,tagName:"li",id:"resource_"+C.id,className:"resource",swaggerOptions:this.options.swaggerOptions});return $("#resources").append(B.render().el)};A.prototype.clear=function(){return $(this.el).html("")};return A})(Backbone.View);n=(function(z){v(y,z);function y(){f=y.__super__.constructor.apply(this,arguments);return f}y.prototype.initialize=function(){if(""===this.model.description){return this.model.description=null}};y.prototype.render=function(){var B,G,D,C,E,A,F;$(this.el).html(Handlebars.templates.resource(this.model));D={};if(this.model.description){this.model.summary=this.model.description}F=this.model.operationsArray;for(E=0,A=F.length;E<A;E++){C=F[E];B=0;G=C.nickname;while(typeof D[G]!=="undefined"){G=G+"_"+B;B+=1}D[G]=C;C.nickname=G;C.parentId=this.model.id;this.addOperation(C)}$(".toggleEndpointList",this.el).click(this.callDocs.bind(this,"toggleEndpointListForResource"));$(".collapseResource",this.el).click(this.callDocs.bind(this,"collapseOperationsForResource"));$(".expandResource",this.el).click(this.callDocs.bind(this,"expandOperationsForResource"));return this};y.prototype.addOperation=function(A){var B;A.number=this.number;B=new o({model:A,tagName:"li",className:"endpoint",swaggerOptions:this.options.swaggerOptions});$(".endpoints",$(this.el)).append(B.render().el);return this.number++};y.prototype.callDocs=function(B,A){A.preventDefault();return Docs[B](A.currentTarget.getAttribute("data-id"))};return y})(Backbone.View);o=(function(z){v(y,z);function y(){e=y.__super__.constructor.apply(this,arguments);return e}y.prototype.invocationUrl=null;y.prototype.events={"submit .sandbox":"submitOperation","click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent","mouseenter .api-ic":"mouseEnter","mouseout .api-ic":"mouseExit"};y.prototype.initialize=function(){};y.prototype.mouseEnter=function(F){var D,E,I,B,A,J,G,C,K,H;D=$(F.currentTarget.parentNode).find("#api_information_panel");K=F.pageX;H=F.pageY;J=$(window).scrollLeft();G=$(window).scrollTop();B=J+$(window).width();A=G+$(window).height();C=D.width();E=D.height();if(K+C>B){K=B-C}if(K<J){K=J}if(H+E>A){H=A-E}if(H<G){H=G}I={};I.top=H;I.left=K;D.css(I);return $(F.currentTarget.parentNode).find("#api_information_panel").show()};y.prototype.mouseExit=function(A){return $(A.currentTarget.parentNode).find("#api_information_panel").hide()};y.prototype.render=function(){var D,U,V,T,R,K,J,Q,L,P,W,O,M,I,N,S,H,G,F,C,Y,ab,Z,X,E,B,A,ac,aa;V=true;if(!V){this.model.isReadOnly=true}this.model.description=this.model.description||this.model.notes;if(this.model.description){this.model.description=this.model.description.replace(/(?:\r\n|\r|\n)/g,"<br />")}this.model.oauth=null;if(this.model.authorizations){E=this.model.authorizations;for(T in E){N=E[T];if(T==="oauth2"){if(this.model.oauth===null){this.model.oauth={}}if(this.model.oauth.scopes===void 0){this.model.oauth.scopes=[]}for(H=0,Y=N.length;H<Y;H++){R=N[H];this.model.oauth.scopes.push(R)}}}}if(typeof this.model.responses!=="undefined"){this.model.responseMessages=[];B=this.model.responses;for(D in B){S=B[D];P=null;W=this.model.responses[D].schema;if(W&&W["$ref"]){P=W["$ref"];if(P.indexOf("#/definitions/")===0){P=P.substring("#/definitions/".length)}}this.model.responseMessages.push({code:D,message:S.description,responseModel:P})}}if(typeof this.model.responseMessages==="undefined"){this.model.responseMessages=[]}$(this.el).html(Handlebars.templates.operation(this.model));if(this.model.responseClassSignature&&this.model.responseClassSignature!=="string"){O={sampleJSON:this.model.responseSampleJSON,isParam:false,signature:this.model.responseClassSignature};L=new i({model:O,tagName:"div"});$(".model-signature",$(this.el)).append(L.render().el)}else{this.model.responseClassSignature="string";$(".model-signature",$(this.el)).html(this.model.type)}U={isParam:false};U.consumes=this.model.consumes;U.produces=this.model.produces;A=this.model.parameters;for(G=0,ab=A.length;G<ab;G++){K=A[G];I=K.type||K.dataType;if(typeof I==="undefined"){P=K.schema;if(P&&P["$ref"]){J=P["$ref"];if(J.indexOf("#/definitions/")===0){I=J.substring("#/definitions/".length)}else{I=J}}}if(I&&I.toLowerCase()==="file"){if(!U.consumes){U.consumes="multipart/form-data"}}K.type=I}Q=new m({model:U});$(".response-content-type",$(this.el)).append(Q.render().el);ac=this.model.parameters;for(F=0,Z=ac.length;F<Z;F++){K=ac[F];this.addParameter(K,U.consumes)}aa=this.model.responseMessages;for(C=0,X=aa.length;C<X;C++){M=aa[C];this.addStatusCode(M)}return this};y.prototype.addParameter=function(C,A){var B;C.consumes=A;B=new k({model:C,tagName:"tr",readOnly:this.model.isReadOnly});return $(".operation-params",$(this.el)).append(B.render().el)};y.prototype.addStatusCode=function(B){var A;A=new p({model:B,tagName:"tr"});return $(".operation-status",$(this.el)).append(A.render().el)};y.prototype.submitOperation=function(O){var Q,G,N,D,I,A,J,M,L,K,P,F,C,H,E,B;if(O!=null){O.preventDefault()}G=$(".sandbox",$(this.el));Q=true;G.find("input.required").each(function(){var R=this;$(this).removeClass("error");if(jQuery.trim($(this).val())===""){$(this).addClass("error");$(this).wiggle({callback:function(){return $(R).focus()}});return Q=false}});if(Q){D={};A={parent:this};N=false;H=G.find("input");for(M=0,P=H.length;M<P;M++){I=H[M];if((I.value!=null)&&jQuery.trim(I.value).length>0){D[I.name]=I.value}if(I.type==="file"){N=true}}E=G.find("textarea");for(L=0,F=E.length;L<F;L++){I=E[L];if((I.value!=null)&&jQuery.trim(I.value).length>0){D.body=I.value}}B=G.find("select");for(K=0,C=B.length;K<C;K++){I=B[K];J=this.getSelectedValue(I);if((J!=null)&&jQuery.trim(J).length>0){D[I.name]=J}}A.responseContentType=$("div select[name=responseContentType]",$(this.el)).val();A.requestContentType=$("div select[name=parameterContentType]",$(this.el)).val();$(".response_throbber",$(this.el)).show();if(N){return this.handleFileUpload(D,G)}else{return this.model["do"](D,A,this.showCompleteStatus,this.showErrorStatus,this)}}};y.prototype.success=function(A,B){return B.showCompleteStatus(A)};y.prototype.handleFileUpload=function(R,I){var M,H,C,N,L,K,P,J,G,F,D,Q,U,T,S,E,B,A,V,O=this;E=I.serializeArray();for(J=0,Q=E.length;J<Q;J++){N=E[J];if((N.value!=null)&&jQuery.trim(N.value).length>0){R[N.name]=N.value}}M=new FormData();P=0;B=this.model.parameters;for(G=0,U=B.length;G<U;G++){K=B[G];if(K.paramType==="form"){if(K.type.toLowerCase()!=="file"&&R[K.name]!==void 0){M.append(K.name,R[K.name])}}}C={};A=this.model.parameters;for(F=0,T=A.length;F<T;F++){K=A[F];if(K.paramType==="header"){C[K.name]=R[K.name]}}V=I.find('input[type~="file"]');for(D=0,S=V.length;D<S;D++){H=V[D];if(typeof H.files[0]!=="undefined"){M.append($(H).attr("name"),H.files[0]);P+=1}}this.invocationUrl=this.model.supportHeaderParams()?(C=this.model.getHeaderParams(R),this.model.urlify(R,false)):this.model.urlify(R,true);$(".request_url",$(this.el)).html("<pre></pre>");$(".request_url pre",$(this.el)).text(this.invocationUrl);L={type:this.model.method,url:this.invocationUrl,headers:C,data:M,dataType:"json",contentType:false,processData:false,error:function(X,Y,W){return O.showErrorStatus(O.wrap(X),O)},success:function(W){return O.showResponse(W,O)},complete:function(W){return O.showCompleteStatus(O.wrap(W),O)}};if(window.authorizations){window.authorizations.apply(L)}if(P===0){L.data.append("fake","true")}jQuery.ajax(L);return false};y.prototype.wrap=function(E){var C,F,H,B,G,D,A;H={};F=E.getAllResponseHeaders().split("\r");for(D=0,A=F.length;D<A;D++){B=F[D];C=B.split(":");if(C[0]!==void 0&&C[1]!==void 0){H[C[0].trim()]=C[1].trim()}}G={};G.content={};G.content.data=E.responseText;G.headers=H;G.request={};G.request.url=this.invocationUrl;G.status=E.status;return G};y.prototype.getSelectedValue=function(A){var D,C,F,B,E;if(!A.multiple){return A.value}else{C=[];E=A.options;for(F=0,B=E.length;F<B;F++){D=E[F];if(D.selected){C.push(D.value)}}if(C.length>0){return C.join(",")}else{return null}}};y.prototype.hideResponse=function(A){if(A!=null){A.preventDefault()}$(".response",$(this.el)).slideUp();return $(".response_hider",$(this.el)).fadeOut()};y.prototype.showResponse=function(A){var B;B=JSON.stringify(A,null,"\t").replace(/\n/g,"<br>");return $(".response_body",$(this.el)).html(escape(B))};y.prototype.showErrorStatus=function(B,A){return A.showStatus(B)};y.prototype.showCompleteStatus=function(B,A){return A.showStatus(B)};y.prototype.formatXml=function(H){var D,G,B,I,N,J,C,A,L,M,F,E,K;A=/(>)(<)(\/*)/g;M=/[ ]*(.*)[ ]+\n/g;D=/(<.+>)(.+\n)/g;H=H.replace(A,"$1\n$2$3").replace(M,"$1\n").replace(D,"$1\n$2");C=0;G="";N=H.split("\n");B=0;I="other";L={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0};F=function(T){var P,O,R,V,S,Q,U;Q={single:Boolean(T.match(/<.+\/>/)),closing:Boolean(T.match(/<\/.+>/)),opening:Boolean(T.match(/<[^!?].*>/))};S=((function(){var W;W=[];for(R in Q){U=Q[R];if(U){W.push(R)}}return W})())[0];S=S===void 0?"other":S;P=I+"->"+S;I=S;V="";B+=L[P];V=((function(){var X,Y,W;W=[];for(O=X=0,Y=B;0<=Y?X<Y:X>Y;O=0<=Y?++X:--X){W.push("  ")}return W})()).join("");if(P==="opening->closing"){return G=G.substr(0,G.length-1)+T+"\n"}else{return G+=V+T+"\n"}};for(E=0,K=N.length;E<K;E++){J=N[E];F(J)}return G};y.prototype.showStatus=function(F){var C,J,L,I,D,M,A,E,H,G,B;if(F.content===void 0){J=F.data;B=F.url}else{J=F.content.data;B=F.request.url}D=F.headers;L=D&&D["Content-Type"]?D["Content-Type"].split(";")[0].trim():null;if(!J){C=$("<code />").text("no content");E=$('<pre class="json" />').append(C)}else{if(L==="application/json"||/\+json$/.test(L)){M=null;try{M=JSON.stringify(JSON.parse(J),null,"  ")}catch(K){I=K;M="can't parse JSON.  Raw result:\n\n"+J}C=$("<code />").text(M);E=$('<pre class="json" />').append(C)}else{if(L==="application/xml"||/\+xml$/.test(L)){C=$("<code />").text(this.formatXml(J));E=$('<pre class="xml" />').append(C)}else{if(L==="text/html"){C=$("<code />").html(_.escape(J));E=$('<pre class="xml" />').append(C)}else{if(/^image\//.test(L)){E=$("<img>").attr("src",B)}else{C=$("<code />").text(J);E=$('<pre class="json" />').append(C)}}}}}H=E;$(".request_url",$(this.el)).html("<pre></pre>");$(".request_url pre",$(this.el)).text(B);$(".response_code",$(this.el)).html("<pre>"+F.status+"</pre>");$(".response_body",$(this.el)).html(H);$(".response_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(F.headers,null,"  ")).replace(/\n/g,"<br>")+"</pre>");$(".response",$(this.el)).slideDown();$(".response_hider",$(this.el)).show();$(".response_throbber",$(this.el)).hide();G=$(".response_body",$(this.el))[0];A=this.options.swaggerOptions;if(A.highlightSizeThreshold&&F.data.length>A.highlightSizeThreshold){return G}else{return hljs.highlightBlock(G)}};y.prototype.toggleOperationContent=function(){var A;A=$("#"+Docs.escapeResourceName(this.model.parentId)+"_"+this.model.nickname+"_content");if(A.is(":visible")){return Docs.collapseOperation(A)}else{return Docs.expandOperation(A)}};return y})(Backbone.View);p=(function(z){v(y,z);function y(){d=y.__super__.constructor.apply(this,arguments);return d}y.prototype.initialize=function(){};y.prototype.render=function(){var B,A,C;C=this.template();$(this.el).html(C(this.model));if(swaggerUi.api.models.hasOwnProperty(this.model.responseModel)){B={sampleJSON:JSON.stringify(swaggerUi.api.models[this.model.responseModel].createJSONSample(),null,2),isParam:false,signature:swaggerUi.api.models[this.model.responseModel].getMockSignature()};A=new i({model:B,tagName:"div"});$(".model-signature",this.$el).append(A.render().el)}else{$(".model-signature",this.$el).html("")}return this};y.prototype.template=function(){return Handlebars.templates.status_code};return y})(Backbone.View);k=(function(z){v(y,z);function y(){b=y.__super__.constructor.apply(this,arguments);return b}y.prototype.initialize=function(){return Handlebars.registerHelper("isArray",function(B,A){if(B.type.toLowerCase()==="array"||B.allowMultiple){return A.fn(this)}else{return A.inverse(this)}})};y.prototype.render=function(){var A,B,E,C,F,D,I,J,H,G;G=this.model.type||this.model.dataType;if(typeof G==="undefined"){D=this.model.schema;if(D&&D["$ref"]){C=D["$ref"];if(C.indexOf("#/definitions/")===0){G=C.substring("#/definitions/".length)}else{G=C}}}this.model.type=G;this.model.paramType=this.model["in"]||this.model.paramType;if(this.model.paramType==="body"){this.model.isBody=true}if(G&&G.toLowerCase()==="file"){this.model.isFile=true}this.model["default"]=this.model["default"]||this.model.defaultValue;H=this.template();$(this.el).html(H(this.model));I={sampleJSON:this.model.sampleJSON,isParam:true,signature:this.model.signature};if(this.model.sampleJSON){J=new i({model:I,tagName:"div"});$(".model-signature",$(this.el)).append(J.render().el)}else{$(".model-signature",$(this.el)).html(this.model.signature)}B=false;if(this.model.isBody){B=true}A={isParam:B};A.consumes=this.model.consumes;if(B){E=new l({model:A});$(".parameter-content-type",$(this.el)).append(E.render().el)}else{F=new m({model:A});$(".response-content-type",$(this.el)).append(F.render().el)}return this};y.prototype.template=function(){if(this.model.isList){return Handlebars.templates.param_list}else{if(this.options.readOnly){if(this.model.required){return Handlebars.templates.param_readonly_required}else{return Handlebars.templates.param_readonly}}else{if(this.model.required){return Handlebars.templates.param_required}else{return Handlebars.templates.param}}}};return y})(Backbone.View);i=(function(z){v(y,z);function y(){a=y.__super__.constructor.apply(this,arguments);return a}y.prototype.events={"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet":"snippetToTextArea"};y.prototype.initialize=function(){};y.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));this.switchToSnippet();this.isParam=this.model.isParam;if(this.isParam){$(".notice",$(this.el)).text("Click to set as parameter value")}return this};y.prototype.template=function(){return Handlebars.templates.signature};y.prototype.switchToDescription=function(A){if(A!=null){A.preventDefault()}$(".snippet",$(this.el)).hide();$(".description",$(this.el)).show();$(".description-link",$(this.el)).addClass("selected");return $(".snippet-link",$(this.el)).removeClass("selected")};y.prototype.switchToSnippet=function(A){if(A!=null){A.preventDefault()}$(".description",$(this.el)).hide();$(".snippet",$(this.el)).show();$(".snippet-link",$(this.el)).addClass("selected");return $(".description-link",$(this.el)).removeClass("selected")};y.prototype.snippetToTextArea=function(A){var B;if(this.isParam){if(A!=null){A.preventDefault()}B=$("textarea",$(this.el.parentNode.parentNode.parentNode));if($.trim(B.val())===""){return B.val(this.model.sampleJSON)}}};return y})(Backbone.View);j=(function(y){v(z,y);function z(){x=z.__super__.constructor.apply(this,arguments);return x}z.prototype.initialize=function(){};z.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=contentType]",$(this.el)).text("Response Content Type");return this};z.prototype.template=function(){return Handlebars.templates.content_type};return z})(Backbone.View);m=(function(y){v(z,y);function z(){w=z.__super__.constructor.apply(this,arguments);return w}z.prototype.initialize=function(){};z.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=responseContentType]",$(this.el)).text("Response Content Type");return this};z.prototype.template=function(){return Handlebars.templates.response_content_type};return z})(Backbone.View);l=(function(z){v(y,z);function y(){c=y.__super__.constructor.apply(this,arguments);return c}y.prototype.initialize=function(){};y.prototype.render=function(){var A;A=this.template();$(this.el).html(A(this.model));$("label[for=parameterContentType]",$(this.el)).text("Parameter content type:");return this};y.prototype.template=function(){return Handlebars.templates.parameter_content_type};return y})(Backbone.View)}).call(this);
\ No newline at end of file
+(function(){function e(){e.history=e.history||[],e.history.push(arguments),this.console&&console.log(Array.prototype.slice.call(arguments)[0])}this.Handlebars=this.Handlebars||{},this.Handlebars.templates=this.Handlebars.templates||{},this.Handlebars.templates.apikey_auth=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <span class="key_auth__value">'+s((r=null!=(r=t.value||(null!=e?e.value:e))?r:o,typeof r===a?r.call(e,{name:"value",hash:{},data:i}):r))+"</span>\n"},3:function(e,t,n,i){return'                <input placeholder="api_key" class="auth_input input_apiKey_entry" name="apiKey" type="text"/>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<div class="key_input_container">\n    <h3 class="auth__title">Api key authorization</h3>\n    <div class="auth__description">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</div>\n    <div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">name:</span>\n            <span class="key_auth__value">'+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+'</span>\n        </div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">in:</span>\n            <span class="key_auth__value">'+l((a=null!=(a=t["in"]||(null!=e?e["in"]:e))?a:s,typeof a===o?a.call(e,{name:"in",hash:{},data:i}):a))+'</span>\n        </div>\n        <div class="key_auth__field">\n            <span class="key_auth__label">value:</span>\n';return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+"        </div>\n    </div>\n</div>\n"},useData:!0}),this.Handlebars.templates.auth_button_operation=Handlebars.template({1:function(e,t,n,i){return"        authorize__btn_operation_login\n"},3:function(e,t,n,i){return"        authorize__btn_operation_logout\n"},5:function(e,t,n,i){var r,a='        <ul class="authorize-scopes">\n';return r=t.each.call(e,null!=e?e.scopes:e,{name:"each",hash:{},fn:this.program(6,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"        </ul>\n"},6:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <li class="authorize__scope" title="'+s((r=null!=(r=t.description||(null!=e?e.description:e))?r:o,typeof r===a?r.call(e,{name:"description",hash:{},data:i}):r))+'">'+s((r=null!=(r=t.scope||(null!=e?e.scope:e))?r:o,typeof r===a?r.call(e,{name:"scope",hash:{},data:i}):r))+"</li>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a='<div class="authorize__btn authorize__btn_operation\n';return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(a+=r),a+='">\n',r=t["if"].call(e,null!=e?e.scopes:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"</div>\n"},useData:!0}),this.Handlebars.templates.auth_button=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){return"<a class='authorize__btn' href=\"#\">Authorize</a>\n"},useData:!0}),this.Handlebars.templates.auth_view=Handlebars.template({1:function(e,t,n,i){return'            <button type="button" class="auth__button auth_submit__button" data-sw-translate>Authorize</button>\n'},3:function(e,t,n,i){return'            <button type="button" class="auth__button auth_logout__button" data-sw-translate>Logout</button>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a='<div class="auth_container">\n\n    <div class="auth_inner"></div>\n    <div class="auth_submit">\n';return r=t.unless.call(e,null!=e?e.isLogout:e,{name:"unless",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(a+=r),r=t["if"].call(e,null!=e?e.isAuthorized:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"    </div>\n\n</div>\n"},useData:!0}),this.Handlebars.templates.basic_auth=Handlebars.template({1:function(e,t,n,i){return" - authorized"},3:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'                <span class="basic_auth__value">'+s((r=null!=(r=t.username||(null!=e?e.username:e))?r:o,typeof r===a?r.call(e,{name:"username",hash:{},data:i}):r))+"</span>\n"},5:function(e,t,n,i){return'                <input required placeholder="username" class="basic_auth__username auth_input" name="username" type="text"/>\n'},7:function(e,t,n,i){return'            <div class="auth_label">\n                <span class="basic_auth__label" data-sw-translate>password:</span>\n                <input required placeholder="password" class="basic_auth__password auth_input" name="password" type="password"/></label>\n            </div>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<div class='basic_auth_container'>\n    <h3 class=\"auth__title\">Basic authentication";return r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='</h3>\n    <form class="basic_input_container">\n        <div class="auth__description">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</div>\n        <div class="auth_label">\n            <span class="basic_auth__label" data-sw-translate>username:</span>\n',r=t["if"].call(e,null!=e?e.isLogout:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.program(5,i),data:i}),null!=r&&(u+=r),u+="        </div>\n",r=t.unless.call(e,null!=e?e.isLogout:e,{name:"unless",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </form>\n</div>\n"},useData:!0}),this.Handlebars.templates.content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'	<option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label data-sw-translate for="'+l((a=null!=(a=t.contentTypeId||(null!=e?e.contentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"contentTypeId",hash:{},data:i}):a))+'">Response Content Type</label>\n<select name="contentType" id="'+l((a=null!=(a=t.contentTypeId||(null!=e?e.contentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"contentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),$(function(){$.fn.vAlign=function(){return this.each(function(){var e=$(this).height(),t=$(this).parent().height(),n=(t-e)/2;$(this).css("margin-top",n)})},$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(){var e=$(this).closest("form").innerWidth(),t=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10),n=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",e-t-n)})},$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent(),$("ul.downplayed li div.content p").vAlign(),$("form.sandbox").submit(function(){var e=!0;return $(this).find("input.required").each(function(){$(this).removeClass("error"),""===$(this).val()&&($(this).addClass("error"),$(this).wiggle(),e=!1)}),e})}),Function.prototype.bind&&console&&"object"==typeof console.log&&["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(e){console[e]=this.bind(console[e],console)},Function.prototype.call),window.Docs={shebang:function(){var e=$.param.fragment().split("/");switch(e.shift(),e.length){case 1:if(e[0].length>0){var t="resource_"+e[0];Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1})}break;case 2:Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1});var n=e.join("_"),i=n+"_content";Docs.expandOperation($("#"+i)),$("#"+n).slideto({highlight:!1})}},toggleEndpointListForResource:function(e){var t=$("li#resource_"+Docs.escapeResourceName(e)+" ul.endpoints");t.is(":visible")?($.bbq.pushState("#/",2),Docs.collapseEndpointListForResource(e)):($.bbq.pushState("#/"+e,2),Docs.expandEndpointListForResource(e))},expandEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideDown();$("li#resource_"+e).addClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideDown()},collapseEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideUp();$("li#resource_"+e).removeClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideUp()},expandOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideDown():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideUp():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(e){return e.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(e){e.slideDown()},collapseOperation:function(e){e.slideUp()}},Handlebars.registerHelper("sanitize",function(e){return e=e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,""),new Handlebars.SafeString(e)}),Handlebars.registerHelper("renderTextParam",function(e){var t,n="text",i="",r=e.type||e.schema.type||"",a="array"===r.toLowerCase()||e.allowMultiple,o=a&&Array.isArray(e["default"])?e["default"].join("\n"):e["default"],s=Object.keys(e).filter(function(e){return null!==e.match(/^X-data-/i)}).reduce(function(t,n){return t+=" "+n.substring(2,n.length)+"='"+e[n]+"'"},"");if("undefined"==typeof o&&(o=""),e.format&&"password"===e.format&&(n="password"),e.valueId&&(i=" id='"+e.valueId+"'"),("string"==typeof o||o instanceof String)&&(o=o.replace(/'/g,"&apos;")),a)t="<textarea class='body-textarea"+(e.required?" required":"")+"' name='"+e.name+"'"+i+s,t+=" placeholder='Provide multiple values in new lines"+(e.required?" (at least one required).":".")+"'>",t+=o+"</textarea>";else{var l="parameter";e.required&&(l+=" required"),t="<input class='"+l+"' minlength='"+(e.required?1:0)+"'",t+=" name='"+e.name+"' placeholder='"+(e.required?"(required)":"")+"'"+i+s,t+=" type='"+n+"' value='"+o+"'/>"}return new Handlebars.SafeString(t)}),Handlebars.registerHelper("ifCond",function(e,t,n,i){switch(t){case"==":return e==n?i.fn(this):i.inverse(this);case"===":return e===n?i.fn(this):i.inverse(this);case"<":return n>e?i.fn(this):i.inverse(this);case"<=":return n>=e?i.fn(this):i.inverse(this);case">":return e>n?i.fn(this):i.inverse(this);case">=":return e>=n?i.fn(this):i.inverse(this);case"&&":return e&&n?i.fn(this):i.inverse(this);case"||":return e||n?i.fn(this):i.inverse(this);default:return i.inverse(this)}}),this.Handlebars.templates.main=Handlebars.template({1:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression,s='  <div class="info_title">'+o(a(null!=(r=null!=e?e.info:e)?r.title:r,e))+'</div>\n  <div class="info_description markdown">';return r=a(null!=(r=null!=e?e.info:e)?r.description:r,e),null!=r&&(s+=r),s+="</div>\n",r=t["if"].call(e,null!=e?e.externalDocs:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="  ",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.termsOfServiceUrl:r,{name:"if",hash:{},fn:this.program(4,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.name:r,{name:"if",hash:{},fn:this.program(6,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,{name:"if",hash:{},fn:this.program(8,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.email:r,{name:"if",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+="\n  ",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.license:r,{name:"if",hash:{},fn:this.program(12,i),inverse:this.noop,data:i}),null!=r&&(s+=r),s+"\n"},2:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"  <p>"+o(a(null!=(r=null!=e?e.externalDocs:e)?r.description:r,e))+'</p>\n  <a href="'+o(a(null!=(r=null!=e?e.externalDocs:e)?r.url:r,e))+'" target="_blank">'+o(a(null!=(r=null!=e?e.externalDocs:e)?r.url:r,e))+"</a>\n"},4:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'<div class="info_tos"><a target="_blank" href="'+o(a(null!=(r=null!=e?e.info:e)?r.termsOfServiceUrl:r,e))+'" data-sw-translate>Terms of service</a></div>'},6:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div><div class='info_name' style=\"display: inline\" data-sw-translate>Created by </div> "+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.name:r,e))+"</div>"},8:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div class='info_url' data-sw-translate>See more at <a href=\""+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,e))+'">'+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.url:r,e))+"</a></div>"},10:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'<div class=\'info_email\'><a target="_parent" href="mailto:'+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.contact:r)?r.email:r,e))+"?subject="+o(a(null!=(r=null!=e?e.info:e)?r.title:r,e))+'" data-sw-translate>Contact the developer</a></div>'},12:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return"<div class='info_license'><a target=\"_blank\" href='"+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.license:r)?r.url:r,e))+"'>"+o(a(null!=(r=null!=(r=null!=e?e.info:e)?r.license:r)?r.name:r,e))+"</a></div>"},14:function(e,t,n,i){var r,a=this.lambda,o=this.escapeExpression;return'  , <span style="font-variant: small-caps" data-sw-translate>api version</span>: '+o(a(null!=(r=null!=e?e.info:e)?r.version:r,e))+"\n    "},16:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'    <span style="float:right"><a target="_blank" href="'+s((r=null!=(r=t.validatorUrl||(null!=e?e.validatorUrl:e))?r:o,typeof r===a?r.call(e,{name:"validatorUrl",hash:{},data:i}):r))+"/debug?url="+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+'"><img id="validator" src="'+s((r=null!=(r=t.validatorUrl||(null!=e?e.validatorUrl:e))?r:o,typeof r===a?r.call(e,{name:"validatorUrl",hash:{},data:i}):r))+"?url="+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+'"></a>\n    </span>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<div class='info' id='api_info'>\n";return r=t["if"].call(e,null!=e?e.info:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="</div>\n<div class='container' id='resources_container'>\n  <div class='authorize-wrapper'></div>\n\n  <ul id='resources'></ul>\n\n  <div class=\"footer\">\n    <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: "+l((a=null!=(a=t.basePath||(null!=e?e.basePath:e))?a:s,typeof a===o?a.call(e,{name:"basePath",hash:{},data:i}):a))+"\n",r=t["if"].call(e,null!=(r=null!=e?e.info:e)?r.version:r,{name:"if",hash:{},fn:this.program(14,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="]\n",r=t["if"].call(e,null!=e?e.validatorUrl:e,{name:"if",hash:{},fn:this.program(16,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </h4>\n    </div>\n</div>\n"},useData:!0}),this.Handlebars.templates.oauth2=Handlebars.template({1:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='            <li>\n                <input class="oauth-scope" type="checkbox" data-scope="'+l((a=null!=(a=t.scope||(null!=e?e.scope:e))?a:s,typeof a===o?a.call(e,{name:"scope",hash:{},data:i}):a))+'" oauthtype="'+l((a=null!=(a=t.OAuthSchemeKey||(null!=e?e.OAuthSchemeKey:e))?a:s,typeof a===o?a.call(e,{name:"OAuthSchemeKey",hash:{},data:i}):a))+'"/>\n                <label>'+l((a=null!=(a=t.scope||(null!=e?e.scope:e))?a:s,typeof a===o?a.call(e,{name:"scope",hash:{},data:i}):a))+'</label><br/>\n                <span class="api-scope-desc">'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+"\n";return r=t["if"].call(e,null!=e?e.OAuthSchemeKey:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"                </span>\n            </li>\n"},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"                        ("+s((r=null!=(r=t.OAuthSchemeKey||(null!=e?e.OAuthSchemeKey:e))?r:o,typeof r===a?r.call(e,{name:"OAuthSchemeKey",hash:{},data:i}):r))+")\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<div>\n    <h3 class="auth__title">Select OAuth2.0 Scopes</h3>\n    <p>'+l((a=null!=(a=t.description||(null!=e?e.description:e))?a:s,typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a))+'</p>\n    <p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.\n        <a href="#">Learn how to use</a>\n    </p>\n    <p><strong> '+l((a=null!=(a=t.appName||(null!=e?e.appName:e))?a:s,typeof a===o?a.call(e,{name:"appName",hash:{},data:i}):a))+" </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>\n    <p>Authorization URL: "+l((a=null!=(a=t.authorizationUrl||(null!=e?e.authorizationUrl:e))?a:s,typeof a===o?a.call(e,{name:"authorizationUrl",hash:{},data:i}):a))+"</p>\n    <p>flow: "+l((a=null!=(a=t.flow||(null!=e?e.flow:e))?a:s,typeof a===o?a.call(e,{name:"flow",hash:{},data:i}):a))+'</p>\n    <ul class="api-popup-scopes">\n';return r=t.each.call(e,null!=e?e.scopes:e,{name:"each",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </ul>\n</div>"},useData:!0}),this.Handlebars.templates.operation=Handlebars.template({1:function(e,t,n,i){return"deprecated"},3:function(e,t,n,i){return"            <h4><span data-sw-translate>Warning: Deprecated</span></h4>\n"},5:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='        <h4><span data-sw-translate>Implementation Notes</span></h4>\n        <div class="markdown">';return a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(l+=r),l+"</div>\n"},7:function(e,t,n,i){return"            <div class='authorize-wrapper authorize-wrapper_operation'></div>\n"},9:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='          <div class="response-class">\n            <h4><span data-sw-translate>Response Class</span> (<span data-sw-translate>Status</span> '+l((a=null!=(a=t.successCode||(null!=e?e.successCode:e))?a:s,typeof a===o?a.call(e,{name:"successCode",hash:{},data:i}):a))+")</h4>\n              ";return r=t["if"].call(e,null!=e?e.successDescription:e,{name:"if",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+'\n            <p><span class="model-signature" /></p>\n            <br/>\n            <div class="response-content-type" />\n            </div>\n'},10:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='<div class="markdown">';return a=null!=(a=t.successDescription||(null!=e?e.successDescription:e))?a:s,r=typeof a===o?a.call(e,{name:"successDescription",hash:{},data:i}):a,null!=r&&(l+=r),l+"</div>"},12:function(e,t,n,i){var r,a='          <h4 data-sw-translate>Headers</h4>\n          <table class="headers">\n            <thead>\n              <tr>\n                <th style="width: 100px; max-width: 100px" data-sw-translate>Header</th>\n                <th style="width: 310px; max-width: 310px" data-sw-translate>Description</th>\n                <th style="width: 200px; max-width: 200px" data-sw-translate>Type</th>\n                <th style="width: 320px; max-width: 320px" data-sw-translate>Other</th>\n              </tr>\n            </thead>\n            <tbody>\n';return r=t.each.call(e,null!=e?e.headers:e,{name:"each",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+"            </tbody>\n          </table>\n"},13:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return"              <tr>\n                <td>"+a(r(i&&i.key,e))+"</td>\n                <td>"+a(r(null!=e?e.description:e,e))+"</td>\n                <td>"+a(r(null!=e?e.type:e,e))+"</td>\n                <td>"+a(r(null!=e?e.other:e,e))+"</td>\n              </tr>\n"},15:function(e,t,n,i){return'          <h4 data-sw-translate>Parameters</h4>\n          <table class=\'fullwidth parameters\'>\n          <thead>\n            <tr>\n            <th style="width: 100px; max-width: 100px" data-sw-translate>Parameter</th>\n            <th style="width: 310px; max-width: 310px" data-sw-translate>Value</th>\n            <th style="width: 200px; max-width: 200px" data-sw-translate>Description</th>\n            <th style="width: 100px; max-width: 100px" data-sw-translate>Parameter Type</th>\n            <th style="width: 220px; max-width: 230px" data-sw-translate>Data Type</th>\n            </tr>\n          </thead>\n          <tbody class="operation-params">\n\n          </tbody>\n          </table>\n'},17:function(e,t,n,i){return"          <div style='margin:0;padding:0;display:inline'></div>\n          <h4 data-sw-translate>Response Messages</h4>\n          <table class='fullwidth response-messages'>\n            <thead>\n            <tr>\n              <th data-sw-translate>HTTP Status Code</th>\n              <th data-sw-translate>Reason</th>\n              <th data-sw-translate>Response Model</th>\n              <th data-sw-translate>Headers</th>\n            </tr>\n            </thead>\n            <tbody class=\"operation-status\">\n            </tbody>\n          </table>\n"},19:function(e,t,n,i){return""},21:function(e,t,n,i){return"          <div class='sandbox_header'>\n            <input class='submit' type='submit' value='Try it out!' data-sw-translate/>\n            <a href='#' class='response_hider' style='display:none' data-sw-translate>Hide Response</a>\n            <span class='response_throbber' style='display:none'></span>\n          </div>\n"},23:function(e,t,n,i){return"          <h4 data-sw-translate>Request Headers</h4>\n          <div class='block request_headers'></div>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="  <ul class='operations' >\n    <li class='"+l((a=null!=(a=t.method||(null!=e?e.method:e))?a:s,typeof a===o?a.call(e,{name:"method",hash:{},data:i}):a))+" operation' id='"+l((a=null!=(a=t.parentId||(null!=e?e.parentId:e))?a:s,typeof a===o?a.call(e,{name:"parentId",hash:{},data:i}):a))+"_"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"'>\n      <div class='heading'>\n        <h3>\n          <span class='http_method'>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+'\' class="toggleOperation">'+l((a=null!=(a=t.method||(null!=e?e.method:e))?a:s,typeof a===o?a.call(e,{name:"method",hash:{},data:i}):a))+"</a>\n          </span>\n          <span class='path'>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"' class=\"toggleOperation ";return r=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='">'+l((a=null!=(a=t.path||(null!=e?e.path:e))?a:s,typeof a===o?a.call(e,{name:"path",hash:{},data:i}):a))+"</a>\n          </span>\n        </h3>\n        <ul class='options'>\n          <li>\n          <a href='#!/"+l((a=null!=(a=t.encodedParentId||(null!=e?e.encodedParentId:e))?a:s,typeof a===o?a.call(e,{name:"encodedParentId",hash:{},data:i}):a))+"/"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+'\' class="toggleOperation">',a=null!=(a=t.summary||(null!=e?e.summary:e))?a:s,r=typeof a===o?a.call(e,{name:"summary",hash:{},data:i}):a,null!=r&&(u+=r),u+="</a>\n          </li>\n        </ul>\n      </div>\n      <div class='content' id='"+l((a=null!=(a=t.parentId||(null!=e?e.parentId:e))?a:s,typeof a===o?a.call(e,{name:"parentId",hash:{},data:i}):a))+"_"+l((a=null!=(a=t.nickname||(null!=e?e.nickname:e))?a:s,typeof a===o?a.call(e,{name:"nickname",hash:{},data:i}):a))+"_content' style='display:none'>\n",r=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.description:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.security:e,{name:"if",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.type:e,{name:"if",hash:{},fn:this.program(9,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n",r=t["if"].call(e,null!=e?e.headers:e,{name:"if",hash:{},fn:this.program(12,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n        <form accept-charset='UTF-8' class='sandbox'>\n          <div style='margin:0;padding:0;display:inline'></div>\n",r=t["if"].call(e,null!=e?e.parameters:e,{name:"if",hash:{},fn:this.program(15,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.responseMessages:e,{name:"if",hash:{},fn:this.program(17,i),inverse:this.noop,data:i}),null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.isReadOnly:e,{name:"if",hash:{},fn:this.program(19,i),inverse:this.program(21,i),data:i}),null!=r&&(u+=r),u+="        </form>\n        <div class='response' style='display:none'>\n          <h4 class='curl'>Curl</h4>\n          <div class='block curl'></div>\n          <h4 data-sw-translate>Request URL</h4>\n          <div class='block request_url'></div>\n",r=t["if"].call(e,null!=e?e.showRequestHeaders:e,{name:"if",hash:{},fn:this.program(23,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"          <h4 data-sw-translate>Response Body</h4>\n          <div class='block response_body'></div>\n          <h4 data-sw-translate>Response Code</h4>\n          <div class='block response_code'></div>\n          <h4 data-sw-translate>Response Headers</h4>\n          <div class='block response_headers'></div>\n        </div>\n      </div>\n    </li>\n  </ul>\n"},useData:!0}),this.Handlebars.templates.param_list=Handlebars.template({1:function(e,t,n,i){return" required"},3:function(e,t,n,i){return' multiple="multiple"'},5:function(e,t,n,i){return" required "},7:function(e,t,n,i){var r,a="      <option ";return r=t.unless.call(e,null!=e?e.hasDefault:e,{name:"unless",hash:{},fn:this.program(8,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a+" value=''></option>\n"},8:function(e,t,n,i){return'  selected="" '},10:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="\n      <option ";return r=t["if"].call(e,null!=e?e.isDefault:e,{name:"if",hash:{},fn:this.program(11,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="  value='"+l((a=null!=(a=t.value||(null!=e?e.value:e))?a:s,typeof a===o?a.call(e,{name:"value",hash:{},data:i}):a))+"'> "+l((a=null!=(a=t.value||(null!=e?e.value:e))?a:s,typeof a===o?a.call(e,{name:"value",hash:{},data:i}):a))+" ",r=t["if"].call(e,null!=e?e.isDefault:e,{name:"if",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+" </option>\n\n"},11:function(e,t,n,i){return' selected=""  '},13:function(e,t,n,i){return" (default) "},15:function(e,t,n,i){return"<strong>"},17:function(e,t,n,i){return"</strong>"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code";return r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n  <select ",r=(t.isArray||e&&e.isArray||s).call(e,e,{name:"isArray",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+=' class="parameter ',r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='" name="'+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+'" id="'+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+'">\n\n',r=t.unless.call(e,null!=e?e.required:e,{name:"unless",hash:{},fn:this.program(7,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="\n",r=t.each.call(e,null!=(r=null!=e?e.allowableValues:e)?r.descriptiveValues:r,{name:"each",hash:{},fn:this.program(10,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+='\n  </select>\n</td>\n<td class="markdown">',r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(15,i),inverse:this.noop,data:i}),null!=r&&(u+=r),a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),r=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(17,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_readonly_required=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"        <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"</textarea>\n"},3:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,i),inverse:this.program(6,i),data:i}),null!=r&&(a+=r),a},4:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"            "+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,
+typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"\n"},6:function(e,t,n,i){return"            (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_readonly=Handlebars.template({1:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"        <textarea class='body-textarea' readonly='readonly' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <div class="parameter-content-type" />\n'},3:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,i),inverse:this.program(6,i),data:i}),null!=r&&(a+=r),a},4:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"            "+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+"\n"},6:function(e,t,n,i){return"            (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(3,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_required=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(4,i),data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'			<input type="file" name=\''+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'/>\n"},4:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.program(7,i),data:i}),null!=r&&(a+=r),a},5:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<div class=\"editor_holder\"></div>\n				<textarea class='body-textarea required' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id=\""+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'">'+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <br />\n        <div class="parameter-content-type" />\n'},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<textarea class='body-textarea required' placeholder='(required)' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'></textarea>\n				<div class="editor_holder"></div>\n				<br />\n				<div class="parameter-content-type" />\n'},9:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(10,i),inverse:this.program(12,i),data:i}),null!=r&&(a+=r),a},10:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"			<input class='parameter' class='required' type='file' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'/>\n"},12:function(e,t,n,i){var r,a=t.helperMissing,o="";return r=(t.renderTextParam||e&&e.renderTextParam||a).call(e,e,{name:"renderTextParam",hash:{},fn:this.program(13,i),inverse:this.noop,data:i}),null!=r&&(o+=r),o},13:function(e,t,n,i){return""},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(9,i),data:i}),null!=r&&(u+=r),u+='</td>\n<td>\n	<strong><span class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</span></strong>\n</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(4,i),data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'			<input type="file" name=\''+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'/>\n			<div class="parameter-content-type" />\n'},4:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.program(7,i),data:i}),null!=r&&(a+=r),a},5:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<div class=\"editor_holder\"></div>\n				<textarea class='body-textarea' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+"'>"+s((r=null!=(r=t["default"]||(null!=e?e["default"]:e))?r:o,typeof r===a?r.call(e,{name:"default",hash:{},data:i}):r))+'</textarea>\n        <br />\n        <div class="parameter-content-type" />\n'},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"				<textarea class='body-textarea' name='"+s((r=null!=(r=t.name||(null!=e?e.name:e))?r:o,typeof r===a?r.call(e,{name:"name",hash:{},data:i}):r))+"' id='"+s((r=null!=(r=t.valueId||(null!=e?e.valueId:e))?r:o,typeof r===a?r.call(e,{name:"valueId",hash:{},data:i}):r))+'\'></textarea>\n				<div class="editor_holder"></div>\n				<br />\n				<div class="parameter-content-type" />\n'},9:function(e,t,n,i){var r,a="";return r=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.program(10,i),data:i}),null!=r&&(a+=r),a},10:function(e,t,n,i){var r,a=t.helperMissing,o="";return r=(t.renderTextParam||e&&e.renderTextParam||a).call(e,e,{name:"renderTextParam",hash:{},fn:this.program(11,i),inverse:this.noop,data:i}),null!=r&&(o+=r),o},11:function(e,t,n,i){return""},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'><label for='"+l((a=null!=(a=t.valueId||(null!=e?e.valueId:e))?a:s,typeof a===o?a.call(e,{name:"valueId",hash:{},data:i}):a))+"'>"+l((a=null!=(a=t.name||(null!=e?e.name:e))?a:s,typeof a===o?a.call(e,{name:"name",hash:{},data:i}):a))+"</label></td>\n<td>\n\n";return r=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(9,i),data:i}),null!=r&&(u+=r),u+='\n</td>\n<td class="markdown">',a=null!=(a=t.description||(null!=e?e.description:e))?a:s,r=typeof a===o?a.call(e,{name:"description",hash:{},data:i}):a,null!=r&&(u+=r),u+="</td>\n<td>",a=null!=(a=t.paramType||(null!=e?e.paramType:e))?a:s,r=typeof a===o?a.call(e,{name:"paramType",hash:{},data:i}):a,null!=r&&(u+=r),u+'</td>\n<td>\n	<span class="model-signature"></span>\n</td>\n'},useData:!0}),this.Handlebars.templates.parameter_content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.consumes:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'  <option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label for="'+l((a=null!=(a=t.parameterContentTypeId||(null!=e?e.parameterContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"parameterContentTypeId",hash:{},data:i}):a))+'" data-sw-translate>Parameter content type:</label>\n<select name="parameterContentType" id="'+l((a=null!=(a=t.parameterContentTypeId||(null!=e?e.parameterContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"parameterContentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.consumes:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),this.Handlebars.templates.popup=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return'<div class="api-popup-dialog-wrapper">\n    <div class="api-popup-title">'+s((r=null!=(r=t.title||(null!=e?e.title:e))?r:o,typeof r===a?r.call(e,{name:"title",hash:{},data:i}):r))+'</div>\n    <div class="api-popup-content"></div>\n    <p class="error-msg"></p>\n    <div class="api-popup-actions">\n        <button class="api-popup-cancel api-button gray" type="button">Cancel</button>\n    </div>\n</div>\n<div class="api-popup-dialog-shadow"></div>'},useData:!0}),this.Handlebars.templates.resource=Handlebars.template({1:function(e,t,n,i){return" : "},3:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"    <li>\n      <a href='"+s((r=null!=(r=t.url||(null!=e?e.url:e))?r:o,typeof r===a?r.call(e,{name:"url",hash:{},data:i}):r))+"' data-sw-translate>Raw</a>\n    </li>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o,s="function",l=t.helperMissing,u=this.escapeExpression,c=t.blockHelperMissing,p="<div class='heading'>\n  <h2>\n    <a href='#!/"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'\' class="toggleEndpointList" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'">'+u((a=null!=(a=t.name||(null!=e?e.name:e))?a:l,typeof a===s?a.call(e,{name:"name",hash:{},data:i}):a))+"</a> ";return a=null!=(a=t.summary||(null!=e?e.summary:e))?a:l,o={name:"summary",hash:{},fn:this.program(1,i),inverse:this.noop,data:i},r=typeof a===s?a.call(e,o):a,t.summary||(r=c.call(e,r,o)),null!=r&&(p+=r),a=null!=(a=t.summary||(null!=e?e.summary:e))?a:l,r=typeof a===s?a.call(e,{name:"summary",hash:{},data:i}):a,null!=r&&(p+=r),p+="\n  </h2>\n  <ul class='options'>\n    <li>\n      <a href='#!/"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+"' id='endpointListTogger_"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'\' class="toggleEndpointList" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>Show/Hide</a>\n    </li>\n    <li>\n      <a href=\'#\' class="collapseResource" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>\n        List Operations\n      </a>\n    </li>\n    <li>\n      <a href=\'#\' class="expandResource" data-id="'+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+'" data-sw-translate>\n        Expand Operations\n      </a>\n    </li>\n',r=t["if"].call(e,null!=e?e.url:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(p+=r),p+"  </ul>\n</div>\n<ul class='endpoints' id='"+u((a=null!=(a=t.id||(null!=e?e.id:e))?a:l,typeof a===s?a.call(e,{name:"id",hash:{},data:i}):a))+"_endpoint_list' style='display:none'>\n\n</ul>\n"},useData:!0}),this.Handlebars.templates.response_content_type=Handlebars.template({1:function(e,t,n,i){var r,a="";return r=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(a+=r),a},2:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return'  <option value="'+a(r(e,e))+'">'+a(r(e,e))+"</option>\n"},4:function(e,t,n,i){return'  <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='<label data-sw-translate for="'+l((a=null!=(a=t.responseContentTypeId||(null!=e?e.responseContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"responseContentTypeId",hash:{},data:i}):a))+'">Response Content Type</label>\n<select name="responseContentType" id="'+l((a=null!=(a=t.responseContentTypeId||(null!=e?e.responseContentTypeId:e))?a:s,typeof a===o?a.call(e,{name:"responseContentTypeId",hash:{},data:i}):a))+'">\n';return r=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,i),inverse:this.program(4,i),data:i}),null!=r&&(u+=r),u+"</select>\n"},useData:!0}),this.Handlebars.templates.signature=Handlebars.template({1:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l='\n<div>\n<ul class="signature-nav">\n  <li><a class="description-link" href="#" data-sw-translate>Model</a></li>\n  <li><a class="snippet-link" href="#" data-sw-translate>Example Value</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n  <div class="description">\n    ';return a=null!=(a=t.signature||(null!=e?e.signature:e))?a:s,r=typeof a===o?a.call(e,{name:"signature",hash:{},data:i}):a,null!=r&&(l+=r),l+='\n  </div>\n\n  <div class="snippet">\n',r=t["if"].call(e,null!=e?e.sampleJSON:e,{name:"if",hash:{},fn:this.program(2,i),inverse:this.noop,data:i}),null!=r&&(l+=r),r=t["if"].call(e,null!=e?e.sampleXML:e,{name:"if",hash:{},fn:this.program(5,i),inverse:this.noop,data:i}),null!=r&&(l+=r),l+"  </div>\n</div>\n"},2:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='      <div class="snippet_json">\n        <pre><code>'+l((a=null!=(a=t.sampleJSON||(null!=e?e.sampleJSON:e))?a:s,typeof a===o?a.call(e,{name:"sampleJSON",hash:{},data:i}):a))+"</code></pre>\n        ";return r=t["if"].call(e,null!=e?e.isParam:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"\n      </div>\n"},3:function(e,t,n,i){return'<small class="notice" data-sw-translate></small>'},5:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u='    <div class="snippet_xml">\n      <pre><code>'+l((a=null!=(a=t.sampleXML||(null!=e?e.sampleXML:e))?a:s,typeof a===o?a.call(e,{name:"sampleXML",hash:{},data:i}):a))+"</code></pre>\n      ";return r=t["if"].call(e,null!=e?e.isParam:e,{name:"if",hash:{},fn:this.program(3,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"\n    </div>\n"},7:function(e,t,n,i){var r,a="function",o=t.helperMissing,s=this.escapeExpression;return"    "+s((r=null!=(r=t.signature||(null!=e?e.signature:e))?r:o,typeof r===a?r.call(e,{name:"signature",hash:{},data:i}):r))+"\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a=t.helperMissing;return r=(t.ifCond||e&&e.ifCond||a).call(e,null!=e?e.sampleJSON:e,"||",null!=e?e.sampleXML:e,{name:"ifCond",hash:{},fn:this.program(1,i),inverse:this.program(7,i),data:i}),null!=r?r:""},useData:!0}),this.Handlebars.templates.status_code=Handlebars.template({1:function(e,t,n,i){var r=this.lambda,a=this.escapeExpression;return"      <tr>\n        <td>"+a(r(i&&i.key,e))+"</td>\n        <td>"+a(r(null!=e?e.description:e,e))+"</td>\n        <td>"+a(r(null!=e?e.type:e,e))+"</td>\n      </tr>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,i){var r,a,o="function",s=t.helperMissing,l=this.escapeExpression,u="<td width='15%' class='code'>"+l((a=null!=(a=t.code||(null!=e?e.code:e))?a:s,typeof a===o?a.call(e,{name:"code",hash:{},data:i}):a))+'</td>\n<td class="markdown">';return a=null!=(a=t.message||(null!=e?e.message:e))?a:s,r=typeof a===o?a.call(e,{name:"message",hash:{},data:i}):a,null!=r&&(u+=r),u+='</td>\n<td width=\'50%\'><span class="model-signature" /></td>\n<td class="headers">\n  <table>\n    <tbody>\n',r=t.each.call(e,null!=e?e.headers:e,{name:"each",hash:{},fn:this.program(1,i),inverse:this.noop,data:i}),null!=r&&(u+=r),u+"    </tbody>\n  </table>\n</td>"},useData:!0}),function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.SwaggerClient=e()}}(function(){var t;return function n(e,t,i){function r(o,s){if(!t[o]){if(!e[o]){var l="function"==typeof require&&require;if(!s&&l)return l(o,!0);if(a)return a(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var c=t[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return r(n?n:t)},c,c.exports,n,e,t,i)}return t[o].exports}for(var a="function"==typeof require&&require,o=0;o<i.length;o++)r(i[o]);return r}({1:[function(e,t,n){"use strict";var i=e("./lib/auth"),r=e("./lib/helpers"),a=e("./lib/client"),o=function(e,t){return r.log('This is deprecated, use "new SwaggerClient" instead.'),new a(e,t)};Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){for(var n=t||0,i=this.length;i>n;n++)if(this[n]===e)return n;return-1}),String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),String.prototype.endsWith||(String.prototype.endsWith=function(e){return-1!==this.indexOf(e,this.length-e.length)}),t.exports=a,a.ApiKeyAuthorization=i.ApiKeyAuthorization,a.PasswordAuthorization=i.PasswordAuthorization,a.CookieAuthorization=i.CookieAuthorization,a.SwaggerApi=o,a.SwaggerClient=o,a.SchemaMarkup=e("./lib/schema-markup")},{"./lib/auth":2,"./lib/client":3,"./lib/helpers":4,"./lib/schema-markup":7}],2:[function(e,t,n){"use strict";var i=e("./helpers"),r=e("btoa"),a=e("cookiejar").CookieJar,o={each:e("lodash-compat/collection/each"),includes:e("lodash-compat/collection/includes"),isObject:e("lodash-compat/lang/isObject"),isArray:e("lodash-compat/lang/isArray")},s=t.exports.SwaggerAuthorizations=function(e){this.authz=e||{}};s.prototype.add=function(e,t){if(o.isObject(e))for(var n in e)this.authz[n]=e[n];else"string"==typeof e&&(this.authz[e]=t);return t},s.prototype.remove=function(e){return delete this.authz[e]},s.prototype.apply=function(e,t){var n=!0,i=!t,r=[],a=e.clientAuthorizations||this.authz;return o.each(t,function(e,t){"string"==typeof t&&r.push(t),o.each(e,function(e,t){r.push(t)})}),o.each(a,function(t,a){if(i||o.includes(r,a)){var s=t.apply(e);n=n&&!!s}}),n};var l=t.exports.ApiKeyAuthorization=function(e,t,n){this.name=e,this.value=t,this.type=n};l.prototype.apply=function(e){if("query"===this.type){var t;if(e.url.indexOf("?")>0){t=e.url.substring(e.url.indexOf("?")+1);var n=t.split("&");if(n&&n.length>0)for(var i=0;i<n.length;i++){var r=n[i].split("=");if(r&&r.length>0&&r[0]===this.name)return!1}}return e.url.indexOf("?")>0?e.url=e.url+"&"+this.name+"="+this.value:e.url=e.url+"?"+this.name+"="+this.value,!0}return"header"===this.type?("undefined"==typeof e.headers[this.name]&&(e.headers[this.name]=this.value),!0):void 0};var u=t.exports.CookieAuthorization=function(e){this.cookie=e};u.prototype.apply=function(e){return e.cookieJar=e.cookieJar||new a,e.cookieJar.setCookie(this.cookie),!0};var c=t.exports.PasswordAuthorization=function(e,t){3===arguments.length&&(i.log("PasswordAuthorization: the 'name' argument has been removed, pass only username and password"),e=arguments[1],t=arguments[2]),this.username=e,this.password=t};c.prototype.apply=function(e){return"undefined"==typeof e.headers.Authorization&&(e.headers.Authorization="Basic "+r(this.username+":"+this.password)),!0}},{"./helpers":4,btoa:13,cookiejar:18,"lodash-compat/collection/each":52,"lodash-compat/collection/includes":55,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isObject":144}],3:[function(e,t,n){"use strict";var i={bind:e("lodash-compat/function/bind"),cloneDeep:e("lodash-compat/lang/cloneDeep"),find:e("lodash-compat/collection/find"),forEach:e("lodash-compat/collection/forEach"),indexOf:e("lodash-compat/array/indexOf"),isArray:e("lodash-compat/lang/isArray"),isObject:e("lodash-compat/lang/isObject"),isFunction:e("lodash-compat/lang/isFunction"),isPlainObject:e("lodash-compat/lang/isPlainObject"),isUndefined:e("lodash-compat/lang/isUndefined")},r=e("./auth"),a=e("./helpers"),o=e("./types/model"),s=e("./types/operation"),l=e("./types/operationGroup"),u=e("./resolver"),c=e("./http"),p=e("./spec-converter"),h=e("q"),f=["apis","authorizationScheme","authorizations","basePath","build","buildFrom1_1Spec","buildFrom1_2Spec","buildFromSpec","clientAuthorizations","convertInfo","debug","defaultErrorCallback","defaultSuccessCallback","enableCookies","fail","failure","finish","help","host","idFromOp","info","initialize","isBuilt","isValid","modelPropertyMacro","models","modelsArray","options","parameterMacro","parseUri","progress","resourceCount","sampleModels","selfReflect","setConsolidatedModels","spec","supportedSubmitMethods","swaggerRequestHeaders","tagFromLabel","title","url","useJQuery","jqueryAjaxCache"],d=["apis","asCurl","description","externalDocs","help","label","name","operation","operations","operationsArray","path","tag"],m=["delete","get","head","options","patch","post","put"],y=t.exports=function(e,t){return this.authorizations=null,this.authorizationScheme=null,this.basePath=null,this.debug=!1,this.enableCookies=!1,this.info=null,this.isBuilt=!1,this.isValid=!1,this.modelsArray=[],this.resourceCount=0,this.url=null,this.useJQuery=!1,this.jqueryAjaxCache=!1,this.swaggerObject={},this.deferredClient=void 0,this.clientAuthorizations=new r.SwaggerAuthorizations,"undefined"!=typeof e?this.initialize(e,t):this};y.prototype.initialize=function(e,t){return this.models={},this.sampleModels={},"string"==typeof e?this.url=e:i.isObject(e)&&(t=e,this.url=t.url),this.url&&-1===this.url.indexOf("http")&&"undefined"!=typeof window&&window&&window.location&&(this.url=window.location.origin+this.url),t=t||{},this.clientAuthorizations.add(t.authorizations),this.swaggerRequestHeaders=t.swaggerRequestHeaders||"application/json;charset=utf-8,*/*",this.defaultSuccessCallback=t.defaultSuccessCallback||null,this.defaultErrorCallback=t.defaultErrorCallback||null,this.modelPropertyMacro=t.modelPropertyMacro||null,this.parameterMacro=t.parameterMacro||null,this.usePromise=t.usePromise||null,this.usePromise&&(this.deferredClient=h.defer()),"function"==typeof t.success&&(this.success=t.success),t.useJQuery&&(this.useJQuery=t.useJQuery),t.jqueryAjaxCache&&(this.jqueryAjaxCache=t.jqueryAjaxCache),t.enableCookies&&(this.enableCookies=t.enableCookies),this.options=t||{},this.supportedSubmitMethods=t.supportedSubmitMethods||[],this.failure=t.failure||function(e){throw e},this.progress=t.progress||function(){},this.spec=i.cloneDeep(t.spec),t.scheme&&(this.scheme=t.scheme),this.usePromise||"function"==typeof t.success?(this.ready=!0,this.build()):void 0},y.prototype.build=function(e){if(this.isBuilt)return this;var t=this;this.progress("fetching resource list: "+this.url+"; Please wait.");var n={useJQuery:this.useJQuery,jqueryAjaxCache:this.jqueryAjaxCache,url:this.url,method:"get",headers:{accept:this.swaggerRequestHeaders},on:{error:function(e){return"http"!==t.url.substring(0,4)?t.fail("Please specify the protocol for "+t.url):0===e.status?t.fail("Can't read from server.  It may not have the appropriate access-control-origin settings."):404===e.status?t.fail("Can't read swagger JSON from "+t.url):t.fail(e.status+" : "+e.statusText+" "+t.url)},response:function(e){var n=e.obj;if(!n)return t.fail("failed to parse JSON/YAML response");if(t.swaggerVersion=n.swaggerVersion,t.swaggerObject=n,n.swagger&&2===parseInt(n.swagger))t.swaggerVersion=n.swagger,(new u).resolve(n,t.url,t.buildFromSpec,t),t.isValid=!0;else{var i=new p;t.oldSwaggerObject=t.swaggerObject,i.setDocumentationLocation(t.url),i.convert(n,t.clientAuthorizations,t.options,function(e){t.swaggerObject=e,(new u).resolve(e,t.url,t.buildFromSpec,t),t.isValid=!0})}}}};if(this.spec)t.swaggerObject=this.spec,setTimeout(function(){(new u).resolve(t.spec,t.url,t.buildFromSpec,t)},10);else{if(this.clientAuthorizations.apply(n),e)return n;(new c).execute(n,this.options)}return this.usePromise?this.deferredClient.promise:this},y.prototype.buildFromSpec=function(e){if(this.isBuilt)return this;this.apis={},this.apisArray=[],this.basePath=e.basePath||"",this.consumes=e.consumes,this.host=e.host||"",this.info=e.info||{},this.produces=e.produces,this.schemes=e.schemes||[],this.securityDefinitions=e.securityDefinitions,this.security=e.security,this.title=e.title||"",e.externalDocs&&(this.externalDocs=e.externalDocs),this.authSchemes=e.securityDefinitions;var t,n={};if(Array.isArray(e.tags))for(n={},t=0;t<e.tags.length;t++){var r=e.tags[t];n[r.name]=r}var u;"string"==typeof this.url?(u=this.parseUri(this.url),"undefined"==typeof this.scheme&&"undefined"==typeof this.schemes||0===this.schemes.length?this.scheme=u.scheme||"http":"undefined"==typeof this.scheme&&(this.scheme=this.schemes[0]||u.scheme),"undefined"!=typeof this.host&&""!==this.host||(this.host=u.host,u.port&&(this.host=this.host+":"+u.port))):"undefined"==typeof this.schemes||0===this.schemes.length?this.scheme="http":"undefined"==typeof this.scheme&&(this.scheme=this.schemes[0]),this.definitions=e.definitions;var c;for(c in this.definitions){var p=new o(c,this.definitions[c],this.models,this.modelPropertyMacro);p&&(this.models[c]=p)}var h=this;h.apis.help=i.bind(h.help,h),i.forEach(e.paths,function(e,t){i.isPlainObject(e)&&i.forEach(m,function(r){var o=e[r];if(!i.isUndefined(o)){if(!i.isPlainObject(o))return void a.log("The '"+r+"' operation for '"+t+"' path is not an Operation Object");var u=o.tags;!i.isUndefined(u)&&i.isArray(u)&&0!==u.length||(u=o.tags=["default"]);var c=h.idFromOp(t,r,o),p=new s(h,o.scheme,c,r,t,o,h.definitions,h.models,h.clientAuthorizations);i.forEach(u,function(e){var t=i.indexOf(f,e)>-1?"_"+e:e,r=i.indexOf(d,e)>-1?"_"+e:e,o=h[t];if(t!==e&&a.log("The '"+e+"' tag conflicts with a SwaggerClient function/property name.  Use 'client."+t+"' or 'client.apis."+e+"' instead of 'client."+e+"'."),r!==e&&a.log("The '"+e+"' tag conflicts with a SwaggerClient operation function/property name.  Use 'client.apis."+r+"' instead of 'client.apis."+e+"'."),i.indexOf(d,c)>-1&&(a.log("The '"+c+"' operationId conflicts with a SwaggerClient operation function/property name.  Use 'client.apis."+r+"._"+c+"' instead of 'client.apis."+r+"."+c+"'."),c="_"+c,p.nickname=c),i.isUndefined(o)){o=h[t]=h.apis[r]={},o.operations={},o.label=r,o.apis={};var s=n[e];i.isUndefined(s)||(o.description=s.description,o.externalDocs=s.externalDocs),h[t].help=i.bind(h.help,o),h.apisArray.push(new l(e,o.description,o.externalDocs,p))}c=h.makeUniqueOperationId(c,h.apis[r]),i.isFunction(o.help)||(o.help=i.bind(h.help,o)),h.apis[r][c]=o[c]=i.bind(p.execute,p),h.apis[r][c].help=o[c].help=i.bind(p.help,p),h.apis[r][c].asCurl=o[c].asCurl=i.bind(p.asCurl,p),o.apis[c]=o.operations[c]=p;var u=i.find(h.apisArray,function(t){return t.tag===e});u&&u.operationsArray.push(p)})}})});var y=[];return i.forEach(Object.keys(n),function(e){var t;for(t in h.apisArray){var n=h.apisArray[t];n&&e===n.name&&(y.push(n),h.apisArray[t]=null)}}),i.forEach(h.apisArray,function(e){e&&y.push(e)}),h.apisArray=y,i.forEach(e.definitions,function(e,t){e.id=t.toLowerCase(),e.name=t,h.modelsArray.push(e)}),this.isBuilt=!0,this.usePromise?(this.isValid=!0,this.isBuilt=!0,this.deferredClient.resolve(this),this.deferredClient.promise):(this.success&&this.success(),this)},y.prototype.makeUniqueOperationId=function(e,t){for(var n=0,r=e;;){var a=!1;if(i.forEach(t.operations,function(e){e.nickname===r&&(a=!0)}),!a)return r;r=e+"_"+n,n++}return e},y.prototype.parseUri=function(e){var t=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,n=t.exec(e);return{scheme:n[4]?n[4].replace(":",""):void 0,host:n[11],port:n[12],path:n[15]}},y.prototype.help=function(e){var t="";return this instanceof y?i.forEach(this.apis,function(e,n){i.isPlainObject(e)&&(t+="operations for the '"+n+"' tag\n",i.forEach(e.operations,function(e,n){t+="  * "+n+": "+e.summary+"\n"}))}):(this instanceof l||i.isPlainObject(this))&&(t+="operations for the '"+this.label+"' tag\n",i.forEach(this.apis,function(e,n){t+="  * "+n+": "+e.summary+"\n"})),e?t:(a.log(t),t)},y.prototype.tagFromLabel=function(e){return e},y.prototype.idFromOp=function(e,t,n){n&&n.operationId||(n=n||{},n.operationId=t+"_"+e);var i=n.operationId.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|.\/?,\\'""-]/g,"_")||e.substring(1)+"_"+t;return i=i.replace(/((_){2,})/g,"_"),i=i.replace(/^(_)*/g,""),i=i.replace(/([_])*$/g,"")},y.prototype.setHost=function(e){this.host=e,this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.host=e})})},y.prototype.setBasePath=function(e){this.basePath=e,this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.basePath=e})})},y.prototype.setSchemes=function(e){this.schemes=e,e&&e.length>0&&this.apis&&i.forEach(this.apis,function(t){t.operations&&i.forEach(t.operations,function(t){t.scheme=e[0]})})},y.prototype.fail=function(e){return this.usePromise?(this.deferredClient.reject(e),this.deferredClient.promise):void(this.failure?this.failure(e):this.failure(e))}},{"./auth":2,"./helpers":4,"./http":5,"./resolver":6,"./spec-converter":8,
+"./types/model":9,"./types/operation":10,"./types/operationGroup":11,"lodash-compat/array/indexOf":49,"lodash-compat/collection/find":53,"lodash-compat/collection/forEach":54,"lodash-compat/function/bind":58,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isFunction":142,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isUndefined":148,q:157}],4:[function(e,t,n){(function(n){"use strict";var i={isPlainObject:e("lodash-compat/lang/isPlainObject"),indexOf:e("lodash-compat/array/indexOf")};t.exports.__bind=function(e,t){return function(){return e.apply(t,arguments)}};var r=t.exports.log=function(){console&&"test"!==n.env.NODE_ENV&&console.log(Array.prototype.slice.call(arguments)[0])};t.exports.fail=function(e){r(e)};var a=(t.exports.optionHtml=function(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"},t.exports.resolveSchema=function(e){return i.isPlainObject(e.schema)&&(e=a(e.schema)),e});t.exports.simpleRef=function(e){return"undefined"==typeof e?null:0===e.indexOf("#/definitions/")?e.substring("#/definitions/".length):e}}).call(this,e("_process"))},{_process:12,"lodash-compat/array/indexOf":49,"lodash-compat/lang/isPlainObject":145}],5:[function(t,n,i){"use strict";var r=t("./helpers"),a=t("superagent"),o=t("js-yaml"),s={isObject:t("lodash-compat/lang/isObject")},l=function(){this.type="JQueryHttpClient"},u=function(){this.type="SuperagentHttpClient"},c=n.exports=function(){};c.prototype.execute=function(t,n){var i;i=n&&n.client?n.client:new u(n),i.opts=n||{};var r=!1;if("undefined"!=typeof window&&"undefined"!=typeof window.jQuery&&(r=!0),"options"===t.method.toLowerCase()&&"SuperagentHttpClient"===i.type&&(e("forcing jQuery as OPTIONS are not supported by SuperAgent"),t.useJQuery=!0),this.isInternetExplorer()&&(t.useJQuery===!1||!r))throw new Error("Unsupported configuration! JQuery is required but not available");(t&&t.useJQuery===!0||this.isInternetExplorer()&&r)&&(i=new l(n));var a=t.on.response,o=t.on.error,c=function(e){return n&&n.requestInterceptor&&(e=n.requestInterceptor.apply(e)),e},p=function(e){return n&&n.responseInterceptor&&(e=n.responseInterceptor.apply(e)),a(e)},h=function(e){n&&n.responseInterceptor&&(e=n.responseInterceptor.apply(e)),o(e)};return t.on.error=function(e){h(e)},t.on.response=function(e){p(e)},s.isObject(t)&&s.isObject(t.body)&&t.body.type&&"formData"===t.body.type&&n.useJQuery&&(t.contentType=!1,t.processData=!1,delete t.headers["Content-Type"]),t=c(t)||t,t.beforeSend?t.beforeSend(function(e){i.execute(e||t)}):i.execute(t),t.deferred?t.deferred.promise:t},c.prototype.isInternetExplorer=function(){var e=!1;if("undefined"!=typeof navigator&&navigator.userAgent){var t=navigator.userAgent.toLowerCase();if(-1!==t.indexOf("msie")){var n=parseInt(t.split("msie")[1]);8>=n&&(e=!0)}}return e},l.prototype.execute=function(e){var t=this.jQuery||"undefined"!=typeof window&&window.jQuery,n=e.on,i=e;if("undefined"==typeof t||t===!1)throw new Error("Unsupported configuration! JQuery is required but not available");return e.type=e.method,e.cache=e.jqueryAjaxCache,e.data=e.body,delete e.jqueryAjaxCache,delete e.useJQuery,delete e.body,e.complete=function(e){for(var t={},a=e.getAllResponseHeaders().split("\n"),s=0;s<a.length;s++){var l=a[s].trim();if(0!==l.length){var u=l.indexOf(":");if(-1!==u){var c=l.substring(0,u).trim(),p=l.substring(u+1).trim();t[c]=p}else t[l]=null}}var h={url:i.url,method:i.method,status:e.status,statusText:e.statusText,data:e.responseText,headers:t};try{var f=e.responseJSON||o.safeLoad(e.responseText);h.obj="string"==typeof f?{}:f}catch(d){r.log("unable to parse JSON/YAML content")}if(h.obj=h.obj||null,e.status>=200&&e.status<300)n.response(h);else{if(!(0===e.status||e.status>=400&&e.status<599))return n.response(h);n.error(h)}},t.support.cors=!0,t.ajax(e)},u.prototype.execute=function(e){var t=e.method.toLowerCase();"delete"===t&&(t="del");var n=e.headers||{},i=a[t](e.url);if(e.enableCookies&&i.withCredentials(),e.body)if(s.isObject(e.body)){var l=e.headers["Content-Type"]||"";if(0===l.indexOf("multipart/form-data"))if(delete n["Content-Type"],"[object FormData]"==={}.toString.apply(e.body))for(var u=e.body.keys();;){var c=u.next();if(c.done)break;var p=c.value,h=e.body.get(p);console.log({}.toString.apply(h)),"[object File]"==={}.toString.apply(h)?i.attach(p,h):i.field(p,h)}else{var f;for(var f in e.body){var h=e.body[f];i.field(f,h)}}else s.isObject(e.body)&&(e.body=JSON.stringify(e.body),i.send(e.body))}else i.send(e.body);var d;for(d in n)i.set(d,n[d]);"function"==typeof i.buffer&&i.buffer(),i.end(function(t,n){n=n||{status:0,headers:{error:"no response from server"}};var i,a={url:e.url,method:e.method,headers:n.headers};if(!t&&n.error&&(t=n.error),t&&e.on&&e.on.error){if(a.errObj=t,a.status=n?n.status:500,a.statusText=n?n.text:t.message,n.headers&&n.headers["content-type"]&&n.headers["content-type"].indexOf("application/json")>=0)try{a.obj=JSON.parse(a.statusText)}catch(s){a.obj=null}i=e.on.error}else if(n&&e.on&&e.on.response){var l;if(n.body&&Object.keys(n.body).length>0)l=n.body;else try{l=o.safeLoad(n.text),l="string"==typeof l?null:l}catch(s){r.log("cannot parse JSON/YAML content")}a.obj="object"==typeof l?l:null,a.status=n.status,a.statusText=n.text,i=e.on.response}a.data=a.statusText,i&&i(a)})}},{"./helpers":4,"js-yaml":19,"lodash-compat/lang/isObject":144,superagent:158}],6:[function(e,t,n){"use strict";var i=e("./http"),r={isObject:e("lodash-compat/lang/isObject"),cloneDeep:e("lodash-compat/lang/cloneDeep"),isArray:e("lodash-compat/lang/isArray")},a=t.exports=function(){this.failedUrls=[]};a.prototype.processAllOf=function(e,t,n,i,r,a){var o,s,l;n["x-resolved-from"]=["#/definitions/"+t];var u=n.allOf;for(u.sort(function(e,t){return e.$ref&&t.$ref?0:e.$ref?-1:1}),o=0;o<u.length;o++)l=u[o],s="/definitions/"+t+"/allOf",this.resolveInline(e,a,l,i,r,s)},a.prototype.resolve=function(e,t,n,a){this.spec=e;var o,s,l=t,u=n,c=a,p={};"function"==typeof t&&(l=null,u=t,c=n);var h=l;this.scope=c||this,this.iteration=this.iteration||0,this.scope.options&&this.scope.options.requestInterceptor&&(p.requestInterceptor=this.scope.options.requestInterceptor),this.scope.options&&this.scope.options.responseInterceptor&&(p.responseInterceptor=this.scope.options.responseInterceptor);var f,d,m,y,g=0,v={},b={},w=[];e.definitions=e.definitions||{};for(f in e.definitions){var x=e.definitions[f];if(x.$ref)this.resolveInline(l,e,x,w,b,x);else{for(y in x.properties)m=x.properties[y],r.isArray(m.allOf)?this.processAllOf(l,f,m,w,b,e):this.resolveTo(l,m,w,"/definitions");x.allOf&&this.processAllOf(l,f,x,w,b,e)}}e.parameters=e.parameters||{};for(f in e.parameters){var A=e.parameters[f];if("body"===A["in"]&&A.schema)if(r.isArray(A.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:A.schema.allOf},delete A.schema.allOf,A.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else this.resolveTo(l,A.schema,w,o);A.$ref&&this.resolveInline(l,e,A,w,b,A.$ref)}for(f in e.paths){var S,k,C;d=e.paths[f];for(S in d)if("$ref"===S)o="/paths"+f,this.resolveInline(l,e,d,w,b,o);else{k=d[S];var E=d.parameters||[],I=k.parameters||[];for(s in E){var A=E[s];I.unshift(A)}"parameters"!==S&&r.isObject(k)&&(k.parameters=k.parameters||I);for(s in I){var A=I[s];if(o="/paths"+f+"/"+S+"/parameters","body"===A["in"]&&A.schema)if(r.isArray(A.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:A.schema.allOf},delete A.schema.allOf,A.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else this.resolveTo(l,A.schema,w,o);A.$ref&&this.resolveInline(l,e,A,w,b,A.$ref)}for(C in k.responses){var T=k.responses[C];if(o="/paths"+f+"/"+S+"/responses/"+C,r.isObject(T)&&(T.$ref&&this.resolveInline(l,e,T,w,b,o),T.schema)){var $=T;if(r.isArray($.schema.allOf)){for(var j="inline_model",f=j,O=!1,_=0;!O;){if("undefined"==typeof e.definitions[f]){O=!0;break}f=j+"_"+_,_++}e.definitions[f]={allOf:$.schema.allOf},delete $.schema.allOf,delete $.schema.type,$.schema.$ref="#/definitions/"+f,this.processAllOf(l,f,e.definitions[f],w,b,e)}else"array"===$.schema.type?$.schema.items&&$.schema.items.$ref&&this.resolveInline(l,e,$.schema.items,w,b,o):this.resolveTo(l,T.schema,w,o)}}}d.parameters=[]}var M,U=0,P=[],L=w;for(s=0;s<L.length;s++){var D=L[s];if(l===D.root){if("ref"===D.resolveAs){var R,N=((D.root||"")+"/"+D.key).split("/"),F=[],B="";if(D.key.indexOf("../")>=0){for(var q=0;q<N.length;q++)".."===N[q]?F=F.slice(0,F.length-1):F.push(N[q]);for(R=0;R<F.length;R++)R>0&&(B+="/"),B+=F[R];D.root=B,P.push(D)}else if(M=D.key.split("#"),2===M.length){0!==M[0].indexOf("http://")&&0!==M[0].indexOf("https://")||(D.root=M[0]),o=M[1].split("/");var V,z=e;for(R=0;R<o.length;R++){var H=o[R];if(""!==H){if(z=z[H],"undefined"==typeof z){V=null;break}V=z}}null===V&&P.push(D)}}else if("inline"===D.resolveAs){if(D.key&&-1===D.key.indexOf("#")&&"/"!==D.key.charAt(0)){for(M=D.root.split("/"),o="",s=0;s<M.length-1;s++)o+=M[s]+"/";o+=D.key,D.root=o,D.location=""}P.push(D)}}else P.push(D)}U=P.length;for(var J=0;J<P.length;J++)!function(e,t,n){if(e.root&&e.root!==l)if(-1===n.failedUrls.indexOf(e.root)){var r={useJQuery:!1,url:e.root,method:"get",headers:{accept:n.scope.swaggerRequestHeaders||"application/json"},on:{error:function(i){g+=1,console.log("failed url: "+r.url),n.failedUrls.push(r.url),b[e.key]={root:e.root,location:e.location},g===U&&n.finish(t,h,w,v,b,u)},response:function(i){var r=i.obj;n.resolveItem(r,e.root,w,v,b,e),g+=1,g===U&&n.finish(t,h,w,v,b,u)}}};c&&c.clientAuthorizations&&c.clientAuthorizations.apply(r),(new i).execute(r,p)}else g+=1,b[e.key]={root:e.root,location:e.location},g===U&&n.finish(t,h,w,v,b,u);else n.resolveItem(t,h,w,v,b,e),g+=1,g===U&&n.finish(t,l,w,v,b,u,!0)}(P[J],e,this);0===Object.keys(P).length&&this.finish(e,h,w,v,b,u)},a.prototype.resolveItem=function(e,t,n,i,r,a){var o=a.location,s=e,l=o.split("/");if(""!==o)for(var u=0;u<l.length;u++){var c=l[u];if(-1!==c.indexOf("~1")&&(c=l[u].replace(/~0/g,"~").replace(/~1/g,"/"),"/"!==c.charAt(0)&&(c="/"+c)),"undefined"==typeof s||null===s)break;if(""===c&&u===l.length-1&&l.length>1){s=null;break}c.length>0&&(s=s[c])}var p=a.key;l=a.key.split("/");var h=l[l.length-1];h.indexOf("#")>=0&&(h=h.split("#")[1]),null!==s&&"undefined"!=typeof s?i[p]={name:h,obj:s,key:a.key,root:a.root}:r[p]={root:a.root,location:a.location}},a.prototype.finish=function(e,t,n,i,r,a,o){var s;for(s in n){var l=n[s],u=l.key,c=i[u];if(c)if(e.definitions=e.definitions||{},"ref"===l.resolveAs){if(o!==!0)for(u in c.obj)var p=this.retainRoot(c.obj[u],l.root);e.definitions[c.name]=c.obj,l.obj.$ref="#/definitions/"+c.name}else if("inline"===l.resolveAs){var h=l.obj;h["x-resolved-from"]=[l.key],delete h.$ref;for(u in c.obj){var p=c.obj[u];o!==!0&&(p=this.retainRoot(c.obj[u],l.root)),h[u]=p}}}var f=this.countUnresolvedRefs(e);0===f||this.iteration>5?(this.resolveAllOf(e.definitions),a.call(this.scope,e,r)):(this.iteration+=1,this.resolve(e,t,a,this.scope))},a.prototype.countUnresolvedRefs=function(e){var t,n=this.getRefs(e),i=[],r=[];for(t in n)0===t.indexOf("#")?i.push(t.substring(1)):r.push(t);for(t=0;t<i.length;t++)for(var a=i[t],o=a.split("/"),s=e,l=0;l<o.length;l++){var u=o[l];if(""!==u&&(s=s[u],"undefined"==typeof s)){r.push(a);break}}return r.length},a.prototype.getRefs=function(e,t){t=t||e;var n={};for(var i in t)if(t.hasOwnProperty(i)){var a=t[i];if("$ref"===i&&"string"==typeof a)n[a]=null;else if(r.isObject(a)){var o=this.getRefs(a);for(var s in o)n[s]=null}}return n},a.prototype.retainRoot=function(e,t){for(var n in e){var i=e[n];if("$ref"===n&&"string"==typeof i){if(0!==i.indexOf("http://")&&0!==i.indexOf("https://")){var a=!0;if(t){var o=t.slice(-1);if("/"!==o&&0!==i.indexOf("#")&&0!==i.indexOf("http://")&&i.indexOf("https://")){a=!1;var s=t.split("/");s=s.splice(0,s.length-1),t="";for(var l=0;l<s.length;l++)t+=s[l]+"/"}}0!==i.indexOf("#")&&a&&(i="#"+i),i=(t||"")+i,e[n]=i}}else r.isObject(i)&&this.retainRoot(i,t)}return e},a.prototype.resolveInline=function(e,t,n,i,r,a){var o,s,l,u,c=n.$ref,p=n.$ref,h=!1;if(e=e||"",p){if(0===p.indexOf("../")){for(s=p.split("../"),l=e.split("/"),p="",o=0;o<s.length;o++)""===s[o]?l=l.slice(0,l.length-1):p+=s[o];for(e="",o=0;o<l.length-1;o++)o>0&&(e+="/"),e+=l[o];h=!0}if(p.indexOf("#")>=0)if(0===p.indexOf("/"))u=p.split("#"),s=e.split("//"),l=s[1].split("/"),e=s[0]+"//"+l[0]+u[0],a=u[1];else{if(u=p.split("#"),""!==u[0]){if(l=e.split("/"),l=l.slice(0,l.length-1),!h){e="";for(var f=0;f<l.length;f++)f>0&&(e+="/"),e+=l[f]}e+="/"+p.split("#")[0]}a=u[1]}if(0===p.indexOf("http"))p.indexOf("#")>=0?(e=p.split("#")[0],a=p.split("#")[1]):(e=p,a=""),i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a});else if(0===p.indexOf("#"))a=p.split("#")[1],i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a});else if(0===p.indexOf("/")&&-1===p.indexOf("#")){a=p;var d=e.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);d&&(e=d[0]+p.substring(1),a=""),i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a})}else i.push({obj:n,resolveAs:"inline",root:e,key:c,location:a})}else"array"===n.type&&this.resolveTo(e,n.items,i,a)},a.prototype.resolveTo=function(e,t,n,i){var a,o,s=t.$ref,l=e;if("undefined"!=typeof s&&null!==s){if(s.indexOf("#")>=0){var u=s.split("#");if(u[0]&&0===s.indexOf("/"));else if(u[0]&&0===u[0].indexOf("http"))l=u[0],s=u[1];else if(u[0]&&u[0].length>0){for(a=e.split("/"),l="",o=0;o<a.length-1;o++)l+=a[o]+"/";l+=u[0]}i=u[1]}else if(0===s.indexOf("http://")||0===s.indexOf("https://"))l=s,i="";else{for(a=e.split("/"),l="",o=0;o<a.length-1;o++)l+=a[o]+"/";l+=s,i=""}n.push({obj:t,resolveAs:"ref",root:l,key:s,location:i})}else if("array"===t.type){var c=t.items;this.resolveTo(e,c,n,i)}else if(t&&t.properties){var p=this.uniqueName("inline_model");t.title&&(p=this.uniqueName(t.title)),delete t.title,this.spec.definitions[p]=r.cloneDeep(t),t.$ref="#/definitions/"+p,delete t.type,delete t.properties}},a.prototype.uniqueName=function(e){for(var t=e,n=0;;){if(!r.isObject(this.spec.definitions[t]))return t;t=e+"_"+n,n++}},a.prototype.resolveAllOf=function(e,t,n){n=n||0,t=t||e;var i;for(var a in t)if(t.hasOwnProperty(a)){var o=t[a];if(null===o)throw new TypeError("Swagger 2.0 does not support null types ("+t+").  See https://github.com/swagger-api/swagger-spec/issues/229.");if("object"==typeof o&&this.resolveAllOf(e,o,n+1),o&&"undefined"!=typeof o.allOf){var s=o.allOf;if(r.isArray(s)){var l=r.cloneDeep(o);delete l.allOf,l["x-composed"]=!0,"undefined"!=typeof o["x-resolved-from"]&&(l["x-resolved-from"]=o["x-resolved-from"]);for(var u=0;u<s.length;u++){var c=s[u],p="self";"undefined"!=typeof c["x-resolved-from"]&&(p=c["x-resolved-from"][0]);for(var h in c)if(l.hasOwnProperty(h))if("properties"===h){var f=c[h];for(i in f){l.properties[i]=r.cloneDeep(f[i]);var d=f[i]["x-resolved-from"];"undefined"!=typeof d&&"self"!==d||(d=p),l.properties[i]["x-resolved-from"]=d}}else if("required"===h){for(var m=l.required.concat(c[h]),y=0;y<m.length;++y)for(var g=y+1;g<m.length;++g)m[y]===m[g]&&m.splice(g--,1);l.required=m}else"x-resolved-from"===h&&l["x-resolved-from"].push(p);else if(l[h]=r.cloneDeep(c[h]),"properties"===h)for(i in l[h])l[h][i]["x-resolved-from"]=p}t[a]=l}}}}},{"./http":5,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isObject":144}],7:[function(e,t,n){"use strict";function i(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"}function r(e,t){var n;return"integer"===e&&"int32"===t?n="integer":"integer"===e&&"int64"===t?n="long":"integer"===e&&"undefined"==typeof t?n="long":"string"===e&&"date-time"===t?n="date-time":"string"===e&&"date"===t?n="date":"number"===e&&"float"===t?n="float":"number"===e&&"double"===t?n="double":"number"===e&&"undefined"==typeof t?n="double":"boolean"===e?n="boolean":"string"===e&&(n="string"),n}function a(e,t){var n="";return"undefined"!=typeof e.$ref?n+=l.simpleRef(e.$ref):"undefined"==typeof e.type?n+="object":"array"===e.type?t?n+=a(e.items||e.$ref||{}):(n+="Array[",n+=a(e.items||e.$ref||{}),n+="]"):n+="integer"===e.type&&"int32"===e.format?"integer":"integer"===e.type&&"int64"===e.format?"long":"integer"===e.type&&"undefined"==typeof e.format?"long":"string"===e.type&&"date-time"===e.format?"date-time":"string"===e.type&&"date"===e.format?"date":"string"===e.type&&"undefined"==typeof e.format?"string":"number"===e.type&&"float"===e.format?"float":"number"===e.type&&"double"===e.format?"double":"number"===e.type&&"undefined"==typeof e.format?"double":"boolean"===e.type?"boolean":e.$ref?l.simpleRef(e.$ref):e.type,n}function o(e,t,n,i){e=l.resolveSchema(e),"function"!=typeof i&&(i=function(e){return(e||{})["default"]}),n=n||{};var r,a,s=e.type||"object",c=e.format;return u.isUndefined(e.example)?u.isUndefined(e.items)&&u.isArray(e["enum"])&&(a=e["enum"][0]):a=e.example,u.isUndefined(a)&&(e.$ref?(r=t[l.simpleRef(e.$ref)],u.isUndefined(r)||(u.isUndefined(n[r.name])?(n[r.name]=r,a=o(r.definition,t,n,i),delete n[r.name]):a="array"===r.type?[]:{})):u.isUndefined(e["default"])?"string"===s?a="date-time"===c?(new Date).toISOString():"date"===c?(new Date).toISOString().split("T")[0]:"string":"integer"===s?a=0:"number"===s?a=0:"boolean"===s?a=!0:"object"===s?(a={},u.forEach(e.properties,function(e,r){var s=u.cloneDeep(e);s["default"]=i(e),a[r]=o(s,t,n,i)})):"array"===s&&(a=[],u.isArray(e.items)?u.forEach(e.items,function(e){a.push(o(e,t,n,i))}):u.isPlainObject(e.items)?a.push(o(e.items,t,n,i)):u.isUndefined(e.items)?a.push({}):l.log("Array type's 'items' property is not an array or an object, cannot process")):a=e["default"]),a}function s(e,t,n,r){function a(e,t,i){var r,a=t;return e.$ref?(a=e.title||l.simpleRef(e.$ref),r=n[a]):u.isUndefined(t)&&(a=e.title||"Inline Model "+ ++m,r={definition:e}),i!==!0&&(f[a]=u.isUndefined(r)?{}:r.definition),a}function o(e){var t='<span class="propType">',n=e.type||"object";return e.$ref?t+=a(e,l.simpleRef(e.$ref)):"object"===n?t+=u.isUndefined(e.properties)?"object":a(e):"array"===n?(t+="Array[",u.isArray(e.items)?t+=u.map(e.items,a).join(","):u.isPlainObject(e.items)?t+=u.isUndefined(e.items.$ref)?u.isUndefined(e.items.type)||-1!==u.indexOf(["array","object"],e.items.type)?a(e.items):e.items.type:a(e.items,l.simpleRef(e.items.$ref)):(l.log("Array type's 'items' schema is not an array or an object, cannot process"),t+="object"),t+="]"):t+=e.type,t+="</span>"}function s(e,t){var n="",r=e.type||"object",a="array"===r;switch(a&&(r=u.isPlainObject(e.items)&&!u.isUndefined(e.items.type)?e.items.type:"object"),u.isUndefined(e["default"])||(n+=i("Default",e["default"])),r){case"string":e.minLength&&(n+=i("Min. Length",e.minLength)),e.maxLength&&(n+=i("Max. Length",e.maxLength)),e.pattern&&(n+=i("Reg. Exp.",e.pattern));break;case"integer":case"number":e.minimum&&(n+=i("Min. Value",e.minimum)),e.exclusiveMinimum&&(n+=i("Exclusive Min.","true")),e.maximum&&(n+=i("Max. Value",e.maximum)),e.exclusiveMaximum&&(n+=i("Exclusive Max.","true")),e.multipleOf&&(n+=i("Multiple Of",e.multipleOf))}if(a&&(e.minItems&&(n+=i("Min. Items",e.minItems)),e.maxItems&&(n+=i("Max. Items",e.maxItems)),e.uniqueItems&&(n+=i("Unique Items","true")),e.collectionFormat&&(n+=i("Coll. Format",e.collectionFormat))),u.isUndefined(e.items)&&u.isArray(e["enum"])){var o;o="number"===r||"integer"===r?e["enum"].join(", "):'"'+e["enum"].join('", "')+'"',n+=i("Enum",o)}return n.length>0&&(t='<span class="propWrap">'+t+'<table class="optionsWrapper"><tr><th colspan="2">'+r+"</th></tr>"+n+"</table></span>"),t}function c(e,t){var i=e.type||"object",c="array"===e.type,f=p+t+" "+(c?"[":"{")+h;if(t&&d.push(t),c)u.isArray(e.items)?f+="<div>"+u.map(e.items,function(e){var t=e.type||"object";return u.isUndefined(e.$ref)?u.indexOf(["array","object"],t)>-1?"object"===t&&u.isUndefined(e.properties)?"object":a(e):s(e,t):a(e,l.simpleRef(e.$ref))}).join(",</div><div>"):u.isPlainObject(e.items)?f+=u.isUndefined(e.items.$ref)?u.indexOf(["array","object"],e.items.type||"object")>-1?(u.isUndefined(e.items.type)||"object"===e.items.type)&&u.isUndefined(e.items.properties)?"<div>object</div>":"<div>"+a(e.items)+"</div>":"<div>"+s(e.items,e.items.type)+"</div>":"<div>"+a(e.items,l.simpleRef(e.items.$ref))+"</div>":(l.log("Array type's 'items' property is not an array or an object, cannot process"),f+="<div>object</div>");else if(e.$ref)f+="<div>"+a(e,t)+"</div>";else if("object"===i){if(u.isPlainObject(e.properties)){var m=u.map(e.properties,function(t,i){var a,c,p=u.indexOf(e.required,i)>=0,h=u.cloneDeep(t),f=p?"required":"",d='<span class="propName '+f+'">'+i+"</span> (";return h["default"]=r(h),h=l.resolveSchema(h),c=t.description||h.description,u.isUndefined(h.$ref)||(a=n[l.simpleRef(h.$ref)],u.isUndefined(a)||-1!==u.indexOf([void 0,"array","object"],a.definition.type)||(h=l.resolveSchema(a.definition))),d+=o(h),p||(d+=', <span class="propOptKey">optional</span>'),t.readOnly&&(d+=', <span class="propReadOnly">read only</span>'),d+=")",u.isUndefined(c)||(d+=': <span class="propDesc">'+c+"</span>"),h["enum"]&&(d+=' = <span class="propVals">[\''+h["enum"].join("', '")+"']</span>"),"<div"+(t.readOnly?' class="readOnly"':"")+">"+s(h,d)}).join(",</div>");m&&(f+=m+"</div>")}}else f+="<div>"+s(e,i)+"</div>";return f+p+(c?"]":"}")+h}var p='<span class="strong">',h="</span>";if(u.isObject(arguments[0])&&(e=void 0,t=arguments[0],n=arguments[1],r=arguments[2]),n=n||{},t=l.resolveSchema(t),u.isEmpty(t))return p+"Empty"+h;if("string"==typeof t.$ref&&(e=l.simpleRef(t.$ref),t=n[e],"undefined"==typeof t))return p+e+" is not defined!"+h;"string"!=typeof e&&(e=t.title||"Inline Model"),t.definition&&(t=t.definition),"function"!=typeof r&&(r=function(e){return(e||{})["default"]});for(var f={},d=[],m=0,y=c(t,e);u.keys(f).length>0;)u.forEach(f,function(e,t){var n=u.indexOf(d,t)>-1;delete f[t],n||(d.push(t),y+="<br />"+c(e,t))});return y}var l=e("./helpers"),u={isPlainObject:e("lodash-compat/lang/isPlainObject"),isUndefined:e("lodash-compat/lang/isUndefined"),isArray:e("lodash-compat/lang/isArray"),isObject:e("lodash-compat/lang/isObject"),isEmpty:e("lodash-compat/lang/isEmpty"),map:e("lodash-compat/collection/map"),indexOf:e("lodash-compat/array/indexOf"),cloneDeep:e("lodash-compat/lang/cloneDeep"),keys:e("lodash-compat/object/keys"),forEach:e("lodash-compat/collection/forEach")};t.exports.optionHtml=i,t.exports.typeFromJsonSchema=r,t.exports.getStringSignature=a,t.exports.schemaToHTML=s,t.exports.schemaToJSON=o},{"./helpers":4,"lodash-compat/array/indexOf":49,"lodash-compat/collection/forEach":54,"lodash-compat/collection/map":56,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isArray":140,"lodash-compat/lang/isEmpty":141,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isUndefined":148,"lodash-compat/object/keys":149}],8:[function(e,t,n){"use strict";var i=e("./http"),r={isObject:e("lodash-compat/lang/isObject")},a=t.exports=function(){this.errors=[],this.warnings=[],this.modelMap={}};a.prototype.setDocumentationLocation=function(e){this.docLocation=e},a.prototype.convert=function(e,t,n,i){if(!e||!Array.isArray(e.apis))return this.finish(i,null);this.clientAuthorizations=t;var r={swagger:"2.0"};r.originalVersion=e.swaggerVersion,this.apiInfo(e,r),this.securityDefinitions(e,r),e.basePath&&this.setDocumentationLocation(e.basePath);var a,o=!1;for(a=0;a<e.apis.length;a++){var s=e.apis[a];Array.isArray(s.operations)&&(o=!0)}o?(this.declaration(e,r),this.finish(i,r)):this.resourceListing(e,r,n,i)},a.prototype.declaration=function(e,t){var n,i,a,o;if(e.apis){0===e.basePath.indexOf("http://")?(a=e.basePath.substring("http://".length),o=a.indexOf("/"),o>0?(t.host=a.substring(0,o),t.basePath=a.substring(o)):(t.host=a,t.basePath="/")):0===e.basePath.indexOf("https://")?(a=e.basePath.substring("https://".length),o=a.indexOf("/"),o>0?(t.host=a.substring(0,o),t.basePath=a.substring(o)):(t.host=a,t.basePath="/")):t.basePath=e.basePath;var s;if(e.authorizations&&(s=e.authorizations),e.consumes&&(t.consumes=e.consumes),e.produces&&(t.produces=e.produces),r.isObject(e))for(n in e.models){var l=e.models[n],u=l.id||n;this.modelMap[u]=n}for(i=0;i<e.apis.length;i++){var c=e.apis[i],p=c.path,h=c.operations;this.operations(p,e.resourcePath,h,s,t)}var f=e.models||{};this.models(f,t)}},a.prototype.models=function(e,t){if(r.isObject(e)){var n;t.definitions=t.definitions||{};for(n in e){var i,a=e[n],o=[],s={properties:{}};for(i in a.properties){var l=a.properties[i],u={};this.dataType(l,u),l.description&&(u.description=l.description),l["enum"]&&(u["enum"]=l["enum"]),"boolean"==typeof l.required&&l.required===!0&&o.push(i),"string"==typeof l.required&&"true"===l.required&&o.push(i),s.properties[i]=u}o.length>0?s.required=o:s.required=a.required,t.definitions[n]=s}}},a.prototype.extractTag=function(e){var t=e||"default";return 0!==t.indexOf("http:")&&0!==t.indexOf("https:")||(t=t.split(["/"]),t=t[t.length-1].substring()),t.endsWith(".json")&&(t=t.substring(0,t.length-".json".length)),t.replace("/","")},a.prototype.operations=function(e,t,n,i,r){if(Array.isArray(n)){var a;r.paths||(r.paths={});var o=r.paths[e]||{},s=this.extractTag(t);r.tags=r.tags||[];var l=!1;for(a=0;a<r.tags.length;a++){var u=r.tags[a];u.name===s&&(l=!0)}for(l||r.tags.push({name:s}),a=0;a<n.length;a++){var c=n[a],p=(c.method||c.httpMethod).toLowerCase(),h={tags:[s]},f=c.authorizations;if(f&&0===Object.keys(f).length&&(f=i),"undefined"!=typeof f){var d;for(var m in f){h.security=h.security||[];var y=f[m];if(y){var g=[];for(var v in y)g.push(y[v].scope);d={},d[m]=g,h.security.push(d)}else d={},d[m]=[],h.security.push(d)}}c.consumes?h.consumes=c.consumes:r.consumes&&(h.consumes=r.consumes),c.produces?h.produces=c.produces:r.produces&&(h.produces=r.produces),c.summary&&(h.summary=c.summary),c.notes&&(h.description=c.notes),c.nickname&&(h.operationId=c.nickname),c.deprecated&&(h.deprecated=c.deprecated),this.authorizations(f,r),this.parameters(h,c.parameters,r),this.responseMessages(h,c,r),o[p]=h}r.paths[e]=o}},a.prototype.responseMessages=function(e,t){if(r.isObject(t)){var n={};this.dataType(t,n),!n.schema&&n.type&&(n={schema:n}),e.responses=e.responses||{};var i=!1;if(Array.isArray(t.responseMessages)){var a,o=t.responseMessages;for(a=0;a<o.length;a++){var s=o[a],l={description:s.message};200===s.code&&(i=!0),s.responseModel&&(l.schema={$ref:"#/definitions/"+s.responseModel}),e.responses[""+s.code]=l}}i?e.responses["default"]=n:e.responses[200]=n}},a.prototype.authorizations=function(e){!r.isObject(e)},a.prototype.parameters=function(e,t){if(Array.isArray(t)){var n;for(n=0;n<t.length;n++){var i=t[n],r={};if(r.name=i.name,r.description=i.description,r.required=i.required,r["in"]=i.paramType,"body"===r["in"]&&(r.name="body"),"form"===r["in"]&&(r["in"]="formData"),i["enum"]&&(r["enum"]=i["enum"]),i.allowMultiple===!0||"true"===i.allowMultiple){var a={};if(this.dataType(i,a),r.type="array",r.items=a,i.allowableValues){var o=i.allowableValues;"LIST"===o.valueType&&(r["enum"]=o.values)}}else this.dataType(i,r);"undefined"!=typeof i.defaultValue&&(r["default"]=i.defaultValue),e.parameters=e.parameters||[],e.parameters.push(r)}}},a.prototype.dataType=function(e,t){if(r.isObject(e)){e.minimum&&(t.minimum=e.minimum),e.maximum&&(t.maximum=e.maximum),e.format&&(t.format=e.format),"undefined"!=typeof e.defaultValue&&(t["default"]=e.defaultValue);var n=this.toJsonSchema(e);n&&(t=t||{},n.type&&(t.type=n.type),n.format&&(t.format=n.format),n.$ref&&(t.schema={$ref:n.$ref}),n.items&&(t.items=n.items))}},a.prototype.toJsonSchema=function(e){if(!e)return"object";var t=e.type||e.dataType||e.responseClass||"",n=t.toLowerCase(),i=(e.format||"").toLowerCase();if(0===n.indexOf("list[")){var r=t.substring(5,t.length-1),a=this.toJsonSchema({type:r});return{type:"array",items:a}}if("int"===n||"integer"===n&&"int32"===i)return{type:"integer",format:"int32"};if("long"===n||"integer"===n&&"int64"===i)return{type:"integer",format:"int64"};if("integer"===n)return{type:"integer",format:"int64"};if("float"===n||"number"===n&&"float"===i)return{type:"number",format:"float"};if("double"===n||"number"===n&&"double"===i)return{type:"number",format:"double"};if("string"===n&&"date-time"===i||"date"===n)return{type:"string",format:"date-time"};if("string"===n)return{type:"string"};if("file"===n)return{type:"file"};if("boolean"===n)return{type:"boolean"};if("boolean"===n)return{type:"boolean"};if("array"===n||"list"===n){if(e.items){var o=this.toJsonSchema(e.items);return{type:"array",items:o}}return{type:"array",items:{type:"object"}}}return e.$ref?{$ref:this.modelMap[e.$ref]?"#/definitions/"+this.modelMap[e.$ref]:e.$ref}:"void"===n||""===n?{}:this.modelMap[e.type]?{$ref:"#/definitions/"+this.modelMap[e.type]}:{type:e.type}},a.prototype.resourceListing=function(e,t,n,r){var a,o=0,s=this,l=e.apis.length,u=t,c={};n&&n.requestInterceptor&&(c.requestInterceptor=n.requestInterceptor),n&&n.responseInterceptor&&(c.responseInterceptor=n.responseInterceptor);var p="application/json";for(n&&n.swaggerRequestHeaders&&(p=n.swaggerRequestHeaders),0===l&&this.finish(r,t),a=0;l>a;a++){var h=e.apis[a],f=h.path,d=this.getAbsolutePath(e.swaggerVersion,this.docLocation,f);h.description&&(t.tags=t.tags||[],t.tags.push({name:this.extractTag(h.path),description:h.description||""}));var m={url:d,headers:{accept:p},on:{},method:"get"};m.on.response=function(e){o+=1;var t=e.obj;t&&s.declaration(t,u),o===l&&s.finish(r,u)},m.on.error=function(e){console.error(e),o+=1,o===l&&s.finish(r,u)},this.clientAuthorizations&&"function"==typeof this.clientAuthorizations.apply&&this.clientAuthorizations.apply(m),(new i).execute(m,c)}},a.prototype.getAbsolutePath=function(e,t,n){if("1.0"===e&&t.endsWith(".json")){var i=t.lastIndexOf("/");i>0&&(t=t.substring(0,i))}var r=t;return 0===n.indexOf("http://")||0===n.indexOf("https://")?r=n:(t.endsWith("/")&&(r=t.substring(0,t.length-1)),r+=n),r=r.replace("{format}","json")},a.prototype.securityDefinitions=function(e,t){if(e.authorizations){var n;for(n in e.authorizations){var i=!1,r={},a=e.authorizations[n];if("apiKey"===a.type)r.type="apiKey",r["in"]=a.passAs,r.name=a.keyname||n,i=!0;else if("basicAuth"===a.type)r.type="basicAuth",i=!0;else if("oauth2"===a.type){var o,s=a.scopes||[],l={};for(o in s){var u=s[o];l[u.scope]=u.description}if(r.type="oauth2",o>0&&(r.scopes=l),a.grantTypes){if(a.grantTypes.implicit){var c=a.grantTypes.implicit;r.flow="implicit",r.authorizationUrl=c.loginEndpoint,i=!0}if(a.grantTypes.authorization_code&&!r.flow){var p=a.grantTypes.authorization_code;r.flow="accessCode",r.authorizationUrl=p.tokenRequestEndpoint.url,r.tokenUrl=p.tokenEndpoint.url,i=!0}}}i&&(t.securityDefinitions=t.securityDefinitions||{},t.securityDefinitions[n]=r)}}},a.prototype.apiInfo=function(e,t){if(e.info){var n=e.info;t.info={},n.contact&&(t.info.contact={},t.info.contact.email=n.contact),n.description&&(t.info.description=n.description),n.title&&(t.info.title=n.title),n.termsOfServiceUrl&&(t.info.termsOfService=n.termsOfServiceUrl),(n.license||n.licenseUrl)&&(t.license={},n.license&&(t.license.name=n.license),n.licenseUrl&&(t.license.url=n.licenseUrl))}else this.warnings.push("missing info section")},a.prototype.finish=function(e,t){e(t)}},{"./http":5,"lodash-compat/lang/isObject":144}],9:[function(e,t,n){"use strict";var i=(e("../helpers").log,{isPlainObject:e("lodash-compat/lang/isPlainObject"),isString:e("lodash-compat/lang/isString")}),r=e("../schema-markup.js"),a=e("js-yaml"),o=t.exports=function(e,t,n,i){return this.definition=t||{},this.isArray="array"===t.type,this.models=n||{},this.name=e||t.title||"Inline Model",this.modelPropertyMacro=i||function(e){return e["default"]},this};o.prototype.createJSONSample=o.prototype.getSampleValue=function(e){return e=e||{},e[this.name]=this,this.examples&&i.isPlainObject(this.examples)&&this.examples["application/json"]?(this.definition.example=this.examples["application/json"],
+i.isString(this.definition.example)&&(this.definition.example=a.safeLoad(this.definition.example))):this.definition.example||(this.definition.example=this.examples),r.schemaToJSON(this.definition,this.models,e,this.modelPropertyMacro)},o.prototype.getMockSignature=function(){return r.schemaToHTML(this.name,this.definition,this.models,this.modelPropertyMacro)}},{"../helpers":4,"../schema-markup.js":7,"js-yaml":19,"lodash-compat/lang/isPlainObject":145,"lodash-compat/lang/isString":146}],10:[function(e,t,n){"use strict";function i(e,t){if(r.isEmpty(t))return e[0];for(var n=0,i=t.length;i>n;n++)if(e.indexOf(t[n])>-1)return t[n];return e[0]}var r={cloneDeep:e("lodash-compat/lang/cloneDeep"),isUndefined:e("lodash-compat/lang/isUndefined"),isEmpty:e("lodash-compat/lang/isEmpty"),isObject:e("lodash-compat/lang/isObject")},a=e("../helpers"),o=e("./model"),s=e("../http"),l=e("q"),u=t.exports=function(e,t,n,i,r,a,s,l,u){var c=[];if(e=e||{},a=a||{},e&&e.options&&(this.client=e.options.client||null,this.requestInterceptor=e.options.requestInterceptor||null,this.responseInterceptor=e.options.responseInterceptor||null),this.authorizations=a.security,this.basePath=e.basePath||"/",this.clientAuthorizations=u,this.consumes=a.consumes||e.consumes||["application/json"],this.produces=a.produces||e.produces||["application/json"],this.deprecated=a.deprecated,this.description=a.description,this.host=e.host||"localhost",this.method=i||c.push("Operation "+n+" is missing method."),this.models=l||{},this.nickname=n||c.push("Operations must have a nickname."),this.operation=a,this.operations={},this.parameters=null!==a?a.parameters||[]:{},this.parent=e,this.path=r||c.push("Operation "+this.nickname+" is missing path."),this.responses=a.responses||{},this.scheme=t||e.scheme||"http",this.schemes=a.schemes||e.schemes,this.security=a.security||e.security,this.summary=a.summary||"",this.type=null,this.useJQuery=e.useJQuery,this.jqueryAjaxCache=e.jqueryAjaxCache,this.enableCookies=e.enableCookies,this.parameterMacro=e.parameterMacro||function(e,t){return t["default"]},this.inlineModels=[],"/"!==this.basePath&&"/"==this.basePath.slice(-1)&&(this.basePath=this.basePath.slice(0,-1)),"string"==typeof this.deprecated)switch(this.deprecated.toLowerCase()){case"true":case"yes":case"1":this.deprecated=!0;break;case"false":case"no":case"0":case null:this.deprecated=!1;break;default:this.deprecated=Boolean(this.deprecated)}var p,h;if(s){var f;for(f in s)h=new o(f,s[f],this.models,e.modelPropertyMacro),h&&(this.models[f]=h)}else s={};for(p=0;p<this.parameters.length;p++){var d=this.parameters[p];d["default"]=this.parameterMacro(this,d),"array"===d.type&&(d.isList=!0,d.allowMultiple=!0);var m=this.getType(d);if(m&&"boolean"===m.toString().toLowerCase()&&(d.allowableValues={},d.isList=!0,d["enum"]=[!0,!1]),"undefined"!=typeof d["x-example"]){var y=d["x-example"];d["default"]=y}if(d["x-examples"]){var y=d["x-examples"]["default"];"undefined"!=typeof y&&(d["default"]=y)}var g=d["enum"]||d.items&&d.items["enum"];if("undefined"!=typeof g){var v;for(d.allowableValues={},d.allowableValues.values=[],d.allowableValues.descriptiveValues=[],v=0;v<g.length;v++){var b=g[v],w=b===d["default"]||b+""===d["default"];d.allowableValues.values.push(b),d.allowableValues.descriptiveValues.push({value:b+"",isDefault:w})}}"array"===d.type&&(m=[m],"undefined"==typeof d.allowableValues&&(delete d.isList,delete d.allowMultiple)),d.modelSignature={type:m,definitions:this.models},d.signature=this.getModelSignature(m,this.models).toString(),d.sampleJSON=this.getModelSampleJSON(m,this.models),d.responseClassSignature=d.signature}var x,A,j=this.responses;if(j[200]?(A=j[200],x="200"):j[201]?(A=j[201],x="201"):j[202]?(A=j[202],x="202"):j[203]?(A=j[203],x="203"):j[204]?(A=j[204],x="204"):j[205]?(A=j[205],x="205"):j[206]?(A=j[206],x="206"):j["default"]&&(A=j["default"],x="default"),A&&A.schema){var O,_=this.resolveModel(A.schema,s);delete j[x],_?(this.successResponse={},O=this.successResponse[x]=_):A.schema.type&&"object"!==A.schema.type&&"array"!==A.schema.type?(this.successResponse={},O=this.successResponse[x]=A.schema):(this.successResponse={},O=this.successResponse[x]=new o(void 0,A.schema||{},this.models,e.modelPropertyMacro)),O&&(A.description&&(O.description=A.description),A.examples&&(O.examples=A.examples),A.headers&&(O.headers=A.headers)),this.type=A}return c.length>0&&this.resource&&this.resource.api&&this.resource.api.fail&&this.resource.api.fail(c),this};u.prototype.isDefaultArrayItemValue=function(e,t){return t["default"]&&Array.isArray(t["default"])?-1!==t["default"].indexOf(e):e===t["default"]},u.prototype.getType=function(e){var t,n=e.type,i=e.format,r=!1;"integer"===n&&"int32"===i?t="integer":"integer"===n&&"int64"===i?t="long":"integer"===n?t="integer":"string"===n?t="date-time"===i?"date-time":"date"===i?"date":"string":"number"===n&&"float"===i?t="float":"number"===n&&"double"===i?t="double":"number"===n?t="double":"boolean"===n?t="boolean":"array"===n?(r=!0,e.items&&(t=this.getType(e.items))):"file"===n&&(t="file"),e.$ref&&(t=a.simpleRef(e.$ref));var o=e.schema;if(o){var s=o.$ref;return s?(s=a.simpleRef(s),r?[s]:s):"object"===o.type?this.addInlineModel(o):this.getType(o)}return r?[t]:t},u.prototype.addInlineModel=function(e){var t=this.inlineModels.length,n=this.resolveModel(e,{});return n?(this.inlineModels.push(n),"Inline Model "+t):null},u.prototype.getInlineModel=function(e){if(/^Inline Model \d+$/.test(e)){var t=parseInt(e.substr("Inline Model".length).trim(),10),n=this.inlineModels[t];return n}return null},u.prototype.resolveModel=function(e,t){if("undefined"!=typeof e.$ref){var n=e.$ref;if(0===n.indexOf("#/definitions/")&&(n=n.substring("#/definitions/".length)),t[n])return new o(n,t[n],this.models,this.parent.modelPropertyMacro)}else if(e&&"object"==typeof e&&("object"===e.type||r.isUndefined(e.type)))return new o(void 0,e,this.models,this.parent.modelPropertyMacro);return null},u.prototype.help=function(e){for(var t=this.nickname+": "+this.summary+"\n",n=0;n<this.parameters.length;n++){var i=this.parameters[n],r=i.signature;t+="\n  * "+i.name+" ("+r+"): "+i.description}return"undefined"==typeof e&&a.log(t),t},u.prototype.getModelSignature=function(e,t){var n,i;return e instanceof Array&&(i=!0,e=e[0]),"undefined"==typeof e?(e="undefined",n=!0):t[e]?(e=t[e],n=!1):this.getInlineModel(e)?(e=this.getInlineModel(e),n=!1):n=!0,n?i?"Array["+e+"]":e.toString():i?"Array["+e.getMockSignature()+"]":e.getMockSignature()},u.prototype.supportHeaderParams=function(){return!0},u.prototype.supportedSubmitMethods=function(){return this.parent.supportedSubmitMethods},u.prototype.getHeaderParams=function(e){for(var t=this.setContentTypes(e,{}),n=0;n<this.parameters.length;n++){var i=this.parameters[n];if("undefined"!=typeof e[i.name]&&"header"===i["in"]){var r=e[i.name];Array.isArray(r)&&(r=r.toString()),t[i.name]=r}}return t},u.prototype.urlify=function(e){for(var t={},n=this.path.replace(/#.*/,""),i="",r=0;r<this.parameters.length;r++){var a=this.parameters[r];if("undefined"!=typeof e[a.name])if("path"===a["in"]){var o=new RegExp("{"+a.name+"}","gi"),s=e[a.name];s=Array.isArray(s)?this.encodePathCollection(a.collectionFormat,a.name,s):this.encodePathParam(s),n=n.replace(o,s)}else if("query"===a["in"]&&"undefined"!=typeof e[a.name])if(i+=""===i&&n.indexOf("?")<0?"?":"&","undefined"!=typeof a.collectionFormat){var l=e[a.name];i+=Array.isArray(l)?this.encodeQueryCollection(a.collectionFormat,a.name,l):this.encodeQueryKey(a.name)+"="+this.encodeQueryParam(e[a.name])}else i+=this.encodeQueryKey(a.name)+"="+this.encodeQueryParam(e[a.name]);else"formData"===a["in"]&&(t[a.name]=e[a.name])}var u=this.scheme+"://"+this.host;return"/"!==this.basePath&&(u+=this.basePath),u+n+i},u.prototype.getMissingParams=function(e){var t,n=[];for(t=0;t<this.parameters.length;t++){var i=this.parameters[t];i.required===!0&&"undefined"==typeof e[i.name]&&(n=i.name)}return n},u.prototype.getBody=function(e,t,n){for(var i,r,a,o,s={},l=!1,u=0;u<this.parameters.length;u++){var c=this.parameters[u];"undefined"!=typeof t[c.name]?"body"===c["in"]?r=t[c.name]:"formData"===c["in"]&&(s[c.name]={param:c,value:t[c.name]},i=!0):"body"===c["in"]&&(l=!0)}if(l&&"undefined"==typeof r){var p=e["Content-Type"];p&&0===p.indexOf("application/json")&&(r="{}")}var h=!1;if(e["Content-Type"]&&e["Content-Type"].indexOf("multipart/form-data")>=0&&(h=!0),i&&!h){var f="";for(a in s){var c=s[a].param;o=s[a].value,"undefined"!=typeof o&&(Array.isArray(o)?(""!==f&&(f+="&"),f+=this.encodeQueryCollection(c.collectionFormat,a,o)):(""!==f&&(f+="&"),f+=encodeURIComponent(a)+"="+encodeURIComponent(o)))}r=f}else if(h){if("function"==typeof FormData){var d=new FormData;d.type="formData";for(a in s)o=t[a],"undefined"!=typeof o&&("[object File]"==={}.toString.apply(o)?d.append(a,o):"file"===o.type&&o.value?d.append(a,o.value):Array.isArray(o)?d.append(a,this.encodeQueryCollection(c.collectionFormat,a,o)):d.append(a,o));r=d}else{d={};for(a in s)if(o=t[a],Array.isArray(o)){var m,y=c.collectionFormat||"multi";m="ssv"===y?" ":"pipes"===y?"|":"tsv"===y?"	":",";var g;o.forEach(function(e){g?g+=m:g="",g+=e}),d[a]=g}else d[a]=o;r=d}e["Content-Type"]="multipart/form-data"}return r},u.prototype.getModelSampleJSON=function(e,t){var n,i,a;if(t=t||{},n=e instanceof Array,a=n?e[0]:e,t[a]?i=t[a].createJSONSample():this.getInlineModel(a)&&(i=this.getInlineModel(a).createJSONSample()),i){if(i=n?[i]:i,"string"==typeof i)return i;if(r.isObject(i)){var o=i;if(i instanceof Array&&i.length>0&&(o=i[0]),o.nodeName&&"Node"==typeof o){var s=(new XMLSerializer).serializeToString(o);return this.formatXml(s)}return JSON.stringify(i,null,2)}return i}},u.prototype["do"]=function(e,t,n,i,r){return this.execute(e,t,n,i,r)},u.prototype.execute=function(e,t,n,i,o){var u,c,p,h=e||{},f={};r.isObject(t)&&(f=t,u=n,c=i),this.client&&(f.client=this.client),!f.requestInterceptor&&this.requestInterceptor&&(f.requestInterceptor=this.requestInterceptor),!f.responseInterceptor&&this.responseInterceptor&&(f.responseInterceptor=this.responseInterceptor),"function"==typeof t&&(u=t,c=n),this.parent.usePromise?p=l.defer():(u=u||this.parent.defaultSuccessCallback||a.log,c=c||this.parent.defaultErrorCallback||a.log),"undefined"==typeof f.useJQuery&&(f.useJQuery=this.useJQuery),"undefined"==typeof f.jqueryAjaxCache&&(f.jqueryAjaxCache=this.jqueryAjaxCache),"undefined"==typeof f.enableCookies&&(f.enableCookies=this.enableCookies);var d=this.getMissingParams(h);if(d.length>0){var m="missing required params: "+d;return a.fail(m),this.parent.usePromise?(p.reject(m),p.promise):(c(m,o),{})}var y,g=this.getHeaderParams(h),v=this.setContentTypes(h,f),b={};for(y in g)b[y]=g[y];for(y in v)b[y]=v[y];var w=this.getBody(v,h,f),x=this.urlify(h);if(x.indexOf(".{format}")>0&&b){var A=b.Accept||b.accept;A&&A.indexOf("json")>0?x=x.replace(".{format}",".json"):A&&A.indexOf("xml")>0&&(x=x.replace(".{format}",".xml"))}var j={url:x,method:this.method.toUpperCase(),body:w,enableCookies:f.enableCookies,useJQuery:f.useJQuery,jqueryAjaxCache:f.jqueryAjaxCache,deferred:p,headers:b,clientAuthorizations:f.clientAuthorizations,on:{response:function(e){return p?(p.resolve(e),p.promise):u(e,o)},error:function(e){return p?(p.reject(e),p.promise):c(e,o)}}};return this.clientAuthorizations.apply(j,this.operation.security),f.mock===!0?j:(new s).execute(j,f)},u.prototype.setContentTypes=function(e,t){var n,r,o=this.parameters,s=e.parameterContentType||i(this.consumes,["application/json","application/yaml"]),l=t.responseContentType||i(this.produces,["application/json","application/yaml"]),u=[],c=[],p={};for(r=0;r<o.length;r++){var h=o[r];if("formData"===h["in"])"file"===h.type?u.push(h):c.push(h);else if("header"===h["in"]&&t){var f=h.name,d=t[h.name];"undefined"!=typeof t[h.name]&&(p[f]=d)}else"body"===h["in"]&&"undefined"!=typeof e[h.name]&&(n=e[h.name])}var m=n||u.length||c.length;if("post"===this.method||"put"===this.method||"patch"===this.method||("delete"===this.method||"get"===this.method)&&m){if(t.requestContentType&&(s=t.requestContentType),c.length>0){if(s=void 0,t.requestContentType)s=t.requestContentType;else if(u.length>0)s="multipart/form-data";else if(this.consumes&&this.consumes.length>0)for(var y in this.consumes){var g=this.consumes[y];0!==g.indexOf("application/x-www-form-urlencoded")&&0!==g.indexOf("multipart/form-data")||(s=g)}"undefined"==typeof s&&(s="application/x-www-form-urlencoded")}}else s=null;return s&&this.consumes&&-1===this.consumes.indexOf(s)&&a.log("server doesn't consume "+s+", try "+JSON.stringify(this.consumes)),this.matchesAccept(l)||a.log("server can't produce "+l),s&&""!==n||"application/x-www-form-urlencoded"===s?p["Content-Type"]=s:this.consumes&&this.consumes.length>0&&"application/x-www-form-urlencoded"===this.consumes[0]&&(p["Content-Type"]=this.consumes[0]),l&&(p.Accept=l),p},u.prototype.matchesAccept=function(e){return e&&this.produces?-1!==this.produces.indexOf(e)||-1!==this.produces.indexOf("*/*"):!0},u.prototype.asCurl=function(e,t){var n={mock:!0};if("object"==typeof t)for(var i in t)n[i]=t[i];var a=this.execute(e,n);this.clientAuthorizations.apply(a,this.operation.security);var o=[];if(o.push("-X "+this.method.toUpperCase()),"undefined"!=typeof a.headers){var s;for(s in a.headers){var l=a.headers[s];"string"==typeof l&&(l=l.replace(/\'/g,"\\u0027")),o.push("--header '"+s+": "+l+"'")}}var u=!1,c=!1,p=a.headers["Content-Type"];if(p&&0===p.indexOf("application/x-www-form-urlencoded")?u=!0:p&&0===p.indexOf("multipart/form-data")&&(u=!0,c=!0),a.body){var h;if(r.isObject(a.body)){if(c){c=!0;for(var f=0;f<this.parameters.length;f++){var d=this.parameters[f];if("formData"===d["in"]){h||(h="");var m;m="function"==typeof FormData&&a.body instanceof FormData?a.body.get(d.name):a.body[d.name],m&&("file"===d.type?m.name&&(h+="-F "+d.name+'=@"'+m.name+'" '):(h+="-F ",h+=Array.isArray(m)?this.encodeQueryCollection(d.collectionFormat,d.name,m):this.encodeQueryKey(d.name)+"="+m,h+=" "))}}}h||(h=JSON.stringify(a.body))}else h=a.body;h=h.replace(/\'/g,"%27").replace(/\n/g," \\ \n "),u||(h=h.replace(/&/g,"%26")),c?o.push(h):o.push("-d '"+h.replace(/@/g,"%40")+"'")}return"curl "+o.join(" ")+" '"+a.url+"'"},u.prototype.encodePathCollection=function(e,t,n){var i,r="",a="";for(a="ssv"===e?"%20":"tsv"===e?"%09":"pipes"===e?"|":",",i=0;i<n.length;i++)0===i?r=this.encodeQueryParam(n[i]):r+=a+this.encodeQueryParam(n[i]);return r},u.prototype.encodeQueryCollection=function(e,t,n){var i,r="";if(e=e||"default","default"===e||"multi"===e)for(i=0;i<n.length;i++)i>0&&(r+="&"),r+=this.encodeQueryKey(t)+"="+this.encodeQueryParam(n[i]);else{var a="";if("csv"===e)a=",";else if("ssv"===e)a="%20";else if("tsv"===e)a="%09";else if("pipes"===e)a="|";else if("brackets"===e)for(i=0;i<n.length;i++)0!==i&&(r+="&"),r+=this.encodeQueryKey(t)+"[]="+this.encodeQueryParam(n[i]);if(""!==a)for(i=0;i<n.length;i++)0===i?r=this.encodeQueryKey(t)+"="+this.encodeQueryParam(n[i]):r+=a+this.encodeQueryParam(n[i])}return r},u.prototype.encodeQueryKey=function(e){return encodeURIComponent(e).replace("%5B","[").replace("%5D","]").replace("%24","$")},u.prototype.encodeQueryParam=function(e){return encodeURIComponent(e)},u.prototype.encodePathParam=function(e){return encodeURIComponent(e)}},{"../helpers":4,"../http":5,"./model":9,"lodash-compat/lang/cloneDeep":138,"lodash-compat/lang/isEmpty":141,"lodash-compat/lang/isObject":144,"lodash-compat/lang/isUndefined":148,q:157}],11:[function(e,t,n){"use strict";var i=t.exports=function(e,t,n,i){this.description=t,this.externalDocs=n,this.name=e,this.operation=i,this.operationsArray=[],this.path=e,this.tag=e};i.prototype.sort=function(){}},{}],12:[function(e,t,n){function i(){if(!s){s=!0;for(var e,t=o.length;t;){e=o,o=[];for(var n=-1;++n<t;)e[n]();t=o.length}s=!1}}function r(){}var a=t.exports={},o=[],s=!1;a.nextTick=function(e){o.push(e),s||setTimeout(i,0)},a.title="browser",a.browser=!0,a.env={},a.argv=[],a.version="",a.versions={},a.on=r,a.addListener=r,a.once=r,a.off=r,a.removeListener=r,a.removeAllListeners=r,a.emit=r,a.binding=function(e){throw new Error("process.binding is not supported")},a.cwd=function(){return"/"},a.chdir=function(e){throw new Error("process.chdir is not supported")},a.umask=function(){return 0}},{}],13:[function(e,t,n){(function(e){!function(){"use strict";function n(t){var n;return n=t instanceof e?t:new e(t.toString(),"binary"),n.toString("base64")}t.exports=n}()}).call(this,e("buffer").Buffer)},{buffer:14}],14:[function(e,t,n){function i(){return r.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function r(e){return this instanceof r?(this.length=0,this.parent=void 0,"number"==typeof e?a(this,e):"string"==typeof e?o(this,e,arguments.length>1?arguments[1]:"utf8"):s(this,e)):arguments.length>1?new r(e,arguments[1]):new r(e)}function a(e,t){if(e=d(e,0>t?0:0|m(t)),!r.TYPED_ARRAY_SUPPORT)for(var n=0;t>n;n++)e[n]=0;return e}function o(e,t,n){"string"==typeof n&&""!==n||(n="utf8");var i=0|g(t,n);return e=d(e,i),e.write(t,n),e}function s(e,t){if(r.isBuffer(t))return l(e,t);if(K(t))return u(e,t);if(null==t)throw new TypeError("must start with number, buffer, array or string");if("undefined"!=typeof ArrayBuffer){if(t.buffer instanceof ArrayBuffer)return c(e,t);if(t instanceof ArrayBuffer)return p(e,t)}return t.length?h(e,t):f(e,t)}function l(e,t){var n=0|m(t.length);return e=d(e,n),t.copy(e,0,0,n),e}function u(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function c(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function p(e,t){return r.TYPED_ARRAY_SUPPORT?(t.byteLength,e=r._augment(new Uint8Array(t))):e=c(e,new Uint8Array(t)),e}function h(e,t){var n=0|m(t.length);e=d(e,n);for(var i=0;n>i;i+=1)e[i]=255&t[i];return e}function f(e,t){var n,i=0;"Buffer"===t.type&&K(t.data)&&(n=t.data,i=0|m(n.length)),e=d(e,i);for(var r=0;i>r;r+=1)e[r]=255&n[r];return e}function d(e,t){r.TYPED_ARRAY_SUPPORT?e=r._augment(new Uint8Array(t)):(e.length=t,e._isBuffer=!0);var n=0!==t&&t<=r.poolSize>>>1;return n&&(e.parent=W),e}function m(e){if(e>=i())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i().toString(16)+" bytes");return 0|e}function y(e,t){if(!(this instanceof y))return new y(e,t);var n=new r(e,t);return delete n.parent,n}function g(e,t){"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var i=!1;;)switch(t){case"ascii":case"binary":case"raw":case"raws":return n;case"utf8":case"utf-8":return q(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return H(e).length;default:if(i)return q(e).length;t=(""+t).toLowerCase(),i=!0}}function v(e,t,n){var i=!1;if(t=0|t,n=void 0===n||n===1/0?this.length:0|n,e||(e="utf8"),0>t&&(t=0),n>this.length&&(n=this.length),t>=n)return"";for(;;)switch(e){case"hex":return I(this,t,n);case"utf8":case"utf-8":return S(this,t,n);case"ascii":return C(this,t,n);case"binary":return E(this,t,n);case"base64":return _(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(i)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),i=!0}}function b(e,t,n,i){n=Number(n)||0;var r=e.length-n;i?(i=Number(i),i>r&&(i=r)):i=r;var a=t.length;if(a%2!==0)throw new Error("Invalid hex string");i>a/2&&(i=a/2);for(var o=0;i>o;o++){var s=parseInt(t.substr(2*o,2),16);if(isNaN(s))throw new Error("Invalid hex string");e[n+o]=s}return o}function w(e,t,n,i){return J(q(t,e.length-n),e,n,i)}function x(e,t,n,i){return J(V(t),e,n,i)}function A(e,t,n,i){return x(e,t,n,i)}function j(e,t,n,i){return J(H(t),e,n,i)}function O(e,t,n,i){return J(z(t,e.length-n),e,n,i)}function _(e,t,n){return 0===t&&n===e.length?Q.fromByteArray(e):Q.fromByteArray(e.slice(t,n))}function S(e,t,n){n=Math.min(e.length,n);for(var i=[],r=t;n>r;){var a=e[r],o=null,s=a>239?4:a>223?3:a>191?2:1;if(n>=r+s){var l,u,c,p;switch(s){case 1:128>a&&(o=a);break;case 2:l=e[r+1],128===(192&l)&&(p=(31&a)<<6|63&l,p>127&&(o=p));break;case 3:l=e[r+1],u=e[r+2],128===(192&l)&&128===(192&u)&&(p=(15&a)<<12|(63&l)<<6|63&u,p>2047&&(55296>p||p>57343)&&(o=p));break;case 4:l=e[r+1],u=e[r+2],c=e[r+3],128===(192&l)&&128===(192&u)&&128===(192&c)&&(p=(15&a)<<18|(63&l)<<12|(63&u)<<6|63&c,p>65535&&1114112>p&&(o=p))}}null===o?(o=65533,s=1):o>65535&&(o-=65536,i.push(o>>>10&1023|55296),o=56320|1023&o),i.push(o),r+=s}return k(i)}function k(e){var t=e.length;if(X>=t)return String.fromCharCode.apply(String,e);for(var n="",i=0;t>i;)n+=String.fromCharCode.apply(String,e.slice(i,i+=X));return n}function C(e,t,n){var i="";n=Math.min(e.length,n);for(var r=t;n>r;r++)i+=String.fromCharCode(127&e[r]);return i}function E(e,t,n){var i="";n=Math.min(e.length,n);for(var r=t;n>r;r++)i+=String.fromCharCode(e[r]);return i}function I(e,t,n){var i=e.length;(!t||0>t)&&(t=0),(!n||0>n||n>i)&&(n=i);for(var r="",a=t;n>a;a++)r+=B(e[a]);return r}function T(e,t,n){for(var i=e.slice(t,n),r="",a=0;a<i.length;a+=2)r+=String.fromCharCode(i[a]+256*i[a+1]);return r}function $(e,t,n){if(e%1!==0||0>e)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function M(e,t,n,i,a,o){if(!r.isBuffer(e))throw new TypeError("buffer must be a Buffer instance");if(t>a||o>t)throw new RangeError("value is out of bounds");if(n+i>e.length)throw new RangeError("index out of range")}function U(e,t,n,i){0>t&&(t=65535+t+1);for(var r=0,a=Math.min(e.length-n,2);a>r;r++)e[n+r]=(t&255<<8*(i?r:1-r))>>>8*(i?r:1-r)}function P(e,t,n,i){0>t&&(t=4294967295+t+1);for(var r=0,a=Math.min(e.length-n,4);a>r;r++)e[n+r]=t>>>8*(i?r:3-r)&255}function L(e,t,n,i,r,a){if(t>r||a>t)throw new RangeError("value is out of bounds");if(n+i>e.length)throw new RangeError("index out of range");if(0>n)throw new RangeError("index out of range")}function D(e,t,n,i,r){return r||L(e,t,n,4,3.4028234663852886e38,-3.4028234663852886e38),Y.write(e,t,n,i,23,4),n+4}function R(e,t,n,i,r){return r||L(e,t,n,8,1.7976931348623157e308,-1.7976931348623157e308),Y.write(e,t,n,i,52,8),n+8}function N(e){if(e=F(e).replace(Z,""),e.length<2)return"";for(;e.length%4!==0;)e+="=";return e}function F(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function B(e){return 16>e?"0"+e.toString(16):e.toString(16)}function q(e,t){t=t||1/0;for(var n,i=e.length,r=null,a=[],o=0;i>o;o++){if(n=e.charCodeAt(o),n>55295&&57344>n){if(!r){if(n>56319){(t-=3)>-1&&a.push(239,191,189);continue}if(o+1===i){(t-=3)>-1&&a.push(239,191,189);continue}r=n;continue}if(56320>n){(t-=3)>-1&&a.push(239,191,189),r=n;continue}n=r-55296<<10|n-56320|65536}else r&&(t-=3)>-1&&a.push(239,191,189);if(r=null,128>n){if((t-=1)<0)break;a.push(n)}else if(2048>n){if((t-=2)<0)break;a.push(n>>6|192,63&n|128)}else if(65536>n){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(1114112>n))throw new Error("Invalid code point");if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return a}function V(e){for(var t=[],n=0;n<e.length;n++)t.push(255&e.charCodeAt(n));return t}function z(e,t){for(var n,i,r,a=[],o=0;o<e.length&&!((t-=2)<0);o++)n=e.charCodeAt(o),i=n>>8,r=n%256,a.push(r),a.push(i);return a}function H(e){return Q.toByteArray(N(e))}function J(e,t,n,i){for(var r=0;i>r&&!(r+n>=t.length||r>=e.length);r++)t[r+n]=e[r];return r}var Q=e("base64-js"),Y=e("ieee754"),K=e("is-array");n.Buffer=r,n.SlowBuffer=y,n.INSPECT_MAX_BYTES=50,r.poolSize=8192;var W={};r.TYPED_ARRAY_SUPPORT=function(){function e(){}try{var t=new Uint8Array(1);return t.foo=function(){return 42},t.constructor=e,42===t.foo()&&t.constructor===e&&"function"==typeof t.subarray&&0===t.subarray(1,1).byteLength}catch(n){return!1}}(),r.isBuffer=function(e){return!(null==e||!e._isBuffer)},r.compare=function(e,t){if(!r.isBuffer(e)||!r.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,i=t.length,a=0,o=Math.min(n,i);o>a&&e[a]===t[a];)++a;return a!==o&&(n=e[a],i=t[a]),i>n?-1:n>i?1:0},r.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},r.concat=function(e,t){if(!K(e))throw new TypeError("list argument must be an Array of Buffers.");if(0===e.length)return new r(0);var n;if(void 0===t)for(t=0,n=0;n<e.length;n++)t+=e[n].length;var i=new r(t),a=0;for(n=0;n<e.length;n++){var o=e[n];o.copy(i,a),a+=o.length}return i},r.byteLength=g,r.prototype.length=void 0,r.prototype.parent=void 0,r.prototype.toString=function(){var e=0|this.length;return 0===e?"":0===arguments.length?S(this,0,e):v.apply(this,arguments)},r.prototype.equals=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?!0:0===r.compare(this,e)},r.prototype.inspect=function(){var e="",t=n.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,t).match(/.{2}/g).join(" "),this.length>t&&(e+=" ... ")),"<Buffer "+e+">"},r.prototype.compare=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?0:r.compare(this,e)},r.prototype.indexOf=function(e,t){function n(e,t,n){for(var i=-1,r=0;n+r<e.length;r++)if(e[n+r]===t[-1===i?0:r-i]){if(-1===i&&(i=r),r-i+1===t.length)return n+i}else i=-1;return-1}if(t>2147483647?t=2147483647:-2147483648>t&&(t=-2147483648),t>>=0,0===this.length)return-1;if(t>=this.length)return-1;if(0>t&&(t=Math.max(this.length+t,0)),"string"==typeof e)return 0===e.length?-1:String.prototype.indexOf.call(this,e,t);if(r.isBuffer(e))return n(this,e,t);if("number"==typeof e)return r.TYPED_ARRAY_SUPPORT&&"function"===Uint8Array.prototype.indexOf?Uint8Array.prototype.indexOf.call(this,e,t):n(this,[e],t);throw new TypeError("val must be string, number or Buffer")},r.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},r.prototype.set=function(e,t){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,t)},r.prototype.write=function(e,t,n,i){if(void 0===t)i="utf8",n=this.length,t=0;else if(void 0===n&&"string"==typeof t)i=t,n=this.length,t=0;else if(isFinite(t))t=0|t,isFinite(n)?(n=0|n,void 0===i&&(i="utf8")):(i=n,n=void 0);else{var r=i;i=t,t=0|n,n=r}var a=this.length-t;if((void 0===n||n>a)&&(n=a),e.length>0&&(0>n||0>t)||t>this.length)throw new RangeError("attempt to write outside buffer bounds");i||(i="utf8");for(var o=!1;;)switch(i){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return x(this,e,t,n);case"binary":return A(this,e,t,n);case"base64":return j(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return O(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+i);i=(""+i).toLowerCase(),o=!0}},r.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var X=4096;r.prototype.slice=function(e,t){var n=this.length;e=~~e,t=void 0===t?n:~~t,0>e?(e+=n,0>e&&(e=0)):e>n&&(e=n),0>t?(t+=n,0>t&&(t=0)):t>n&&(t=n),e>t&&(t=e);var i;if(r.TYPED_ARRAY_SUPPORT)i=r._augment(this.subarray(e,t));else{var a=t-e;i=new r(a,void 0);for(var o=0;a>o;o++)i[o]=this[o+e]}return i.length&&(i.parent=this.parent||this),i},r.prototype.readUIntLE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e],r=1,a=0;++a<t&&(r*=256);)i+=this[e+a]*r;return i},r.prototype.readUIntBE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e+--t],r=1;t>0&&(r*=256);)i+=this[e+--t]*r;return i},r.prototype.readUInt8=function(e,t){return t||$(e,1,this.length),this[e]},r.prototype.readUInt16LE=function(e,t){return t||$(e,2,this.length),this[e]|this[e+1]<<8},r.prototype.readUInt16BE=function(e,t){return t||$(e,2,this.length),this[e]<<8|this[e+1]},r.prototype.readUInt32LE=function(e,t){return t||$(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},r.prototype.readUInt32BE=function(e,t){return t||$(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},r.prototype.readIntLE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=this[e],r=1,a=0;++a<t&&(r*=256);)i+=this[e+a]*r;return r*=128,i>=r&&(i-=Math.pow(2,8*t)),i},r.prototype.readIntBE=function(e,t,n){e=0|e,t=0|t,n||$(e,t,this.length);for(var i=t,r=1,a=this[e+--i];i>0&&(r*=256);)a+=this[e+--i]*r;return r*=128,a>=r&&(a-=Math.pow(2,8*t)),a},r.prototype.readInt8=function(e,t){return t||$(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},r.prototype.readInt16LE=function(e,t){t||$(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt16BE=function(e,t){t||$(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt32LE=function(e,t){return t||$(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},r.prototype.readInt32BE=function(e,t){return t||$(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},r.prototype.readFloatLE=function(e,t){return t||$(e,4,this.length),Y.read(this,e,!0,23,4)},r.prototype.readFloatBE=function(e,t){return t||$(e,4,this.length),Y.read(this,e,!1,23,4)},r.prototype.readDoubleLE=function(e,t){return t||$(e,8,this.length),Y.read(this,e,!0,52,8)},r.prototype.readDoubleBE=function(e,t){return t||$(e,8,this.length),Y.read(this,e,!1,52,8)},r.prototype.writeUIntLE=function(e,t,n,i){e=+e,t=0|t,n=0|n,i||M(this,e,t,n,Math.pow(2,8*n),0);var r=1,a=0;for(this[t]=255&e;++a<n&&(r*=256);)this[t+a]=e/r&255;return t+n},r.prototype.writeUIntBE=function(e,t,n,i){e=+e,t=0|t,n=0|n,i||M(this,e,t,n,Math.pow(2,8*n),0);var r=n-1,a=1;for(this[t+r]=255&e;--r>=0&&(a*=256);)this[t+r]=e/a&255;return t+n},r.prototype.writeUInt8=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,1,255,0),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=e,t+1},r.prototype.writeUInt16LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):U(this,e,t,!0),t+2},r.prototype.writeUInt16BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):U(this,e,t,!1),t+2},r.prototype.writeUInt32LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=e):P(this,e,t,!0),t+4},r.prototype.writeUInt32BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):P(this,e,t,!1),t+4},r.prototype.writeIntLE=function(e,t,n,i){if(e=+e,t=0|t,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var a=0,o=1,s=0>e?1:0;for(this[t]=255&e;++a<n&&(o*=256);)this[t+a]=(e/o>>0)-s&255;return t+n},r.prototype.writeIntBE=function(e,t,n,i){if(e=+e,t=0|t,!i){var r=Math.pow(2,8*n-1);M(this,e,t,n,r-1,-r)}var a=n-1,o=1,s=0>e?1:0;for(this[t+a]=255&e;--a>=0&&(o*=256);)this[t+a]=(e/o>>0)-s&255;return t+n},r.prototype.writeInt8=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,1,127,-128),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),0>e&&(e=255+e+1),this[t]=e,t+1},r.prototype.writeInt16LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):U(this,e,t,!0),t+2},r.prototype.writeInt16BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):U(this,e,t,!1),t+2},r.prototype.writeInt32LE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,2147483647,-2147483648),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):P(this,e,t,!0),t+4},r.prototype.writeInt32BE=function(e,t,n){return e=+e,t=0|t,n||M(this,e,t,4,2147483647,-2147483648),0>e&&(e=4294967295+e+1),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):P(this,e,t,!1),t+4},r.prototype.writeFloatLE=function(e,t,n){return D(this,e,t,!0,n)},r.prototype.writeFloatBE=function(e,t,n){return D(this,e,t,!1,n)},r.prototype.writeDoubleLE=function(e,t,n){return R(this,e,t,!0,n)},r.prototype.writeDoubleBE=function(e,t,n){
+return R(this,e,t,!1,n)},r.prototype.copy=function(e,t,n,i){if(n||(n=0),i||0===i||(i=this.length),t>=e.length&&(t=e.length),t||(t=0),i>0&&n>i&&(i=n),i===n)return 0;if(0===e.length||0===this.length)return 0;if(0>t)throw new RangeError("targetStart out of bounds");if(0>n||n>=this.length)throw new RangeError("sourceStart out of bounds");if(0>i)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),e.length-t<i-n&&(i=e.length-t+n);var a,o=i-n;if(this===e&&t>n&&i>t)for(a=o-1;a>=0;a--)e[a+t]=this[a+n];else if(1e3>o||!r.TYPED_ARRAY_SUPPORT)for(a=0;o>a;a++)e[a+t]=this[a+n];else e._set(this.subarray(n,n+o),t);return o},r.prototype.fill=function(e,t,n){if(e||(e=0),t||(t=0),n||(n=this.length),t>n)throw new RangeError("end < start");if(n!==t&&0!==this.length){if(0>t||t>=this.length)throw new RangeError("start out of bounds");if(0>n||n>this.length)throw new RangeError("end out of bounds");var i;if("number"==typeof e)for(i=t;n>i;i++)this[i]=e;else{var r=q(e.toString()),a=r.length;for(i=t;n>i;i++)this[i]=r[i%a]}return this}},r.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(r.TYPED_ARRAY_SUPPORT)return new r(this).buffer;for(var e=new Uint8Array(this.length),t=0,n=e.length;n>t;t+=1)e[t]=this[t];return e.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser")};var G=r.prototype;r._augment=function(e){return e.constructor=r,e._isBuffer=!0,e._set=e.set,e.get=G.get,e.set=G.set,e.write=G.write,e.toString=G.toString,e.toLocaleString=G.toString,e.toJSON=G.toJSON,e.equals=G.equals,e.compare=G.compare,e.indexOf=G.indexOf,e.copy=G.copy,e.slice=G.slice,e.readUIntLE=G.readUIntLE,e.readUIntBE=G.readUIntBE,e.readUInt8=G.readUInt8,e.readUInt16LE=G.readUInt16LE,e.readUInt16BE=G.readUInt16BE,e.readUInt32LE=G.readUInt32LE,e.readUInt32BE=G.readUInt32BE,e.readIntLE=G.readIntLE,e.readIntBE=G.readIntBE,e.readInt8=G.readInt8,e.readInt16LE=G.readInt16LE,e.readInt16BE=G.readInt16BE,e.readInt32LE=G.readInt32LE,e.readInt32BE=G.readInt32BE,e.readFloatLE=G.readFloatLE,e.readFloatBE=G.readFloatBE,e.readDoubleLE=G.readDoubleLE,e.readDoubleBE=G.readDoubleBE,e.writeUInt8=G.writeUInt8,e.writeUIntLE=G.writeUIntLE,e.writeUIntBE=G.writeUIntBE,e.writeUInt16LE=G.writeUInt16LE,e.writeUInt16BE=G.writeUInt16BE,e.writeUInt32LE=G.writeUInt32LE,e.writeUInt32BE=G.writeUInt32BE,e.writeIntLE=G.writeIntLE,e.writeIntBE=G.writeIntBE,e.writeInt8=G.writeInt8,e.writeInt16LE=G.writeInt16LE,e.writeInt16BE=G.writeInt16BE,e.writeInt32LE=G.writeInt32LE,e.writeInt32BE=G.writeInt32BE,e.writeFloatLE=G.writeFloatLE,e.writeFloatBE=G.writeFloatBE,e.writeDoubleLE=G.writeDoubleLE,e.writeDoubleBE=G.writeDoubleBE,e.fill=G.fill,e.inspect=G.inspect,e.toArrayBuffer=G.toArrayBuffer,e};var Z=/[^+\/0-9A-Za-z-_]/g},{"base64-js":15,ieee754:16,"is-array":17}],15:[function(e,t,n){var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function t(e){var t=e.charCodeAt(0);return t===o||t===p?62:t===s||t===h?63:l>t?-1:l+10>t?t-l+26+26:c+26>t?t-c:u+26>t?t-u+26:void 0}function n(e){function n(e){u[p++]=e}var i,r,o,s,l,u;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var c=e.length;l="="===e.charAt(c-2)?2:"="===e.charAt(c-1)?1:0,u=new a(3*e.length/4-l),o=l>0?e.length-4:e.length;var p=0;for(i=0,r=0;o>i;i+=4,r+=3)s=t(e.charAt(i))<<18|t(e.charAt(i+1))<<12|t(e.charAt(i+2))<<6|t(e.charAt(i+3)),n((16711680&s)>>16),n((65280&s)>>8),n(255&s);return 2===l?(s=t(e.charAt(i))<<2|t(e.charAt(i+1))>>4,n(255&s)):1===l&&(s=t(e.charAt(i))<<10|t(e.charAt(i+1))<<4|t(e.charAt(i+2))>>2,n(s>>8&255),n(255&s)),u}function r(e){function t(e){return i.charAt(e)}function n(e){return t(e>>18&63)+t(e>>12&63)+t(e>>6&63)+t(63&e)}var r,a,o,s=e.length%3,l="";for(r=0,o=e.length-s;o>r;r+=3)a=(e[r]<<16)+(e[r+1]<<8)+e[r+2],l+=n(a);switch(s){case 1:a=e[e.length-1],l+=t(a>>2),l+=t(a<<4&63),l+="==";break;case 2:a=(e[e.length-2]<<8)+e[e.length-1],l+=t(a>>10),l+=t(a>>4&63),l+=t(a<<2&63),l+="="}return l}var a="undefined"!=typeof Uint8Array?Uint8Array:Array,o="+".charCodeAt(0),s="/".charCodeAt(0),l="0".charCodeAt(0),u="a".charCodeAt(0),c="A".charCodeAt(0),p="-".charCodeAt(0),h="_".charCodeAt(0);e.toByteArray=n,e.fromByteArray=r}("undefined"==typeof n?this.base64js={}:n)},{}],16:[function(e,t,n){n.read=function(e,t,n,i,r){var a,o,s=8*r-i-1,l=(1<<s)-1,u=l>>1,c=-7,p=n?r-1:0,h=n?-1:1,f=e[t+p];for(p+=h,a=f&(1<<-c)-1,f>>=-c,c+=s;c>0;a=256*a+e[t+p],p+=h,c-=8);for(o=a&(1<<-c)-1,a>>=-c,c+=i;c>0;o=256*o+e[t+p],p+=h,c-=8);if(0===a)a=1-u;else{if(a===l)return o?NaN:(f?-1:1)*(1/0);o+=Math.pow(2,i),a-=u}return(f?-1:1)*o*Math.pow(2,a-i)},n.write=function(e,t,n,i,r,a){var o,s,l,u=8*a-r-1,c=(1<<u)-1,p=c>>1,h=23===r?Math.pow(2,-24)-Math.pow(2,-77):0,f=i?0:a-1,d=i?1:-1,m=0>t||0===t&&0>1/t?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=c):(o=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-o))<1&&(o--,l*=2),t+=o+p>=1?h/l:h*Math.pow(2,1-p),t*l>=2&&(o++,l/=2),o+p>=c?(s=0,o=c):o+p>=1?(s=(t*l-1)*Math.pow(2,r),o+=p):(s=t*Math.pow(2,p-1)*Math.pow(2,r),o=0));r>=8;e[n+f]=255&s,f+=d,s/=256,r-=8);for(o=o<<r|s,u+=r;u>0;e[n+f]=255&o,f+=d,o/=256,u-=8);e[n+f-d]|=128*m}},{}],17:[function(e,t,n){var i=Array.isArray,r=Object.prototype.toString;t.exports=i||function(e){return!!e&&"[object Array]"==r.call(e)}},{}],18:[function(e,t,n){!function(){"use strict";function e(t,n,i,r){return this instanceof e?(this.domain=t||void 0,this.path=n||"/",this.secure=!!i,this.script=!!r,this):new e(t,n,i,r)}function t(e,n,i){return e instanceof t?e:this instanceof t?(this.name=null,this.value=null,this.expiration_date=1/0,this.path=String(i||"/"),this.explicit_path=!1,this.domain=n||null,this.explicit_domain=!1,this.secure=!1,this.noscript=!1,e&&this.parse(e,n,i),this):new t(e,n,i)}function i(){var e,n,r;return this instanceof i?(e=Object.create(null),this.setCookie=function(i,a,o){var s,l;if(i=new t(i,a,o),s=i.expiration_date<=Date.now(),void 0!==e[i.name]){for(n=e[i.name],l=0;l<n.length;l+=1)if(r=n[l],r.collidesWith(i))return s?(n.splice(l,1),0===n.length&&delete e[i.name],!1):(n[l]=i,i);return s?!1:(n.push(i),i)}return s?!1:(e[i.name]=[i],e[i.name])},this.getCookie=function(t,i){var r,a;if(n=e[t])for(a=0;a<n.length;a+=1)if(r=n[a],r.expiration_date<=Date.now())0===n.length&&delete e[r.name];else if(r.matches(i))return r},this.getCookies=function(t){var n,i,r=[];for(n in e)i=this.getCookie(n,t),i&&r.push(i);return r.toString=function(){return r.join(":")},r.toValueString=function(){return r.map(function(e){return e.toValueString()}).join(";")},r},this):new i}n.CookieAccessInfo=e,n.Cookie=t,t.prototype.toString=function(){var e=[this.name+"="+this.value];return this.expiration_date!==1/0&&e.push("expires="+new Date(this.expiration_date).toGMTString()),this.domain&&e.push("domain="+this.domain),this.path&&e.push("path="+this.path),this.secure&&e.push("secure"),this.noscript&&e.push("httponly"),e.join("; ")},t.prototype.toValueString=function(){return this.name+"="+this.value};var r=/[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;t.prototype.parse=function(e,n,i){if(this instanceof t){var r,a=e.split(";").filter(function(e){return!!e}),o=a[0].match(/([^=]+)=([\s\S]*)/),s=o[1],l=o[2];for(this.name=s,this.value=l,r=1;r<a.length;r+=1)switch(o=a[r].match(/([^=]+)(?:=([\s\S]*))?/),s=o[1].trim().toLowerCase(),l=o[2],s){case"httponly":this.noscript=!0;break;case"expires":this.expiration_date=l?Number(Date.parse(l)):1/0;break;case"path":this.path=l?l.trim():"",this.explicit_path=!0;break;case"domain":this.domain=l?l.trim():"",this.explicit_domain=!!this.domain;break;case"secure":this.secure=!0}return this.explicit_path||(this.path=i||"/"),this.explicit_domain||(this.domain=n),this}return(new t).parse(e,n,i)},t.prototype.matches=function(e){return!(this.noscript&&e.script||this.secure&&!e.secure)&&this.collidesWith(e)},t.prototype.collidesWith=function(e){if(this.path&&!e.path||this.domain&&!e.domain)return!1;if(this.path&&0!==e.path.indexOf(this.path))return!1;if(this.explicit_path&&0!==e.path.indexOf(this.path))return!1;var t=e.domain&&e.domain.replace(/^[\.]/,""),n=this.domain&&this.domain.replace(/^[\.]/,"");if(n===t)return!0;if(n){if(!this.explicit_domain)return!1;var i=t.indexOf(n);return-1!==i&&i===t.length-n.length}return!0},n.CookieJar=i,i.prototype.setCookies=function(e,n,i){e=Array.isArray(e)?e:e.split(r);var a,o,s=[];for(e=e.map(function(e){return new t(e,n,i)}),a=0;a<e.length;a+=1)o=e[a],this.setCookie(o,n,i)&&s.push(o);return s}}()},{}],19:[function(e,t,n){"use strict";var i=e("./lib/js-yaml.js");t.exports=i},{"./lib/js-yaml.js":20}],20:[function(e,t,n){"use strict";function i(e){return function(){throw new Error("Function "+e+" is deprecated and cannot be used.")}}var r=e("./js-yaml/loader"),a=e("./js-yaml/dumper");t.exports.Type=e("./js-yaml/type"),t.exports.Schema=e("./js-yaml/schema"),t.exports.FAILSAFE_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.JSON_SCHEMA=e("./js-yaml/schema/json"),t.exports.CORE_SCHEMA=e("./js-yaml/schema/core"),t.exports.DEFAULT_SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_FULL_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.load=r.load,t.exports.loadAll=r.loadAll,t.exports.safeLoad=r.safeLoad,t.exports.safeLoadAll=r.safeLoadAll,t.exports.dump=a.dump,t.exports.safeDump=a.safeDump,t.exports.YAMLException=e("./js-yaml/exception"),t.exports.MINIMAL_SCHEMA=e("./js-yaml/schema/failsafe"),t.exports.SAFE_SCHEMA=e("./js-yaml/schema/default_safe"),t.exports.DEFAULT_SCHEMA=e("./js-yaml/schema/default_full"),t.exports.scan=i("scan"),t.exports.parse=i("parse"),t.exports.compose=i("compose"),t.exports.addConstructor=i("addConstructor")},{"./js-yaml/dumper":22,"./js-yaml/exception":23,"./js-yaml/loader":24,"./js-yaml/schema":26,"./js-yaml/schema/core":27,"./js-yaml/schema/default_full":28,"./js-yaml/schema/default_safe":29,"./js-yaml/schema/failsafe":30,"./js-yaml/schema/json":31,"./js-yaml/type":32}],21:[function(e,t,n){"use strict";function i(e){return"undefined"==typeof e||null===e}function r(e){return"object"==typeof e&&null!==e}function a(e){return Array.isArray(e)?e:i(e)?[]:[e]}function o(e,t){var n,i,r,a;if(t)for(a=Object.keys(t),n=0,i=a.length;i>n;n+=1)r=a[n],e[r]=t[r];return e}function s(e,t){var n,i="";for(n=0;t>n;n+=1)i+=e;return i}function l(e){return 0===e&&Number.NEGATIVE_INFINITY===1/e}t.exports.isNothing=i,t.exports.isObject=r,t.exports.toArray=a,t.exports.repeat=s,t.exports.isNegativeZero=l,t.exports.extend=o},{}],22:[function(e,t,n){"use strict";function i(e,t){var n,i,r,a,o,s,l;if(null===t)return{};for(n={},i=Object.keys(t),r=0,a=i.length;a>r;r+=1)o=i[r],s=String(t[o]),"!!"===o.slice(0,2)&&(o="tag:yaml.org,2002:"+o.slice(2)),l=e.compiledTypeMap[o],l&&P.call(l.styleAliases,s)&&(s=l.styleAliases[s]),n[o]=s;return n}function r(e){var t,n,i;if(t=e.toString(16).toUpperCase(),255>=e)n="x",i=2;else if(65535>=e)n="u",i=4;else{if(!(4294967295>=e))throw new T("code point within a string may not be greater than 0xFFFFFFFF");n="U",i=8}return"\\"+n+I.repeat("0",i-t.length)+t}function a(e){this.schema=e.schema||$,this.indent=Math.max(1,e.indent||2),this.skipInvalid=e.skipInvalid||!1,this.flowLevel=I.isNothing(e.flowLevel)?-1:e.flowLevel,this.styleMap=i(this.schema,e.styles||null),this.sortKeys=e.sortKeys||!1,this.lineWidth=e.lineWidth||80,this.noRefs=e.noRefs||!1,this.noCompatMode=e.noCompatMode||!1,this.implicitTypes=this.schema.compiledImplicit,this.explicitTypes=this.schema.compiledExplicit,this.tag=null,this.result="",this.duplicates=[],this.usedDuplicates=null}function o(e,t){for(var n,i=I.repeat(" ",t),r=0,a=-1,o="",s=e.length;s>r;)a=e.indexOf("\n",r),-1===a?(n=e.slice(r),r=s):(n=e.slice(r,a+1),r=a+1),n.length&&"\n"!==n&&(o+=i),o+=n;return o}function s(e,t){return"\n"+I.repeat(" ",e.indent*t)}function l(e,t){var n,i,r;for(n=0,i=e.implicitTypes.length;i>n;n+=1)if(r=e.implicitTypes[n],r.resolve(t))return!0;return!1}function u(e){return e===R||e===L}function c(e){return e>=32&&126>=e||e>=161&&55295>=e&&8232!==e&&8233!==e||e>=57344&&65533>=e&&65279!==e||e>=65536&&1114111>=e}function p(e){return c(e)&&65279!==e&&e!==J&&e!==G&&e!==Z&&e!==te&&e!==ie&&e!==Y&&e!==B}function h(e){return c(e)&&65279!==e&&!u(e)&&e!==Q&&e!==W&&e!==Y&&e!==J&&e!==G&&e!==Z&&e!==te&&e!==ie&&e!==B&&e!==V&&e!==H&&e!==N&&e!==ne&&e!==K&&e!==z&&e!==F&&e!==q&&e!==X&&e!==ee}function f(e,t,n,i,r){var a,o,s=!1,l=!1,f=-1!==i,d=-1,m=h(e.charCodeAt(0))&&!u(e.charCodeAt(e.length-1));if(t)for(a=0;a<e.length;a++){if(o=e.charCodeAt(a),!c(o))return ce;m=m&&p(o)}else{for(a=0;a<e.length;a++){if(o=e.charCodeAt(a),o===D)s=!0,f&&(l=l||a-d-1>i&&" "!==e[d+1],d=a);else if(!c(o))return ce;m=m&&p(o)}l=l||f&&a-d-1>i&&" "!==e[d+1]}return s||l?" "===e[0]&&n>9?ce:l?ue:le:m&&!r(e)?oe:se}function d(e,t,n,i){e.dump=function(){function r(t){return l(e,t)}if(0===t.length)return"''";if(!e.noCompatMode&&-1!==ae.indexOf(t))return"'"+t+"'";var a=e.indent*Math.max(1,n),s=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-a),u=i||e.flowLevel>-1&&n>=e.flowLevel;switch(f(t,u,e.indent,s,r)){case oe:return t;case se:return"'"+t.replace(/'/g,"''")+"'";case le:return"|"+m(t,e.indent)+y(o(t,a));case ue:return">"+m(t,e.indent)+y(o(g(t,s),a));case ce:return'"'+b(t,s)+'"';default:throw new T("impossible error: invalid scalar style")}}()}function m(e,t){var n=" "===e[0]?String(t):"",i="\n"===e[e.length-1],r=i&&("\n"===e[e.length-2]||"\n"===e),a=r?"+":i?"":"-";return n+a+"\n"}function y(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function g(e,t){for(var n,i,r=/(\n+)([^\n]*)/g,a=function(){var n=e.indexOf("\n");return n=-1!==n?n:e.length,r.lastIndex=n,v(e.slice(0,n),t)}(),o="\n"===e[0]||" "===e[0];i=r.exec(e);){var s=i[1],l=i[2];n=" "===l[0],a+=s+(o||n||""===l?"":"\n")+v(l,t),o=n}return a}function v(e,t){if(""===e||" "===e[0])return e;for(var n,i,r=/ [^ ]/g,a=0,o=0,s=0,l="";n=r.exec(e);)s=n.index,s-a>t&&(i=o>a?o:s,l+="\n"+e.slice(a,i),a=i+1),o=s;return l+="\n",l+=e.length-a>t&&o>a?e.slice(a,o)+"\n"+e.slice(o+1):e.slice(a),l.slice(1)}function b(e){for(var t,n,i="",a=0;a<e.length;a++)t=e.charCodeAt(a),n=re[t],i+=!n&&c(t)?e[a]:n||r(t);return i}function w(e,t,n){var i,r,a="",o=e.tag;for(i=0,r=n.length;r>i;i+=1)_(e,t,n[i],!1,!1)&&(0!==i&&(a+=", "),a+=e.dump);e.tag=o,e.dump="["+a+"]"}function x(e,t,n,i){var r,a,o="",l=e.tag;for(r=0,a=n.length;a>r;r+=1)_(e,t+1,n[r],!0,!0)&&(i&&0===r||(o+=s(e,t)),o+="- "+e.dump);e.tag=l,e.dump=o||"[]"}function A(e,t,n){var i,r,a,o,s,l="",u=e.tag,c=Object.keys(n);for(i=0,r=c.length;r>i;i+=1)s="",0!==i&&(s+=", "),a=c[i],o=n[a],_(e,t,a,!1,!1)&&(e.dump.length>1024&&(s+="? "),s+=e.dump+": ",_(e,t,o,!1,!1)&&(s+=e.dump,l+=s));e.tag=u,e.dump="{"+l+"}"}function j(e,t,n,i){var r,a,o,l,u,c,p="",h=e.tag,f=Object.keys(n);if(e.sortKeys===!0)f.sort();else if("function"==typeof e.sortKeys)f.sort(e.sortKeys);else if(e.sortKeys)throw new T("sortKeys must be a boolean or a function");for(r=0,a=f.length;a>r;r+=1)c="",i&&0===r||(c+=s(e,t)),o=f[r],l=n[o],_(e,t+1,o,!0,!0,!0)&&(u=null!==e.tag&&"?"!==e.tag||e.dump&&e.dump.length>1024,u&&(c+=e.dump&&D===e.dump.charCodeAt(0)?"?":"? "),c+=e.dump,u&&(c+=s(e,t)),_(e,t+1,l,!0,u)&&(c+=e.dump&&D===e.dump.charCodeAt(0)?":":": ",c+=e.dump,p+=c));e.tag=h,e.dump=p||"{}"}function O(e,t,n){var i,r,a,o,s,l;for(r=n?e.explicitTypes:e.implicitTypes,a=0,o=r.length;o>a;a+=1)if(s=r[a],(s.instanceOf||s.predicate)&&(!s.instanceOf||"object"==typeof t&&t instanceof s.instanceOf)&&(!s.predicate||s.predicate(t))){if(e.tag=n?s.tag:"?",s.represent){if(l=e.styleMap[s.tag]||s.defaultStyle,"[object Function]"===U.call(s.represent))i=s.represent(t,l);else{if(!P.call(s.represent,l))throw new T("!<"+s.tag+'> tag resolver accepts not "'+l+'" style');i=s.represent[l](t,l)}e.dump=i}return!0}return!1}function _(e,t,n,i,r,a){e.tag=null,e.dump=n,O(e,n,!1)||O(e,n,!0);var o=U.call(e.dump);i&&(i=e.flowLevel<0||e.flowLevel>t);var s,l,u="[object Object]"===o||"[object Array]"===o;if(u&&(s=e.duplicates.indexOf(n),l=-1!==s),(null!==e.tag&&"?"!==e.tag||l||2!==e.indent&&t>0)&&(r=!1),l&&e.usedDuplicates[s])e.dump="*ref_"+s;else{if(u&&l&&!e.usedDuplicates[s]&&(e.usedDuplicates[s]=!0),"[object Object]"===o)i&&0!==Object.keys(e.dump).length?(j(e,t,e.dump,r),l&&(e.dump="&ref_"+s+e.dump)):(A(e,t,e.dump),l&&(e.dump="&ref_"+s+" "+e.dump));else if("[object Array]"===o)i&&0!==e.dump.length?(x(e,t,e.dump,r),l&&(e.dump="&ref_"+s+e.dump)):(w(e,t,e.dump),l&&(e.dump="&ref_"+s+" "+e.dump));else{if("[object String]"!==o){if(e.skipInvalid)return!1;throw new T("unacceptable kind of an object to dump "+o)}"?"!==e.tag&&d(e,e.dump,t,a)}null!==e.tag&&"?"!==e.tag&&(e.dump="!<"+e.tag+"> "+e.dump)}return!0}function S(e,t){var n,i,r=[],a=[];for(k(e,r,a),n=0,i=a.length;i>n;n+=1)t.duplicates.push(r[a[n]]);t.usedDuplicates=new Array(i)}function k(e,t,n){var i,r,a;if(null!==e&&"object"==typeof e)if(r=t.indexOf(e),-1!==r)-1===n.indexOf(r)&&n.push(r);else if(t.push(e),Array.isArray(e))for(r=0,a=e.length;a>r;r+=1)k(e[r],t,n);else for(i=Object.keys(e),r=0,a=i.length;a>r;r+=1)k(e[i[r]],t,n)}function C(e,t){t=t||{};var n=new a(t);return n.noRefs||S(e,n),_(n,0,e,!0,!0)?n.dump+"\n":""}function E(e,t){return C(e,I.extend({schema:M},t))}var I=e("./common"),T=e("./exception"),$=e("./schema/default_full"),M=e("./schema/default_safe"),U=Object.prototype.toString,P=Object.prototype.hasOwnProperty,L=9,D=10,R=32,N=33,F=34,B=35,q=37,V=38,z=39,H=42,J=44,Q=45,Y=58,K=62,W=63,X=64,G=91,Z=93,ee=96,te=123,ne=124,ie=125,re={};re[0]="\\0",re[7]="\\a",re[8]="\\b",re[9]="\\t",re[10]="\\n",re[11]="\\v",re[12]="\\f",re[13]="\\r",re[27]="\\e",re[34]='\\"',re[92]="\\\\",re[133]="\\N",re[160]="\\_",re[8232]="\\L",re[8233]="\\P";var ae=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],oe=1,se=2,le=3,ue=4,ce=5;t.exports.dump=C,t.exports.safeDump=E},{"./common":21,"./exception":23,"./schema/default_full":28,"./schema/default_safe":29}],23:[function(e,t,n){"use strict";function i(e,t){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack||"",this.name="YAMLException",this.reason=e,this.mark=t,this.message=(this.reason||"(unknown reason)")+(this.mark?" "+this.mark.toString():"")}i.prototype=Object.create(Error.prototype),i.prototype.constructor=i,i.prototype.toString=function(e){var t=this.name+": ";return t+=this.reason||"(unknown reason)",!e&&this.mark&&(t+=" "+this.mark.toString()),t},t.exports=i},{}],24:[function(e,t,n){"use strict";function i(e){return 10===e||13===e}function r(e){return 9===e||32===e}function a(e){return 9===e||32===e||10===e||13===e}function o(e){return 44===e||91===e||93===e||123===e||125===e}function s(e){var t;return e>=48&&57>=e?e-48:(t=32|e,t>=97&&102>=t?t-97+10:-1)}function l(e){return 120===e?2:117===e?4:85===e?8:0}function u(e){return e>=48&&57>=e?e-48:-1}function c(e){return 48===e?"\x00":97===e?"":98===e?"\b":116===e?"	":9===e?"	":110===e?"\n":118===e?"\x0B":102===e?"\f":114===e?"\r":101===e?"":32===e?" ":34===e?'"':47===e?"/":92===e?"\\":78===e?"…":95===e?" ":76===e?"\u2028":80===e?"\u2029":""}function p(e){return 65535>=e?String.fromCharCode(e):String.fromCharCode((e-65536>>10)+55296,(e-65536&1023)+56320)}function h(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||z,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.documents=[]}function f(e,t){return new B(t,new q(e.filename,e.input,e.position,e.line,e.position-e.lineStart))}function d(e,t){throw f(e,t)}function m(e,t){e.onWarning&&e.onWarning.call(null,f(e,t))}function y(e,t,n,i){var r,a,o,s;if(n>t){if(s=e.input.slice(t,n),i)for(r=0,a=s.length;a>r;r+=1)o=s.charCodeAt(r),9===o||o>=32&&1114111>=o||d(e,"expected valid JSON character");else Z.test(s)&&d(e,"the stream contains non-printable characters");e.result+=s}}function g(e,t,n,i){var r,a,o,s;for(F.isObject(n)||d(e,"cannot merge mappings; the provided source object is unacceptable"),r=Object.keys(n),o=0,s=r.length;s>o;o+=1)a=r[o],H.call(t,a)||(t[a]=n[a],i[a]=!0)}function v(e,t,n,i,r,a){var o,s;if(r=String(r),null===t&&(t={}),"tag:yaml.org,2002:merge"===i)if(Array.isArray(a))for(o=0,s=a.length;s>o;o+=1)g(e,t,a[o],n);else g(e,t,a,n);else e.json||H.call(n,r)||!H.call(t,r)||d(e,"duplicated mapping key"),t[r]=a,delete n[r];return t}function b(e){var t;t=e.input.charCodeAt(e.position),10===t?e.position++:13===t?(e.position++,10===e.input.charCodeAt(e.position)&&e.position++):d(e,"a line break is expected"),e.line+=1,e.lineStart=e.position}function w(e,t,n){for(var a=0,o=e.input.charCodeAt(e.position);0!==o;){for(;r(o);)o=e.input.charCodeAt(++e.position);if(t&&35===o)do o=e.input.charCodeAt(++e.position);while(10!==o&&13!==o&&0!==o);if(!i(o))break;for(b(e),o=e.input.charCodeAt(e.position),a++,e.lineIndent=0;32===o;)e.lineIndent++,o=e.input.charCodeAt(++e.position)}return-1!==n&&0!==a&&e.lineIndent<n&&m(e,"deficient indentation"),a}function x(e){var t,n=e.position;return t=e.input.charCodeAt(n),(45===t||46===t)&&t===e.input.charCodeAt(n+1)&&t===e.input.charCodeAt(n+2)&&(n+=3,t=e.input.charCodeAt(n),0===t||a(t))}function A(e,t){1===t?e.result+=" ":t>1&&(e.result+=F.repeat("\n",t-1))}function j(e,t,n){var s,l,u,c,p,h,f,d,m,g=e.kind,v=e.result;if(m=e.input.charCodeAt(e.position),a(m)||o(m)||35===m||38===m||42===m||33===m||124===m||62===m||39===m||34===m||37===m||64===m||96===m)return!1;if((63===m||45===m)&&(l=e.input.charCodeAt(e.position+1),a(l)||n&&o(l)))return!1;for(e.kind="scalar",e.result="",u=c=e.position,p=!1;0!==m;){if(58===m){if(l=e.input.charCodeAt(e.position+1),a(l)||n&&o(l))break}else if(35===m){if(s=e.input.charCodeAt(e.position-1),a(s))break}else{if(e.position===e.lineStart&&x(e)||n&&o(m))break;if(i(m)){if(h=e.line,f=e.lineStart,d=e.lineIndent,w(e,!1,-1),e.lineIndent>=t){p=!0,m=e.input.charCodeAt(e.position);continue}e.position=c,e.line=h,e.lineStart=f,e.lineIndent=d;break}}p&&(y(e,u,c,!1),A(e,e.line-h),u=c=e.position,p=!1),r(m)||(c=e.position+1),m=e.input.charCodeAt(++e.position)}return y(e,u,c,!1),e.result?!0:(e.kind=g,e.result=v,!1)}function O(e,t){var n,r,a;if(n=e.input.charCodeAt(e.position),39!==n)return!1;for(e.kind="scalar",e.result="",e.position++,r=a=e.position;0!==(n=e.input.charCodeAt(e.position));)if(39===n){if(y(e,r,e.position,!0),n=e.input.charCodeAt(++e.position),39!==n)return!0;r=a=e.position,e.position++}else i(n)?(y(e,r,a,!0),A(e,w(e,!1,t)),r=a=e.position):e.position===e.lineStart&&x(e)?d(e,"unexpected end of the document within a single quoted scalar"):(e.position++,a=e.position);d(e,"unexpected end of the stream within a single quoted scalar")}function _(e,t){var n,r,a,o,u,c;if(c=e.input.charCodeAt(e.position),34!==c)return!1;for(e.kind="scalar",e.result="",e.position++,n=r=e.position;0!==(c=e.input.charCodeAt(e.position));){if(34===c)return y(e,n,e.position,!0),e.position++,!0;if(92===c){if(y(e,n,e.position,!0),c=e.input.charCodeAt(++e.position),i(c))w(e,!1,t);else if(256>c&&re[c])e.result+=ae[c],e.position++;else if((u=l(c))>0){for(a=u,o=0;a>0;a--)c=e.input.charCodeAt(++e.position),(u=s(c))>=0?o=(o<<4)+u:d(e,"expected hexadecimal character");e.result+=p(o),e.position++}else d(e,"unknown escape sequence");n=r=e.position}else i(c)?(y(e,n,r,!0),A(e,w(e,!1,t)),n=r=e.position):e.position===e.lineStart&&x(e)?d(e,"unexpected end of the document within a double quoted scalar"):(e.position++,r=e.position)}d(e,"unexpected end of the stream within a double quoted scalar")}function S(e,t){var n,i,r,o,s,l,u,c,p,h,f,m=!0,y=e.tag,g=e.anchor,b={};if(f=e.input.charCodeAt(e.position),91===f)o=93,u=!1,i=[];else{if(123!==f)return!1;o=125,u=!0,i={}}for(null!==e.anchor&&(e.anchorMap[e.anchor]=i),f=e.input.charCodeAt(++e.position);0!==f;){if(w(e,!0,t),f=e.input.charCodeAt(e.position),f===o)return e.position++,e.tag=y,e.anchor=g,e.kind=u?"mapping":"sequence",e.result=i,!0;m||d(e,"missed comma between flow collection entries"),p=c=h=null,s=l=!1,63===f&&(r=e.input.charCodeAt(e.position+1),a(r)&&(s=l=!0,e.position++,w(e,!0,t))),n=e.line,M(e,t,J,!1,!0),p=e.tag,c=e.result,w(e,!0,t),f=e.input.charCodeAt(e.position),!l&&e.line!==n||58!==f||(s=!0,f=e.input.charCodeAt(++e.position),w(e,!0,t),M(e,t,J,!1,!0),h=e.result),u?v(e,i,b,p,c,h):s?i.push(v(e,null,b,p,c,h)):i.push(c),w(e,!0,t),f=e.input.charCodeAt(e.position),44===f?(m=!0,f=e.input.charCodeAt(++e.position)):m=!1}d(e,"unexpected end of the stream within a flow collection")}function k(e,t){var n,a,o,s,l=W,c=!1,p=!1,h=t,f=0,m=!1;if(s=e.input.charCodeAt(e.position),124===s)a=!1;else{if(62!==s)return!1;a=!0}for(e.kind="scalar",e.result="";0!==s;)if(s=e.input.charCodeAt(++e.position),43===s||45===s)W===l?l=43===s?G:X:d(e,"repeat of a chomping mode identifier");else{if(!((o=u(s))>=0))break;0===o?d(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):p?d(e,"repeat of an indentation width identifier"):(h=t+o-1,p=!0)}if(r(s)){do s=e.input.charCodeAt(++e.position);while(r(s));if(35===s)do s=e.input.charCodeAt(++e.position);while(!i(s)&&0!==s)}for(;0!==s;){for(b(e),e.lineIndent=0,s=e.input.charCodeAt(e.position);(!p||e.lineIndent<h)&&32===s;)e.lineIndent++,s=e.input.charCodeAt(++e.position);if(!p&&e.lineIndent>h&&(h=e.lineIndent),i(s))f++;else{if(e.lineIndent<h){l===G?e.result+=F.repeat("\n",c?1+f:f):l===W&&c&&(e.result+="\n");break}for(a?r(s)?(m=!0,e.result+=F.repeat("\n",c?1+f:f)):m?(m=!1,e.result+=F.repeat("\n",f+1)):0===f?c&&(e.result+=" "):e.result+=F.repeat("\n",f):e.result+=F.repeat("\n",c?1+f:f),c=!0,p=!0,f=0,n=e.position;!i(s)&&0!==s;)s=e.input.charCodeAt(++e.position);y(e,n,e.position,!1)}}return!0}function C(e,t){var n,i,r,o=e.tag,s=e.anchor,l=[],u=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=l),r=e.input.charCodeAt(e.position);0!==r&&45===r&&(i=e.input.charCodeAt(e.position+1),a(i));)if(u=!0,e.position++,w(e,!0,-1)&&e.lineIndent<=t)l.push(null),r=e.input.charCodeAt(e.position);else if(n=e.line,M(e,t,Y,!1,!0),l.push(e.result),w(e,!0,-1),r=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==r)d(e,"bad indentation of a sequence entry");else if(e.lineIndent<t)break;return u?(e.tag=o,e.anchor=s,e.kind="sequence",e.result=l,!0):!1}function E(e,t,n){var i,o,s,l,u=e.tag,c=e.anchor,p={},h={},f=null,m=null,y=null,g=!1,b=!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=p),l=e.input.charCodeAt(e.position);0!==l;){if(i=e.input.charCodeAt(e.position+1),s=e.line,63!==l&&58!==l||!a(i)){if(!M(e,n,Q,!1,!0))break;if(e.line===s){for(l=e.input.charCodeAt(e.position);r(l);)l=e.input.charCodeAt(++e.position);if(58===l)l=e.input.charCodeAt(++e.position),a(l)||d(e,"a whitespace character is expected after the key-value separator within a block mapping"),g&&(v(e,p,h,f,m,null),f=m=y=null),b=!0,g=!1,o=!1,f=e.tag,m=e.result;else{if(!b)return e.tag=u,e.anchor=c,!0;d(e,"can not read an implicit mapping pair; a colon is missed")}}else{if(!b)return e.tag=u,e.anchor=c,!0;d(e,"can not read a block mapping entry; a multiline key may not be an implicit key")}}else 63===l?(g&&(v(e,p,h,f,m,null),f=m=y=null),b=!0,g=!0,o=!0):g?(g=!1,o=!0):d(e,"incomplete explicit mapping pair; a key node is missed"),e.position+=1,l=i;if((e.line===s||e.lineIndent>t)&&(M(e,t,K,!0,o)&&(g?m=e.result:y=e.result),g||(v(e,p,h,f,m,y),f=m=y=null),w(e,!0,-1),l=e.input.charCodeAt(e.position)),e.lineIndent>t&&0!==l)d(e,"bad indentation of a mapping entry");else if(e.lineIndent<t)break}return g&&v(e,p,h,f,m,null),b&&(e.tag=u,e.anchor=c,e.kind="mapping",e.result=p),b}function I(e){var t,n,i,r,o=!1,s=!1;if(r=e.input.charCodeAt(e.position),33!==r)return!1;if(null!==e.tag&&d(e,"duplication of a tag property"),r=e.input.charCodeAt(++e.position),60===r?(o=!0,r=e.input.charCodeAt(++e.position)):33===r?(s=!0,n="!!",r=e.input.charCodeAt(++e.position)):n="!",t=e.position,o){do r=e.input.charCodeAt(++e.position);while(0!==r&&62!==r);e.position<e.length?(i=e.input.slice(t,e.position),r=e.input.charCodeAt(++e.position)):d(e,"unexpected end of the stream within a verbatim tag")}else{for(;0!==r&&!a(r);)33===r&&(s?d(e,"tag suffix cannot contain exclamation marks"):(n=e.input.slice(t-1,e.position+1),ne.test(n)||d(e,"named tag handle cannot contain such characters"),s=!0,t=e.position+1)),r=e.input.charCodeAt(++e.position);i=e.input.slice(t,e.position),te.test(i)&&d(e,"tag suffix cannot contain flow indicator characters")}return i&&!ie.test(i)&&d(e,"tag name cannot contain such characters: "+i),o?e.tag=i:H.call(e.tagMap,n)?e.tag=e.tagMap[n]+i:"!"===n?e.tag="!"+i:"!!"===n?e.tag="tag:yaml.org,2002:"+i:d(e,'undeclared tag handle "'+n+'"'),!0}function T(e){var t,n;if(n=e.input.charCodeAt(e.position),38!==n)return!1;for(null!==e.anchor&&d(e,"duplication of an anchor property"),n=e.input.charCodeAt(++e.position),t=e.position;0!==n&&!a(n)&&!o(n);)n=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an anchor node must contain at least one character"),e.anchor=e.input.slice(t,e.position),!0}function $(e){var t,n,i;if(i=e.input.charCodeAt(e.position),42!==i)return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!a(i)&&!o(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&d(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),e.anchorMap.hasOwnProperty(n)||d(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],w(e,!0,-1),!0}function M(e,t,n,i,r){var a,o,s,l,u,c,p,h,f=1,m=!1,y=!1;if(null!==e.listener&&e.listener("open",e),e.tag=null,e.anchor=null,e.kind=null,e.result=null,a=o=s=K===n||Y===n,i&&w(e,!0,-1)&&(m=!0,e.lineIndent>t?f=1:e.lineIndent===t?f=0:e.lineIndent<t&&(f=-1)),1===f)for(;I(e)||T(e);)w(e,!0,-1)?(m=!0,s=a,e.lineIndent>t?f=1:e.lineIndent===t?f=0:e.lineIndent<t&&(f=-1)):s=!1;if(s&&(s=m||r),1!==f&&K!==n||(p=J===n||Q===n?t:t+1,h=e.position-e.lineStart,1===f?s&&(C(e,h)||E(e,h,p))||S(e,p)?y=!0:(o&&k(e,p)||O(e,p)||_(e,p)?y=!0:$(e)?(y=!0,null===e.tag&&null===e.anchor||d(e,"alias node should not have any properties")):j(e,p,J===n)&&(y=!0,null===e.tag&&(e.tag="?")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===f&&(y=s&&C(e,h))),null!==e.tag&&"!"!==e.tag)if("?"===e.tag){for(l=0,u=e.implicitTypes.length;u>l;l+=1)if(c=e.implicitTypes[l],c.resolve(e.result)){e.result=c.construct(e.result),e.tag=c.tag,null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);break}}else H.call(e.typeMap,e.tag)?(c=e.typeMap[e.tag],null!==e.result&&c.kind!==e.kind&&d(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+c.kind+'", not "'+e.kind+'"'),c.resolve(e.result)?(e.result=c.construct(e.result),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):d(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")):d(e,"unknown tag !<"+e.tag+">");return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||y}function U(e){var t,n,o,s,l=e.position,u=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap={},e.anchorMap={};0!==(s=e.input.charCodeAt(e.position))&&(w(e,!0,-1),s=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==s));){for(u=!0,s=e.input.charCodeAt(++e.position),t=e.position;0!==s&&!a(s);)s=e.input.charCodeAt(++e.position);for(n=e.input.slice(t,e.position),o=[],n.length<1&&d(e,"directive name must not be less than one character in length");0!==s;){for(;r(s);)s=e.input.charCodeAt(++e.position);if(35===s){do s=e.input.charCodeAt(++e.position);while(0!==s&&!i(s));break}if(i(s))break;for(t=e.position;0!==s&&!a(s);)s=e.input.charCodeAt(++e.position);o.push(e.input.slice(t,e.position))}0!==s&&b(e),H.call(se,n)?se[n](e,n,o):m(e,'unknown document directive "'+n+'"')}return w(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,w(e,!0,-1)):u&&d(e,"directives end mark is expected"),M(e,e.lineIndent-1,K,!1,!0),w(e,!0,-1),e.checkLineBreaks&&ee.test(e.input.slice(l,e.position))&&m(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&x(e)?void(46===e.input.charCodeAt(e.position)&&(e.position+=3,
+w(e,!0,-1))):void(e.position<e.length-1&&d(e,"end of the stream or a document separator is expected"))}function P(e,t){e=String(e),t=t||{},0!==e.length&&(10!==e.charCodeAt(e.length-1)&&13!==e.charCodeAt(e.length-1)&&(e+="\n"),65279===e.charCodeAt(0)&&(e=e.slice(1)));var n=new h(e,t);for(n.input+="\x00";32===n.input.charCodeAt(n.position);)n.lineIndent+=1,n.position+=1;for(;n.position<n.length-1;)U(n);return n.documents}function L(e,t,n){var i,r,a=P(e,n);for(i=0,r=a.length;r>i;i+=1)t(a[i])}function D(e,t){var n=P(e,t);if(0!==n.length){if(1===n.length)return n[0];throw new B("expected a single document in the stream, but found more")}}function R(e,t,n){L(e,t,F.extend({schema:V},n))}function N(e,t){return D(e,F.extend({schema:V},t))}for(var F=e("./common"),B=e("./exception"),q=e("./mark"),V=e("./schema/default_safe"),z=e("./schema/default_full"),H=Object.prototype.hasOwnProperty,J=1,Q=2,Y=3,K=4,W=1,X=2,G=3,Z=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,ee=/[\x85\u2028\u2029]/,te=/[,\[\]\{\}]/,ne=/^(?:!|!!|![a-z\-]+!)$/i,ie=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i,re=new Array(256),ae=new Array(256),oe=0;256>oe;oe++)re[oe]=c(oe)?1:0,ae[oe]=c(oe);var se={YAML:function(e,t,n){var i,r,a;null!==e.version&&d(e,"duplication of %YAML directive"),1!==n.length&&d(e,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),null===i&&d(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),a=parseInt(i[2],10),1!==r&&d(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=2>a,1!==a&&2!==a&&m(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&d(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],ne.test(i)||d(e,"ill-formed tag handle (first argument) of the TAG directive"),H.call(e.tagMap,i)&&d(e,'there is a previously declared suffix for "'+i+'" tag handle'),ie.test(r)||d(e,"ill-formed tag prefix (second argument) of the TAG directive"),e.tagMap[i]=r}};t.exports.loadAll=L,t.exports.load=D,t.exports.safeLoadAll=R,t.exports.safeLoad=N},{"./common":21,"./exception":23,"./mark":25,"./schema/default_full":28,"./schema/default_safe":29}],25:[function(e,t,n){"use strict";function i(e,t,n,i,r){this.name=e,this.buffer=t,this.position=n,this.line=i,this.column=r}var r=e("./common");i.prototype.getSnippet=function(e,t){var n,i,a,o,s;if(!this.buffer)return null;for(e=e||4,t=t||75,n="",i=this.position;i>0&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(i-1));)if(i-=1,this.position-i>t/2-1){n=" ... ",i+=5;break}for(a="",o=this.position;o<this.buffer.length&&-1==="\x00\r\n…\u2028\u2029".indexOf(this.buffer.charAt(o));)if(o+=1,o-this.position>t/2-1){a=" ... ",o-=5;break}return s=this.buffer.slice(i,o),r.repeat(" ",e)+n+s+a+"\n"+r.repeat(" ",e+this.position-i+n.length)+"^"},i.prototype.toString=function(e){var t,n="";return this.name&&(n+='in "'+this.name+'" '),n+="at line "+(this.line+1)+", column "+(this.column+1),e||(t=this.getSnippet(),t&&(n+=":\n"+t)),n},t.exports=i},{"./common":21}],26:[function(e,t,n){"use strict";function i(e,t,n){var r=[];return e.include.forEach(function(e){n=i(e,t,n)}),e[t].forEach(function(e){n.forEach(function(t,n){t.tag===e.tag&&r.push(n)}),n.push(e)}),n.filter(function(e,t){return-1===r.indexOf(t)})}function r(){function e(e){i[e.tag]=e}var t,n,i={};for(t=0,n=arguments.length;n>t;t+=1)arguments[t].forEach(e);return i}function a(e){this.include=e.include||[],this.implicit=e.implicit||[],this.explicit=e.explicit||[],this.implicit.forEach(function(e){if(e.loadKind&&"scalar"!==e.loadKind)throw new s("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.")}),this.compiledImplicit=i(this,"implicit",[]),this.compiledExplicit=i(this,"explicit",[]),this.compiledTypeMap=r(this.compiledImplicit,this.compiledExplicit)}var o=e("./common"),s=e("./exception"),l=e("./type");a.DEFAULT=null,a.create=function(){var e,t;switch(arguments.length){case 1:e=a.DEFAULT,t=arguments[0];break;case 2:e=arguments[0],t=arguments[1];break;default:throw new s("Wrong number of arguments for Schema.create function")}if(e=o.toArray(e),t=o.toArray(t),!e.every(function(e){return e instanceof a}))throw new s("Specified list of super schemas (or a single Schema object) contains a non-Schema object.");if(!t.every(function(e){return e instanceof l}))throw new s("Specified list of YAML types (or a single Type object) contains a non-Type object.");return new a({include:e,explicit:t})},t.exports=a},{"./common":21,"./exception":23,"./type":32}],27:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./json")]})},{"../schema":26,"./json":31}],28:[function(e,t,n){"use strict";var i=e("../schema");t.exports=i.DEFAULT=new i({include:[e("./default_safe")],explicit:[e("../type/js/undefined"),e("../type/js/regexp"),e("../type/js/function")]})},{"../schema":26,"../type/js/function":37,"../type/js/regexp":38,"../type/js/undefined":39,"./default_safe":29}],29:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./core")],implicit:[e("../type/timestamp"),e("../type/merge")],explicit:[e("../type/binary"),e("../type/omap"),e("../type/pairs"),e("../type/set")]})},{"../schema":26,"../type/binary":33,"../type/merge":41,"../type/omap":43,"../type/pairs":44,"../type/set":46,"../type/timestamp":48,"./core":27}],30:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({explicit:[e("../type/str"),e("../type/seq"),e("../type/map")]})},{"../schema":26,"../type/map":40,"../type/seq":45,"../type/str":47}],31:[function(e,t,n){"use strict";var i=e("../schema");t.exports=new i({include:[e("./failsafe")],implicit:[e("../type/null"),e("../type/bool"),e("../type/int"),e("../type/float")]})},{"../schema":26,"../type/bool":34,"../type/float":35,"../type/int":36,"../type/null":42,"./failsafe":30}],32:[function(e,t,n){"use strict";function i(e){var t={};return null!==e&&Object.keys(e).forEach(function(n){e[n].forEach(function(e){t[String(e)]=n})}),t}function r(e,t){if(t=t||{},Object.keys(t).forEach(function(t){if(-1===o.indexOf(t))throw new a('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')}),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=i(t.styleAliases||null),-1===s.indexOf(this.kind))throw new a('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}var a=e("./exception"),o=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],s=["scalar","sequence","mapping"];t.exports=r},{"./exception":23}],33:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t,n,i=0,r=e.length,a=p;for(n=0;r>n;n++)if(t=a.indexOf(e.charAt(n)),!(t>64)){if(0>t)return!1;i+=6}return i%8===0}function r(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,a=p,o=0,l=[];for(t=0;r>t;t++)t%4===0&&t&&(l.push(o>>16&255),l.push(o>>8&255),l.push(255&o)),o=o<<6|a.indexOf(i.charAt(t));return n=r%4*6,0===n?(l.push(o>>16&255),l.push(o>>8&255),l.push(255&o)):18===n?(l.push(o>>10&255),l.push(o>>2&255)):12===n&&l.push(o>>4&255),s?new s(l):l}function a(e){var t,n,i="",r=0,a=e.length,o=p;for(t=0;a>t;t++)t%3===0&&t&&(i+=o[r>>18&63],i+=o[r>>12&63],i+=o[r>>6&63],i+=o[63&r]),r=(r<<8)+e[t];return n=a%3,0===n?(i+=o[r>>18&63],i+=o[r>>12&63],i+=o[r>>6&63],i+=o[63&r]):2===n?(i+=o[r>>10&63],i+=o[r>>4&63],i+=o[r<<2&63],i+=o[64]):1===n&&(i+=o[r>>2&63],i+=o[r<<4&63],i+=o[64],i+=o[64]),i}function o(e){return s&&s.isBuffer(e)}var s;try{var l=e;s=l("buffer").Buffer}catch(u){}var c=e("../type"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";t.exports=new c("tag:yaml.org,2002:binary",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../type":32}],34:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;var t=e.length;return 4===t&&("true"===e||"True"===e||"TRUE"===e)||5===t&&("false"===e||"False"===e||"FALSE"===e)}function r(e){return"true"===e||"True"===e||"TRUE"===e}function a(e){return"[object Boolean]"===Object.prototype.toString.call(e)}var o=e("../type");t.exports=new o("tag:yaml.org,2002:bool",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:{lowercase:function(e){return e?"true":"false"},uppercase:function(e){return e?"TRUE":"FALSE"},camelcase:function(e){return e?"True":"False"}},defaultStyle:"lowercase"})},{"../type":32}],35:[function(e,t,n){"use strict";function i(e){return null===e?!1:!!u.test(e)}function r(e){var t,n,i,r;return t=e.replace(/_/g,"").toLowerCase(),n="-"===t[0]?-1:1,r=[],"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:t.indexOf(":")>=0?(t.split(":").forEach(function(e){r.unshift(parseFloat(e,10))}),t=0,i=1,r.forEach(function(e){t+=e*i,i*=60}),n*t):n*parseFloat(t,10)}function a(e,t){var n;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(s.isNegativeZero(e))return"-0.0";return n=e.toString(10),c.test(n)?n.replace("e",".e"):n}function o(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!==0||s.isNegativeZero(e))}var s=e("../common"),l=e("../type"),u=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+][0-9]+)?|\\.[0-9_]+(?:[eE][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$"),c=/^[-+]?[0-9]+e/;t.exports=new l("tag:yaml.org,2002:float",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a,defaultStyle:"lowercase"})},{"../common":21,"../type":32}],36:[function(e,t,n){"use strict";function i(e){return e>=48&&57>=e||e>=65&&70>=e||e>=97&&102>=e}function r(e){return e>=48&&55>=e}function a(e){return e>=48&&57>=e}function o(e){if(null===e)return!1;var t,n=e.length,o=0,s=!1;if(!n)return!1;if(t=e[o],"-"!==t&&"+"!==t||(t=e[++o]),"0"===t){if(o+1===n)return!0;if(t=e[++o],"b"===t){for(o++;n>o;o++)if(t=e[o],"_"!==t){if("0"!==t&&"1"!==t)return!1;s=!0}return s}if("x"===t){for(o++;n>o;o++)if(t=e[o],"_"!==t){if(!i(e.charCodeAt(o)))return!1;s=!0}return s}for(;n>o;o++)if(t=e[o],"_"!==t){if(!r(e.charCodeAt(o)))return!1;s=!0}return s}for(;n>o;o++)if(t=e[o],"_"!==t){if(":"===t)break;if(!a(e.charCodeAt(o)))return!1;s=!0}return s?":"!==t?!0:/^(:[0-5]?[0-9])+$/.test(e.slice(o)):!1}function s(e){var t,n,i=e,r=1,a=[];return-1!==i.indexOf("_")&&(i=i.replace(/_/g,"")),t=i[0],"-"!==t&&"+"!==t||("-"===t&&(r=-1),i=i.slice(1),t=i[0]),"0"===i?0:"0"===t?"b"===i[1]?r*parseInt(i.slice(2),2):"x"===i[1]?r*parseInt(i,16):r*parseInt(i,8):-1!==i.indexOf(":")?(i.split(":").forEach(function(e){a.unshift(parseInt(e,10))}),i=0,n=1,a.forEach(function(e){i+=e*n,n*=60}),r*i):r*parseInt(i,10)}function l(e){return"[object Number]"===Object.prototype.toString.call(e)&&e%1===0&&!u.isNegativeZero(e)}var u=e("../common"),c=e("../type");t.exports=new c("tag:yaml.org,2002:int",{kind:"scalar",resolve:o,construct:s,predicate:l,represent:{binary:function(e){return"0b"+e.toString(2)},octal:function(e){return"0"+e.toString(8)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return"0x"+e.toString(16).toUpperCase()}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}})},{"../common":21,"../type":32}],37:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;try{var t="("+e+")",n=s.parse(t,{range:!0});return"Program"===n.type&&1===n.body.length&&"ExpressionStatement"===n.body[0].type&&"FunctionExpression"===n.body[0].expression.type}catch(i){return!1}}function r(e){var t,n="("+e+")",i=s.parse(n,{range:!0}),r=[];if("Program"!==i.type||1!==i.body.length||"ExpressionStatement"!==i.body[0].type||"FunctionExpression"!==i.body[0].expression.type)throw new Error("Failed to resolve function");return i.body[0].expression.params.forEach(function(e){r.push(e.name)}),t=i.body[0].expression.body.range,new Function(r,n.slice(t[0]+1,t[1]-1))}function a(e){return e.toString()}function o(e){return"[object Function]"===Object.prototype.toString.call(e)}var s;try{var l=e;s=l("esprima")}catch(u){"undefined"!=typeof window&&(s=window.esprima)}var c=e("../../type");t.exports=new c("tag:yaml.org,2002:js/function",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],38:[function(e,t,n){"use strict";function i(e){if(null===e)return!1;if(0===e.length)return!1;var t=e,n=/\/([gim]*)$/.exec(e),i="";if("/"===t[0]){if(n&&(i=n[1]),i.length>3)return!1;if("/"!==t[t.length-i.length-1])return!1}return!0}function r(e){var t=e,n=/\/([gim]*)$/.exec(e),i="";return"/"===t[0]&&(n&&(i=n[1]),t=t.slice(1,t.length-i.length-1)),new RegExp(t,i)}function a(e){var t="/"+e.source+"/";return e.global&&(t+="g"),e.multiline&&(t+="m"),e.ignoreCase&&(t+="i"),t}function o(e){return"[object RegExp]"===Object.prototype.toString.call(e)}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/regexp",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],39:[function(e,t,n){"use strict";function i(){return!0}function r(){}function a(){return""}function o(e){return"undefined"==typeof e}var s=e("../../type");t.exports=new s("tag:yaml.org,2002:js/undefined",{kind:"scalar",resolve:i,construct:r,predicate:o,represent:a})},{"../../type":32}],40:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:map",{kind:"mapping",construct:function(e){return null!==e?e:{}}})},{"../type":32}],41:[function(e,t,n){"use strict";function i(e){return"<<"===e||null===e}var r=e("../type");t.exports=new r("tag:yaml.org,2002:merge",{kind:"scalar",resolve:i})},{"../type":32}],42:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t=e.length;return 1===t&&"~"===e||4===t&&("null"===e||"Null"===e||"NULL"===e)}function r(){return null}function a(e){return null===e}var o=e("../type");t.exports=new o("tag:yaml.org,2002:null",{kind:"scalar",resolve:i,construct:r,predicate:a,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"}},defaultStyle:"lowercase"})},{"../type":32}],43:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,a,l=[],u=e;for(t=0,n=u.length;n>t;t+=1){if(i=u[t],a=!1,"[object Object]"!==s.call(i))return!1;for(r in i)if(o.call(i,r)){if(a)return!1;a=!0}if(!a)return!1;if(-1!==l.indexOf(r))return!1;l.push(r)}return!0}function r(e){return null!==e?e:[]}var a=e("../type"),o=Object.prototype.hasOwnProperty,s=Object.prototype.toString;t.exports=new a("tag:yaml.org,2002:omap",{kind:"sequence",resolve:i,construct:r})},{"../type":32}],44:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n,i,r,a,s=e;for(a=new Array(s.length),t=0,n=s.length;n>t;t+=1){if(i=s[t],"[object Object]"!==o.call(i))return!1;if(r=Object.keys(i),1!==r.length)return!1;a[t]=[r[0],i[r[0]]]}return!0}function r(e){if(null===e)return[];var t,n,i,r,a,o=e;for(a=new Array(o.length),t=0,n=o.length;n>t;t+=1)i=o[t],r=Object.keys(i),a[t]=[r[0],i[r[0]]];return a}var a=e("../type"),o=Object.prototype.toString;t.exports=new a("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:i,construct:r})},{"../type":32}],45:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(e){return null!==e?e:[]}})},{"../type":32}],46:[function(e,t,n){"use strict";function i(e){if(null===e)return!0;var t,n=e;for(t in n)if(o.call(n,t)&&null!==n[t])return!1;return!0}function r(e){return null!==e?e:{}}var a=e("../type"),o=Object.prototype.hasOwnProperty;t.exports=new a("tag:yaml.org,2002:set",{kind:"mapping",resolve:i,construct:r})},{"../type":32}],47:[function(e,t,n){"use strict";var i=e("../type");t.exports=new i("tag:yaml.org,2002:str",{kind:"scalar",construct:function(e){return null!==e?e:""}})},{"../type":32}],48:[function(e,t,n){"use strict";function i(e){return null===e?!1:null!==s.exec(e)?!0:null!==l.exec(e)}function r(e){var t,n,i,r,a,o,u,c,p,h,f=0,d=null;if(t=s.exec(e),null===t&&(t=l.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(a=+t[4],o=+t[5],u=+t[6],t[7]){for(f=t[7].slice(0,3);f.length<3;)f+="0";f=+f}return t[9]&&(c=+t[10],p=+(t[11]||0),d=6e4*(60*c+p),"-"===t[9]&&(d=-d)),h=new Date(Date.UTC(n,i,r,a,o,u,f)),d&&h.setTime(h.getTime()-d),h}function a(e){return e.toISOString()}var o=e("../type"),s=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),l=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");t.exports=new o("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:i,construct:r,instanceOf:Date,represent:a})},{"../type":32}],49:[function(e,t,n){function i(e,t,n){var i=e?e.length:0;if(!i)return-1;if("number"==typeof n)n=0>n?o(i+n,0):n;else if(n){var s=a(e,t);return i>s&&(t===t?t===e[s]:e[s]!==e[s])?s:-1}return r(e,t,n||0)}var r=e("../internal/baseIndexOf"),a=e("../internal/binaryIndex"),o=Math.max;t.exports=i},{"../internal/baseIndexOf":78,"../internal/binaryIndex":92}],50:[function(e,t,n){function i(e){var t=e?e.length:0;return t?e[t-1]:void 0}t.exports=i},{}],51:[function(e,t,n){function i(e){if(l(e)&&!s(e)&&!(e instanceof r)){if(e instanceof a)return e;if(p.call(e,"__chain__")&&p.call(e,"__wrapped__"))return u(e)}return new a(e)}var r=e("../internal/LazyWrapper"),a=e("../internal/LodashWrapper"),o=e("../internal/baseLodash"),s=e("../lang/isArray"),l=e("../internal/isObjectLike"),u=e("../internal/wrapperClone"),c=Object.prototype,p=c.hasOwnProperty;i.prototype=o.prototype,t.exports=i},{"../internal/LazyWrapper":60,"../internal/LodashWrapper":61,"../internal/baseLodash":82,"../internal/isObjectLike":126,"../internal/wrapperClone":137,"../lang/isArray":140}],52:[function(e,t,n){t.exports=e("./forEach")},{"./forEach":54}],53:[function(e,t,n){var i=e("../internal/baseEach"),r=e("../internal/createFind"),a=r(i);t.exports=a},{"../internal/baseEach":71,"../internal/createFind":102}],54:[function(e,t,n){var i=e("../internal/arrayEach"),r=e("../internal/baseEach"),a=e("../internal/createForEach"),o=a(i,r);t.exports=o},{"../internal/arrayEach":63,"../internal/baseEach":71,"../internal/createForEach":103}],55:[function(e,t,n){function i(e,t,n,i){var h=e?a(e):0;return l(h)||(e=c(e),h=e.length),n="number"!=typeof n||i&&s(t,n,i)?0:0>n?p(h+n,0):n||0,"string"==typeof e||!o(e)&&u(e)?h>=n&&e.indexOf(t,n)>-1:!!h&&r(e,t,n)>-1}var r=e("../internal/baseIndexOf"),a=e("../internal/getLength"),o=e("../lang/isArray"),s=e("../internal/isIterateeCall"),l=e("../internal/isLength"),u=e("../lang/isString"),c=e("../object/values"),p=Math.max;t.exports=i},{"../internal/baseIndexOf":78,"../internal/getLength":112,"../internal/isIterateeCall":122,"../internal/isLength":125,"../lang/isArray":140,"../lang/isString":146,"../object/values":152}],56:[function(e,t,n){function i(e,t,n){var i=s(e)?r:o;return t=a(t,n,3),i(e,t)}var r=e("../internal/arrayMap"),a=e("../internal/baseCallback"),o=e("../internal/baseMap"),s=e("../lang/isArray");t.exports=i},{"../internal/arrayMap":64,"../internal/baseCallback":67,"../internal/baseMap":83,"../lang/isArray":140}],57:[function(e,t,n){var i=e("../internal/getNative"),r=i(Date,"now"),a=r||function(){return(new Date).getTime()};t.exports=a},{"../internal/getNative":114}],58:[function(e,t,n){var i=e("../internal/createWrapper"),r=e("../internal/replaceHolders"),a=e("./restParam"),o=1,s=32,l=a(function(e,t,n){var a=o;if(n.length){var u=r(n,l.placeholder);a|=s}return i(e,a,t,n,u)});l.placeholder={},t.exports=l},{"../internal/createWrapper":106,"../internal/replaceHolders":132,"./restParam":59}],59:[function(e,t,n){function i(e,t){if("function"!=typeof e)throw new TypeError(r);return t=a(void 0===t?e.length-1:+t||0,0),function(){for(var n=arguments,i=-1,r=a(n.length-t,0),o=Array(r);++i<r;)o[i]=n[t+i];switch(t){case 0:return e.call(this,o);case 1:return e.call(this,n[0],o);case 2:return e.call(this,n[0],n[1],o)}var s=Array(t+1);for(i=-1;++i<t;)s[i]=n[i];return s[t]=o,e.apply(this,s)}}var r="Expected a function",a=Math.max;t.exports=i},{}],60:[function(e,t,n){function i(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=o,this.__views__=[]}var r=e("./baseCreate"),a=e("./baseLodash"),o=Number.POSITIVE_INFINITY;i.prototype=r(a.prototype),i.prototype.constructor=i,t.exports=i},{"./baseCreate":70,"./baseLodash":82}],61:[function(e,t,n){function i(e,t,n){this.__wrapped__=e,this.__actions__=n||[],this.__chain__=!!t}var r=e("./baseCreate"),a=e("./baseLodash");i.prototype=r(a.prototype),i.prototype.constructor=i,t.exports=i},{"./baseCreate":70,"./baseLodash":82}],62:[function(e,t,n){function i(e,t){var n=-1,i=e.length;for(t||(t=Array(i));++n<i;)t[n]=e[n];return t}t.exports=i},{}],63:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length;++n<i&&t(e[n],n,e)!==!1;);return e}t.exports=i},{}],64:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length,r=Array(i);++n<i;)r[n]=t(e[n],n,e);return r}t.exports=i},{}],65:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length;++n<i;)if(t(e[n],n,e))return!0;return!1}t.exports=i},{}],66:[function(e,t,n){function i(e,t){return null==t?e:r(t,a(t),e)}var r=e("./baseCopy"),a=e("../object/keys");t.exports=i},{"../object/keys":149,"./baseCopy":69}],67:[function(e,t,n){function i(e,t,n){var i=typeof e;return"function"==i?void 0===t?e:o(e,t,n):null==e?s:"object"==i?r(e):void 0===t?l(e):a(e,t)}var r=e("./baseMatches"),a=e("./baseMatchesProperty"),o=e("./bindCallback"),s=e("../utility/identity"),l=e("../utility/property");t.exports=i},{"../utility/identity":154,"../utility/property":156,"./baseMatches":84,"./baseMatchesProperty":85,"./bindCallback":94}],68:[function(e,t,n){function i(e,t,n,m,y,g,v){var w;if(n&&(w=y?n(e,m,y):n(e)),void 0!==w)return w;if(!f(e))return e;var x=p(e);if(x){if(w=l(e),!t)return r(e,w)}else{var j=N.call(e),O=j==b;if(j!=A&&j!=d&&(!O||y))return D[j]?u(e,j,t):y?e:{};if(h(e))return y?e:{};if(w=c(O?{}:e),!t)return o(w,e)}g||(g=[]),v||(v=[]);for(var _=g.length;_--;)if(g[_]==e)return v[_];return g.push(e),v.push(w),(x?a:s)(e,function(r,a){w[a]=i(r,t,n,a,e,g,v)}),w}var r=e("./arrayCopy"),a=e("./arrayEach"),o=e("./baseAssign"),s=e("./baseForOwn"),l=e("./initCloneArray"),u=e("./initCloneByTag"),c=e("./initCloneObject"),p=e("../lang/isArray"),h=e("./isHostObject"),f=e("../lang/isObject"),d="[object Arguments]",m="[object Array]",y="[object Boolean]",g="[object Date]",v="[object Error]",b="[object Function]",w="[object Map]",x="[object Number]",A="[object Object]",j="[object RegExp]",O="[object Set]",_="[object String]",S="[object WeakMap]",k="[object ArrayBuffer]",C="[object Float32Array]",E="[object Float64Array]",I="[object Int8Array]",T="[object Int16Array]",$="[object Int32Array]",M="[object Uint8Array]",U="[object Uint8ClampedArray]",P="[object Uint16Array]",L="[object Uint32Array]",D={};D[d]=D[m]=D[k]=D[y]=D[g]=D[C]=D[E]=D[I]=D[T]=D[$]=D[x]=D[A]=D[j]=D[_]=D[M]=D[U]=D[P]=D[L]=!0,D[v]=D[b]=D[w]=D[O]=D[S]=!1;var R=Object.prototype,N=R.toString;t.exports=i},{"../lang/isArray":140,"../lang/isObject":144,"./arrayCopy":62,"./arrayEach":63,"./baseAssign":66,"./baseForOwn":76,"./initCloneArray":116,"./initCloneByTag":117,"./initCloneObject":118,"./isHostObject":120}],69:[function(e,t,n){function i(e,t,n){n||(n={});for(var i=-1,r=t.length;++i<r;){var a=t[i];n[a]=e[a]}return n}t.exports=i},{}],70:[function(e,t,n){var i=e("../lang/isObject"),r=function(){function e(){}return function(t){if(i(t)){e.prototype=t;var n=new e;e.prototype=void 0}return n||{}}}();t.exports=r},{"../lang/isObject":144}],71:[function(e,t,n){var i=e("./baseForOwn"),r=e("./createBaseEach"),a=r(i);t.exports=a},{"./baseForOwn":76,"./createBaseEach":98}],72:[function(e,t,n){function i(e,t,n,i){var r;return n(e,function(e,n,a){return t(e,n,a)?(r=i?n:e,!1):void 0}),r}t.exports=i},{}],73:[function(e,t,n){function i(e,t,n){for(var i=e.length,r=n?i:-1;n?r--:++r<i;)if(t(e[r],r,e))return r;return-1}t.exports=i},{}],74:[function(e,t,n){var i=e("./createBaseFor"),r=i();t.exports=r},{"./createBaseFor":99}],75:[function(e,t,n){function i(e,t){return r(e,t,a)}var r=e("./baseFor"),a=e("../object/keysIn");t.exports=i},{"../object/keysIn":150,"./baseFor":74}],76:[function(e,t,n){function i(e,t){return r(e,t,a)}var r=e("./baseFor"),a=e("../object/keys");t.exports=i},{"../object/keys":149,"./baseFor":74}],77:[function(e,t,n){function i(e,t,n){if(null!=e){e=r(e),void 0!==n&&n in e&&(t=[n]);for(var i=0,a=t.length;null!=e&&a>i;)e=r(e)[t[i++]];return i&&i==a?e:void 0}}var r=e("./toObject");t.exports=i},{"./toObject":135}],78:[function(e,t,n){function i(e,t,n){if(t!==t)return r(e,n);for(var i=n-1,a=e.length;++i<a;)if(e[i]===t)return i;return-1}var r=e("./indexOfNaN");t.exports=i},{"./indexOfNaN":115}],79:[function(e,t,n){function i(e,t,n,s,l,u){return e===t?!0:null==e||null==t||!a(e)&&!o(t)?e!==e&&t!==t:r(e,t,i,n,s,l,u)}var r=e("./baseIsEqualDeep"),a=e("../lang/isObject"),o=e("./isObjectLike");t.exports=i},{"../lang/isObject":144,"./baseIsEqualDeep":80,"./isObjectLike":126}],80:[function(e,t,n){function i(e,t,n,i,f,y,g){var v=s(e),b=s(t),w=p,x=p;v||(w=m.call(e),w==c?w=h:w!=h&&(v=u(e))),b||(x=m.call(t),x==c?x=h:x!=h&&(b=u(t)));var A=w==h&&!l(e),j=x==h&&!l(t),O=w==x;if(O&&!v&&!A)return a(e,t,w);if(!f){var _=A&&d.call(e,"__wrapped__"),S=j&&d.call(t,"__wrapped__");if(_||S)return n(_?e.value():e,S?t.value():t,i,f,y,g)}if(!O)return!1;y||(y=[]),g||(g=[]);for(var k=y.length;k--;)if(y[k]==e)return g[k]==t;y.push(e),g.push(t);var C=(v?r:o)(e,t,n,i,f,y,g);return y.pop(),g.pop(),C}var r=e("./equalArrays"),a=e("./equalByTag"),o=e("./equalObjects"),s=e("../lang/isArray"),l=e("./isHostObject"),u=e("../lang/isTypedArray"),c="[object Arguments]",p="[object Array]",h="[object Object]",f=Object.prototype,d=f.hasOwnProperty,m=f.toString;t.exports=i},{"../lang/isArray":140,"../lang/isTypedArray":147,"./equalArrays":107,"./equalByTag":108,"./equalObjects":109,"./isHostObject":120}],81:[function(e,t,n){function i(e,t,n){var i=t.length,o=i,s=!n;if(null==e)return!o;for(e=a(e);i--;){var l=t[i];if(s&&l[2]?l[1]!==e[l[0]]:!(l[0]in e))return!1}for(;++i<o;){l=t[i];var u=l[0],c=e[u],p=l[1];if(s&&l[2]){if(void 0===c&&!(u in e))return!1}else{var h=n?n(c,p,u):void 0;if(!(void 0===h?r(p,c,n,!0):h))return!1}}return!0}var r=e("./baseIsEqual"),a=e("./toObject");t.exports=i},{"./baseIsEqual":79,"./toObject":135}],82:[function(e,t,n){function i(){}t.exports=i},{}],83:[function(e,t,n){function i(e,t){var n=-1,i=a(e)?Array(e.length):[];return r(e,function(e,r,a){i[++n]=t(e,r,a)}),i}var r=e("./baseEach"),a=e("./isArrayLike");t.exports=i},{"./baseEach":71,"./isArrayLike":119}],84:[function(e,t,n){function i(e){var t=a(e);if(1==t.length&&t[0][2]){var n=t[0][0],i=t[0][1];return function(e){return null==e?!1:(e=o(e),e[n]===i&&(void 0!==i||n in e))}}return function(e){return r(e,t)}}var r=e("./baseIsMatch"),a=e("./getMatchData"),o=e("./toObject");t.exports=i},{"./baseIsMatch":81,"./getMatchData":113,"./toObject":135}],85:[function(e,t,n){function i(e,t){var n=s(e),i=l(e)&&u(t),f=e+"";return e=h(e),function(s){if(null==s)return!1;var l=f;if(s=p(s),(n||!i)&&!(l in s)){if(s=1==e.length?s:r(s,o(e,0,-1)),null==s)return!1;l=c(e),s=p(s)}return s[l]===t?void 0!==t||l in s:a(t,s[l],void 0,!0)}}var r=e("./baseGet"),a=e("./baseIsEqual"),o=e("./baseSlice"),s=e("../lang/isArray"),l=e("./isKey"),u=e("./isStrictComparable"),c=e("../array/last"),p=e("./toObject"),h=e("./toPath");t.exports=i},{"../array/last":50,"../lang/isArray":140,"./baseGet":77,"./baseIsEqual":79,"./baseSlice":89,"./isKey":123,"./isStrictComparable":127,"./toObject":135,"./toPath":136}],86:[function(e,t,n){function i(e){return function(t){return null==t?void 0:r(t)[e]}}var r=e("./toObject");t.exports=i},{"./toObject":135}],87:[function(e,t,n){function i(e){var t=e+"";return e=a(e),function(n){return r(n,e,t)}}var r=e("./baseGet"),a=e("./toPath");t.exports=i},{"./baseGet":77,"./toPath":136}],88:[function(e,t,n){var i=e("../utility/identity"),r=e("./metaMap"),a=r?function(e,t){return r.set(e,t),e}:i;t.exports=a},{"../utility/identity":154,"./metaMap":129}],89:[function(e,t,n){function i(e,t,n){var i=-1,r=e.length;t=null==t?0:+t||0,0>t&&(t=-t>r?0:r+t),n=void 0===n||n>r?r:+n||0,0>n&&(n+=r),r=t>n?0:n-t>>>0,t>>>=0;for(var a=Array(r);++i<r;)a[i]=e[i+t];return a}t.exports=i},{}],90:[function(e,t,n){function i(e){return null==e?"":e+""}t.exports=i},{}],91:[function(e,t,n){function i(e,t){for(var n=-1,i=t.length,r=Array(i);++n<i;)r[n]=e[t[n]];return r}t.exports=i},{}],92:[function(e,t,n){function i(e,t,n){var i=0,o=e?e.length:i;if("number"==typeof t&&t===t&&s>=o){for(;o>i;){var l=i+o>>>1,u=e[l];(n?t>=u:t>u)&&null!==u?i=l+1:o=l}return o}return r(e,t,a,n)}var r=e("./binaryIndexBy"),a=e("../utility/identity"),o=4294967295,s=o>>>1;t.exports=i},{"../utility/identity":154,"./binaryIndexBy":93}],93:[function(e,t,n){function i(e,t,n,i){t=n(t);for(var o=0,l=e?e.length:0,u=t!==t,c=null===t,p=void 0===t;l>o;){var h=r((o+l)/2),f=n(e[h]),d=void 0!==f,m=f===f;if(u)var y=m||i;else y=c?m&&d&&(i||null!=f):p?m&&(i||d):null==f?!1:i?t>=f:t>f;y?o=h+1:l=h}return a(l,s)}var r=Math.floor,a=Math.min,o=4294967295,s=o-1;t.exports=i},{}],94:[function(e,t,n){function i(e,t,n){if("function"!=typeof e)return r;if(void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,i,r){return e.call(t,n,i,r)};case 4:return function(n,i,r,a){return e.call(t,n,i,r,a)};case 5:return function(n,i,r,a,o){return e.call(t,n,i,r,a,o)}}return function(){return e.apply(t,arguments)}}var r=e("../utility/identity");t.exports=i},{"../utility/identity":154}],95:[function(e,t,n){(function(e){function n(e){var t=new i(e.byteLength),n=new r(t);return n.set(new r(e)),t}var i=e.ArrayBuffer,r=e.Uint8Array;t.exports=n}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],96:[function(e,t,n){function i(e,t,n){for(var i=n.length,a=-1,o=r(e.length-i,0),s=-1,l=t.length,u=Array(l+o);++s<l;)u[s]=t[s];for(;++a<i;)u[n[a]]=e[a];for(;o--;)u[s++]=e[a++];return u}var r=Math.max;t.exports=i},{}],97:[function(e,t,n){function i(e,t,n){for(var i=-1,a=n.length,o=-1,s=r(e.length-a,0),l=-1,u=t.length,c=Array(s+u);++o<s;)c[o]=e[o];for(var p=o;++l<u;)c[p+l]=t[l];for(;++i<a;)c[p+n[i]]=e[o++];return c}var r=Math.max;t.exports=i},{}],98:[function(e,t,n){function i(e,t){return function(n,i){var s=n?r(n):0;if(!a(s))return e(n,i);for(var l=t?s:-1,u=o(n);(t?l--:++l<s)&&i(u[l],l,u)!==!1;);return n}}var r=e("./getLength"),a=e("./isLength"),o=e("./toObject");t.exports=i},{"./getLength":112,"./isLength":125,"./toObject":135}],99:[function(e,t,n){function i(e){return function(t,n,i){for(var a=r(t),o=i(t),s=o.length,l=e?s:-1;e?l--:++l<s;){var u=o[l];if(n(a[u],u,a)===!1)break}return t}}var r=e("./toObject");t.exports=i},{"./toObject":135}],100:[function(e,t,n){(function(n){function i(e,t){function i(){var r=this&&this!==n&&this instanceof i?a:e;return r.apply(t,arguments)}var a=r(e);return i}var r=e("./createCtorWrapper");t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":101}],101:[function(e,t,n){function i(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:
+return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=r(e.prototype),i=e.apply(n,t);return a(i)?i:n}}var r=e("./baseCreate"),a=e("../lang/isObject");t.exports=i},{"../lang/isObject":144,"./baseCreate":70}],102:[function(e,t,n){function i(e,t){return function(n,i,l){if(i=r(i,l,3),s(n)){var u=o(n,i,t);return u>-1?n[u]:void 0}return a(n,i,e)}}var r=e("./baseCallback"),a=e("./baseFind"),o=e("./baseFindIndex"),s=e("../lang/isArray");t.exports=i},{"../lang/isArray":140,"./baseCallback":67,"./baseFind":72,"./baseFindIndex":73}],103:[function(e,t,n){function i(e,t){return function(n,i,o){return"function"==typeof i&&void 0===o&&a(n)?e(n,i):t(n,r(i,o,3))}}var r=e("./bindCallback"),a=e("../lang/isArray");t.exports=i},{"../lang/isArray":140,"./bindCallback":94}],104:[function(e,t,n){(function(n){function i(e,t,x,A,j,O,_,S,k,C){function E(){for(var d=arguments.length,m=d,y=Array(d);m--;)y[m]=arguments[m];if(A&&(y=a(y,A,j)),O&&(y=o(y,O,_)),M||P){var b=E.placeholder,D=c(y,b);if(d-=D.length,C>d){var R=S?r(S):void 0,N=w(C-d,0),F=M?D:void 0,B=M?void 0:D,q=M?y:void 0,V=M?void 0:y;t|=M?g:v,t&=~(M?v:g),U||(t&=~(h|f));var z=[e,t,x,q,F,V,B,R,k,N],H=i.apply(void 0,z);return l(e)&&p(H,z),H.placeholder=b,H}}var J=T?x:this,Q=$?J[e]:e;return S&&(y=u(y,S)),I&&k<y.length&&(y.length=k),this&&this!==n&&this instanceof E&&(Q=L||s(e)),Q.apply(J,y)}var I=t&b,T=t&h,$=t&f,M=t&m,U=t&d,P=t&y,L=$?void 0:s(e);return E}var r=e("./arrayCopy"),a=e("./composeArgs"),o=e("./composeArgsRight"),s=e("./createCtorWrapper"),l=e("./isLaziable"),u=e("./reorder"),c=e("./replaceHolders"),p=e("./setData"),h=1,f=2,d=4,m=8,y=16,g=32,v=64,b=128,w=Math.max;t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./arrayCopy":62,"./composeArgs":96,"./composeArgsRight":97,"./createCtorWrapper":101,"./isLaziable":124,"./reorder":131,"./replaceHolders":132,"./setData":133}],105:[function(e,t,n){(function(n){function i(e,t,i,o){function s(){for(var t=-1,r=arguments.length,a=-1,c=o.length,p=Array(c+r);++a<c;)p[a]=o[a];for(;r--;)p[a++]=arguments[++t];var h=this&&this!==n&&this instanceof s?u:e;return h.apply(l?i:this,p)}var l=t&a,u=r(e);return s}var r=e("./createCtorWrapper"),a=1;t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":101}],106:[function(e,t,n){function i(e,t,n,i,g,v,b,w){var x=t&h;if(!x&&"function"!=typeof e)throw new TypeError(m);var A=i?i.length:0;if(A||(t&=~(f|d),i=g=void 0),A-=g?g.length:0,t&d){var j=i,O=g;i=g=void 0}var _=x?void 0:l(e),S=[e,t,n,i,g,j,O,v,b,w];if(_&&(u(S,_),t=S[1],w=S[9]),S[9]=null==w?x?0:e.length:y(w-A,0)||0,t==p)var k=a(S[0],S[2]);else k=t!=f&&t!=(p|f)||S[4].length?o.apply(void 0,S):s.apply(void 0,S);var C=_?r:c;return C(k,S)}var r=e("./baseSetData"),a=e("./createBindWrapper"),o=e("./createHybridWrapper"),s=e("./createPartialWrapper"),l=e("./getData"),u=e("./mergeData"),c=e("./setData"),p=1,h=2,f=32,d=64,m="Expected a function",y=Math.max;t.exports=i},{"./baseSetData":88,"./createBindWrapper":100,"./createHybridWrapper":104,"./createPartialWrapper":105,"./getData":110,"./mergeData":128,"./setData":133}],107:[function(e,t,n){function i(e,t,n,i,a,o,s){var l=-1,u=e.length,c=t.length;if(u!=c&&!(a&&c>u))return!1;for(;++l<u;){var p=e[l],h=t[l],f=i?i(a?h:p,a?p:h,l):void 0;if(void 0!==f){if(f)continue;return!1}if(a){if(!r(t,function(e){return p===e||n(p,e,i,a,o,s)}))return!1}else if(p!==h&&!n(p,h,i,a,o,s))return!1}return!0}var r=e("./arraySome");t.exports=i},{"./arraySome":65}],108:[function(e,t,n){function i(e,t,n){switch(n){case r:case a:return+e==+t;case o:return e.name==t.name&&e.message==t.message;case s:return e!=+e?t!=+t:e==+t;case l:case u:return e==t+""}return!1}var r="[object Boolean]",a="[object Date]",o="[object Error]",s="[object Number]",l="[object RegExp]",u="[object String]";t.exports=i},{}],109:[function(e,t,n){function i(e,t,n,i,a,s,l){var u=r(e),c=u.length,p=r(t),h=p.length;if(c!=h&&!a)return!1;for(var f=c;f--;){var d=u[f];if(!(a?d in t:o.call(t,d)))return!1}for(var m=a;++f<c;){d=u[f];var y=e[d],g=t[d],v=i?i(a?g:y,a?y:g,d):void 0;if(!(void 0===v?n(y,g,i,a,s,l):v))return!1;m||(m="constructor"==d)}if(!m){var b=e.constructor,w=t.constructor;if(b!=w&&"constructor"in e&&"constructor"in t&&!("function"==typeof b&&b instanceof b&&"function"==typeof w&&w instanceof w))return!1}return!0}var r=e("../object/keys"),a=Object.prototype,o=a.hasOwnProperty;t.exports=i},{"../object/keys":149}],110:[function(e,t,n){var i=e("./metaMap"),r=e("../utility/noop"),a=i?function(e){return i.get(e)}:r;t.exports=a},{"../utility/noop":155,"./metaMap":129}],111:[function(e,t,n){function i(e){for(var t=e.name+"",n=r[t],i=n?n.length:0;i--;){var a=n[i],o=a.func;if(null==o||o==e)return a.name}return t}var r=e("./realNames");t.exports=i},{"./realNames":130}],112:[function(e,t,n){var i=e("./baseProperty"),r=i("length");t.exports=r},{"./baseProperty":86}],113:[function(e,t,n){function i(e){for(var t=a(e),n=t.length;n--;)t[n][2]=r(t[n][1]);return t}var r=e("./isStrictComparable"),a=e("../object/pairs");t.exports=i},{"../object/pairs":151,"./isStrictComparable":127}],114:[function(e,t,n){function i(e,t){var n=null==e?void 0:e[t];return r(n)?n:void 0}var r=e("../lang/isNative");t.exports=i},{"../lang/isNative":143}],115:[function(e,t,n){function i(e,t,n){for(var i=e.length,r=t+(n?0:-1);n?r--:++r<i;){var a=e[r];if(a!==a)return r}return-1}t.exports=i},{}],116:[function(e,t,n){function i(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&a.call(e,"index")&&(n.index=e.index,n.input=e.input),n}var r=Object.prototype,a=r.hasOwnProperty;t.exports=i},{}],117:[function(e,t,n){(function(n){function i(e,t,n){var i=e.constructor;switch(t){case c:return r(e);case a:case o:return new i(+e);case p:case h:case f:case d:case m:case y:case g:case v:case b:i instanceof i&&(i=A[t]);var x=e.buffer;return new i(n?r(x):x,e.byteOffset,e.length);case s:case u:return new i(e);case l:var j=new i(e.source,w.exec(e));j.lastIndex=e.lastIndex}return j}var r=e("./bufferClone"),a="[object Boolean]",o="[object Date]",s="[object Number]",l="[object RegExp]",u="[object String]",c="[object ArrayBuffer]",p="[object Float32Array]",h="[object Float64Array]",f="[object Int8Array]",d="[object Int16Array]",m="[object Int32Array]",y="[object Uint8Array]",g="[object Uint8ClampedArray]",v="[object Uint16Array]",b="[object Uint32Array]",w=/\w*$/,x=n.Uint8Array,A={};A[p]=n.Float32Array,A[h]=n.Float64Array,A[f]=n.Int8Array,A[d]=n.Int16Array,A[m]=n.Int32Array,A[y]=x,A[g]=n.Uint8ClampedArray,A[v]=n.Uint16Array,A[b]=n.Uint32Array,t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./bufferClone":95}],118:[function(e,t,n){function i(e){var t=e.constructor;return"function"==typeof t&&t instanceof t||(t=Object),new t}t.exports=i},{}],119:[function(e,t,n){function i(e){return null!=e&&a(r(e))}var r=e("./getLength"),a=e("./isLength");t.exports=i},{"./getLength":112,"./isLength":125}],120:[function(e,t,n){var i=function(){try{Object({toString:0}+"")}catch(e){return function(){return!1}}return function(e){return"function"!=typeof e.toString&&"string"==typeof(e+"")}}();t.exports=i},{}],121:[function(e,t,n){function i(e,t){return e="number"==typeof e||r.test(e)?+e:-1,t=null==t?a:t,e>-1&&e%1==0&&t>e}var r=/^\d+$/,a=9007199254740991;t.exports=i},{}],122:[function(e,t,n){function i(e,t,n){if(!o(n))return!1;var i=typeof t;if("number"==i?r(n)&&a(t,n.length):"string"==i&&t in n){var s=n[t];return e===e?e===s:s!==s}return!1}var r=e("./isArrayLike"),a=e("./isIndex"),o=e("../lang/isObject");t.exports=i},{"../lang/isObject":144,"./isArrayLike":119,"./isIndex":121}],123:[function(e,t,n){function i(e,t){var n=typeof e;if("string"==n&&s.test(e)||"number"==n)return!0;if(r(e))return!1;var i=!o.test(e);return i||null!=t&&e in a(t)}var r=e("../lang/isArray"),a=e("./toObject"),o=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/,s=/^\w*$/;t.exports=i},{"../lang/isArray":140,"./toObject":135}],124:[function(e,t,n){function i(e){var t=o(e),n=s[t];if("function"!=typeof n||!(t in r.prototype))return!1;if(e===n)return!0;var i=a(n);return!!i&&e===i[0]}var r=e("./LazyWrapper"),a=e("./getData"),o=e("./getFuncName"),s=e("../chain/lodash");t.exports=i},{"../chain/lodash":51,"./LazyWrapper":60,"./getData":110,"./getFuncName":111}],125:[function(e,t,n){function i(e){return"number"==typeof e&&e>-1&&e%1==0&&r>=e}var r=9007199254740991;t.exports=i},{}],126:[function(e,t,n){function i(e){return!!e&&"object"==typeof e}t.exports=i},{}],127:[function(e,t,n){function i(e){return e===e&&!r(e)}var r=e("../lang/isObject");t.exports=i},{"../lang/isObject":144}],128:[function(e,t,n){function i(e,t){var n=e[1],i=t[1],m=n|i,y=p>m,g=i==p&&n==c||i==p&&n==h&&e[7].length<=t[8]||i==(p|h)&&n==c;if(!y&&!g)return e;i&l&&(e[2]=t[2],m|=n&l?0:u);var v=t[3];if(v){var b=e[3];e[3]=b?a(b,v,t[4]):r(v),e[4]=b?s(e[3],f):r(t[4])}return v=t[5],v&&(b=e[5],e[5]=b?o(b,v,t[6]):r(v),e[6]=b?s(e[5],f):r(t[6])),v=t[7],v&&(e[7]=r(v)),i&p&&(e[8]=null==e[8]?t[8]:d(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=m,e}var r=e("./arrayCopy"),a=e("./composeArgs"),o=e("./composeArgsRight"),s=e("./replaceHolders"),l=1,u=4,c=8,p=128,h=256,f="__lodash_placeholder__",d=Math.min;t.exports=i},{"./arrayCopy":62,"./composeArgs":96,"./composeArgsRight":97,"./replaceHolders":132}],129:[function(e,t,n){(function(n){var i=e("./getNative"),r=i(n,"WeakMap"),a=r&&new r;t.exports=a}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./getNative":114}],130:[function(e,t,n){var i={};t.exports=i},{}],131:[function(e,t,n){function i(e,t){for(var n=e.length,i=o(t.length,n),s=r(e);i--;){var l=t[i];e[i]=a(l,n)?s[l]:void 0}return e}var r=e("./arrayCopy"),a=e("./isIndex"),o=Math.min;t.exports=i},{"./arrayCopy":62,"./isIndex":121}],132:[function(e,t,n){function i(e,t){for(var n=-1,i=e.length,a=-1,o=[];++n<i;)e[n]===t&&(e[n]=r,o[++a]=n);return o}var r="__lodash_placeholder__";t.exports=i},{}],133:[function(e,t,n){var i=e("./baseSetData"),r=e("../date/now"),a=150,o=16,s=function(){var e=0,t=0;return function(n,s){var l=r(),u=o-(l-t);if(t=l,u>0){if(++e>=a)return n}else e=0;return i(n,s)}}();t.exports=s},{"../date/now":57,"./baseSetData":88}],134:[function(e,t,n){function i(e){for(var t=u(e),n=t.length,i=n&&e.length,c=!!i&&s(i)&&(a(e)||r(e)||l(e)),h=-1,f=[];++h<n;){var d=t[h];(c&&o(d,i)||p.call(e,d))&&f.push(d)}return f}var r=e("../lang/isArguments"),a=e("../lang/isArray"),o=e("./isIndex"),s=e("./isLength"),l=e("../lang/isString"),u=e("../object/keysIn"),c=Object.prototype,p=c.hasOwnProperty;t.exports=i},{"../lang/isArguments":139,"../lang/isArray":140,"../lang/isString":146,"../object/keysIn":150,"./isIndex":121,"./isLength":125}],135:[function(e,t,n){function i(e){if(o.unindexedChars&&a(e)){for(var t=-1,n=e.length,i=Object(e);++t<n;)i[t]=e.charAt(t);return i}return r(e)?e:Object(e)}var r=e("../lang/isObject"),a=e("../lang/isString"),o=e("../support");t.exports=i},{"../lang/isObject":144,"../lang/isString":146,"../support":153}],136:[function(e,t,n){function i(e){if(a(e))return e;var t=[];return r(e).replace(o,function(e,n,i,r){t.push(i?r.replace(s,"$1"):n||e)}),t}var r=e("./baseToString"),a=e("../lang/isArray"),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g,s=/\\(\\)?/g;t.exports=i},{"../lang/isArray":140,"./baseToString":90}],137:[function(e,t,n){function i(e){return e instanceof r?e.clone():new a(e.__wrapped__,e.__chain__,o(e.__actions__))}var r=e("./LazyWrapper"),a=e("./LodashWrapper"),o=e("./arrayCopy");t.exports=i},{"./LazyWrapper":60,"./LodashWrapper":61,"./arrayCopy":62}],138:[function(e,t,n){function i(e,t,n){return"function"==typeof t?r(e,!0,a(t,n,3)):r(e,!0)}var r=e("../internal/baseClone"),a=e("../internal/bindCallback");t.exports=i},{"../internal/baseClone":68,"../internal/bindCallback":94}],139:[function(e,t,n){function i(e){return a(e)&&r(e)&&s.call(e,"callee")&&!l.call(e,"callee")}var r=e("../internal/isArrayLike"),a=e("../internal/isObjectLike"),o=Object.prototype,s=o.hasOwnProperty,l=o.propertyIsEnumerable;t.exports=i},{"../internal/isArrayLike":119,"../internal/isObjectLike":126}],140:[function(e,t,n){var i=e("../internal/getNative"),r=e("../internal/isLength"),a=e("../internal/isObjectLike"),o="[object Array]",s=Object.prototype,l=s.toString,u=i(Array,"isArray"),c=u||function(e){return a(e)&&r(e.length)&&l.call(e)==o};t.exports=c},{"../internal/getNative":114,"../internal/isLength":125,"../internal/isObjectLike":126}],141:[function(e,t,n){function i(e){return null==e?!0:o(e)&&(a(e)||u(e)||r(e)||l(e)&&s(e.splice))?!e.length:!c(e).length}var r=e("./isArguments"),a=e("./isArray"),o=e("../internal/isArrayLike"),s=e("./isFunction"),l=e("../internal/isObjectLike"),u=e("./isString"),c=e("../object/keys");t.exports=i},{"../internal/isArrayLike":119,"../internal/isObjectLike":126,"../object/keys":149,"./isArguments":139,"./isArray":140,"./isFunction":142,"./isString":146}],142:[function(e,t,n){function i(e){return r(e)&&s.call(e)==a}var r=e("./isObject"),a="[object Function]",o=Object.prototype,s=o.toString;t.exports=i},{"./isObject":144}],143:[function(e,t,n){function i(e){return null==e?!1:r(e)?p.test(u.call(e)):o(e)&&(a(e)?p:s).test(e)}var r=e("./isFunction"),a=e("../internal/isHostObject"),o=e("../internal/isObjectLike"),s=/^\[object .+?Constructor\]$/,l=Object.prototype,u=Function.prototype.toString,c=l.hasOwnProperty,p=RegExp("^"+u.call(c).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=i},{"../internal/isHostObject":120,"../internal/isObjectLike":126,"./isFunction":142}],144:[function(e,t,n){function i(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}t.exports=i},{}],145:[function(e,t,n){function i(e){var t;if(!s(e)||h.call(e)!=u||o(e)||a(e)||!p.call(e,"constructor")&&(t=e.constructor,"function"==typeof t&&!(t instanceof t)))return!1;var n;return l.ownLast?(r(e,function(e,t,i){return n=p.call(i,t),!1}),n!==!1):(r(e,function(e,t){n=t}),void 0===n||p.call(e,n))}var r=e("../internal/baseForIn"),a=e("./isArguments"),o=e("../internal/isHostObject"),s=e("../internal/isObjectLike"),l=e("../support"),u="[object Object]",c=Object.prototype,p=c.hasOwnProperty,h=c.toString;t.exports=i},{"../internal/baseForIn":75,"../internal/isHostObject":120,"../internal/isObjectLike":126,"../support":153,"./isArguments":139}],146:[function(e,t,n){function i(e){return"string"==typeof e||r(e)&&s.call(e)==a}var r=e("../internal/isObjectLike"),a="[object String]",o=Object.prototype,s=o.toString;t.exports=i},{"../internal/isObjectLike":126}],147:[function(e,t,n){function i(e){return a(e)&&r(e.length)&&!!E[T.call(e)]}var r=e("../internal/isLength"),a=e("../internal/isObjectLike"),o="[object Arguments]",s="[object Array]",l="[object Boolean]",u="[object Date]",c="[object Error]",p="[object Function]",h="[object Map]",f="[object Number]",d="[object Object]",m="[object RegExp]",y="[object Set]",g="[object String]",v="[object WeakMap]",b="[object ArrayBuffer]",w="[object Float32Array]",x="[object Float64Array]",A="[object Int8Array]",j="[object Int16Array]",O="[object Int32Array]",_="[object Uint8Array]",S="[object Uint8ClampedArray]",k="[object Uint16Array]",C="[object Uint32Array]",E={};E[w]=E[x]=E[A]=E[j]=E[O]=E[_]=E[S]=E[k]=E[C]=!0,E[o]=E[s]=E[b]=E[l]=E[u]=E[c]=E[p]=E[h]=E[f]=E[d]=E[m]=E[y]=E[g]=E[v]=!1;var I=Object.prototype,T=I.toString;t.exports=i},{"../internal/isLength":125,"../internal/isObjectLike":126}],148:[function(e,t,n){function i(e){return void 0===e}t.exports=i},{}],149:[function(e,t,n){var i=e("../internal/getNative"),r=e("../internal/isArrayLike"),a=e("../lang/isObject"),o=e("../internal/shimKeys"),s=e("../support"),l=i(Object,"keys"),u=l?function(e){var t=null==e?void 0:e.constructor;return"function"==typeof t&&t.prototype===e||("function"==typeof e?s.enumPrototypes:r(e))?o(e):a(e)?l(e):[]}:o;t.exports=u},{"../internal/getNative":114,"../internal/isArrayLike":119,"../internal/shimKeys":134,"../lang/isObject":144,"../support":153}],150:[function(e,t,n){function i(e){if(null==e)return[];c(e)||(e=Object(e));var t=e.length;t=t&&u(t)&&(o(e)||a(e)||p(e))&&t||0;for(var n=e.constructor,i=-1,r=s(n)&&n.prototype||O,f=r===e,d=Array(t),m=t>0,g=h.enumErrorProps&&(e===j||e instanceof Error),v=h.enumPrototypes&&s(e);++i<t;)d[i]=i+"";for(var w in e)v&&"prototype"==w||g&&("message"==w||"name"==w)||m&&l(w,t)||"constructor"==w&&(f||!S.call(e,w))||d.push(w);if(h.nonEnumShadows&&e!==O){var E=e===_?x:e===j?y:k.call(e),I=C[E]||C[b];for(E==b&&(r=O),t=A.length;t--;){w=A[t];var T=I[w];f&&T||(T?!S.call(e,w):e[w]===r[w])||d.push(w)}}return d}var r=e("../internal/arrayEach"),a=e("../lang/isArguments"),o=e("../lang/isArray"),s=e("../lang/isFunction"),l=e("../internal/isIndex"),u=e("../internal/isLength"),c=e("../lang/isObject"),p=e("../lang/isString"),h=e("../support"),f="[object Array]",d="[object Boolean]",m="[object Date]",y="[object Error]",g="[object Function]",v="[object Number]",b="[object Object]",w="[object RegExp]",x="[object String]",A=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],j=Error.prototype,O=Object.prototype,_=String.prototype,S=O.hasOwnProperty,k=O.toString,C={};C[f]=C[m]=C[v]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},C[d]=C[x]={constructor:!0,toString:!0,valueOf:!0},C[y]=C[g]=C[w]={constructor:!0,toString:!0},C[b]={constructor:!0},r(A,function(e){for(var t in C)if(S.call(C,t)){var n=C[t];n[e]=S.call(n,e)}}),t.exports=i},{"../internal/arrayEach":63,"../internal/isIndex":121,"../internal/isLength":125,"../lang/isArguments":139,"../lang/isArray":140,"../lang/isFunction":142,"../lang/isObject":144,"../lang/isString":146,"../support":153}],151:[function(e,t,n){function i(e){e=a(e);for(var t=-1,n=r(e),i=n.length,o=Array(i);++t<i;){var s=n[t];o[t]=[s,e[s]]}return o}var r=e("./keys"),a=e("../internal/toObject");t.exports=i},{"../internal/toObject":135,"./keys":149}],152:[function(e,t,n){function i(e){return r(e,a(e))}var r=e("../internal/baseValues"),a=e("./keys");t.exports=i},{"../internal/baseValues":91,"./keys":149}],153:[function(e,t,n){var i=Array.prototype,r=Error.prototype,a=Object.prototype,o=a.propertyIsEnumerable,s=i.splice,l={};!function(e){var t=function(){this.x=e},n={0:e,length:e},i=[];t.prototype={valueOf:e,y:e};for(var a in new t)i.push(a);l.enumErrorProps=o.call(r,"message")||o.call(r,"name"),l.enumPrototypes=o.call(t,"prototype"),l.nonEnumShadows=!/valueOf/.test(i),l.ownLast="x"!=i[0],l.spliceObjects=(s.call(n,0,1),!n[0]),l.unindexedChars="x"[0]+Object("x")[0]!="xx"}(1,0),t.exports=l},{}],154:[function(e,t,n){function i(e){return e}t.exports=i},{}],155:[function(e,t,n){function i(){}t.exports=i},{}],156:[function(e,t,n){function i(e){return o(e)?r(e):a(e)}var r=e("../internal/baseProperty"),a=e("../internal/basePropertyDeep"),o=e("../internal/isKey");t.exports=i},{"../internal/baseProperty":86,"../internal/basePropertyDeep":87,"../internal/isKey":123}],157:[function(e,n,i){(function(e){!function(e){"use strict";if("function"==typeof bootstrap)bootstrap("promise",e);else if("object"==typeof i&&"object"==typeof n)n.exports=e();else if("function"==typeof t&&t.amd)t(e);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeQ=e}else{if("undefined"==typeof window&&"undefined"==typeof self)throw new Error("This environment was not anticipated by Q. Please file a bug.");var r="undefined"!=typeof window?window:self,a=r.Q;r.Q=e(),r.Q.noConflict=function(){return r.Q=a,this}}}(function(){"use strict";function t(e){return function(){return K.apply(e,arguments)}}function n(e){return e===Object(e)}function i(e){return"[object StopIteration]"===ie(e)||e instanceof H}function r(e,t){if(q&&t.stack&&"object"==typeof e&&null!==e&&e.stack&&-1===e.stack.indexOf(re)){for(var n=[],i=t;i;i=i.source)i.stack&&n.unshift(i.stack);n.unshift(e.stack);var r=n.join("\n"+re+"\n");e.stack=a(r)}}function a(e){for(var t=e.split("\n"),n=[],i=0;i<t.length;++i){var r=t[i];l(r)||o(r)||!r||n.push(r)}return n.join("\n")}function o(e){return-1!==e.indexOf("(module.js:")||-1!==e.indexOf("(node.js:")}function s(e){var t=/at .+ \((.+):(\d+):(?:\d+)\)$/.exec(e);if(t)return[t[1],Number(t[2])];var n=/at ([^ ]+):(\d+):(?:\d+)$/.exec(e);if(n)return[n[1],Number(n[2])];var i=/.*@(.+):(\d+)$/.exec(e);return i?[i[1],Number(i[2])]:void 0}function l(e){var t=s(e);if(!t)return!1;var n=t[0],i=t[1];return n===z&&i>=J&&ue>=i}function u(){if(q)try{throw new Error}catch(e){var t=e.stack.split("\n"),n=t[0].indexOf("@")>0?t[1]:t[2],i=s(n);if(!i)return;return z=i[0],i[1]}}function c(e,t,n){return function(){return"undefined"!=typeof console&&"function"==typeof console.warn&&console.warn(t+" is deprecated, use "+n+" instead.",new Error("").stack),e.apply(e,arguments)}}function p(e){return e instanceof m?e:b(e)?C(e):k(e)}function h(){function e(e){t=e,a.source=e,X(n,function(t,n){p.nextTick(function(){e.promiseDispatch.apply(e,n)})},void 0),n=void 0,i=void 0}var t,n=[],i=[],r=ee(h.prototype),a=ee(m.prototype);if(a.promiseDispatch=function(e,r,a){var o=W(arguments);n?(n.push(o),"when"===r&&a[1]&&i.push(a[1])):p.nextTick(function(){t.promiseDispatch.apply(t,o)})},a.valueOf=function(){if(n)return a;var e=g(t);return v(e)&&(t=e),e},a.inspect=function(){return t?t.inspect():{state:"pending"}},p.longStackSupport&&q)try{throw new Error}catch(o){a.stack=o.stack.substring(o.stack.indexOf("\n")+1)}return r.promise=a,r.resolve=function(n){t||e(p(n))},r.fulfill=function(n){t||e(k(n))},r.reject=function(n){t||e(S(n))},r.notify=function(e){t||X(i,function(t,n){p.nextTick(function(){n(e)})},void 0)},r}function f(e){if("function"!=typeof e)throw new TypeError("resolver must be a function.");var t=h();try{e(t.resolve,t.reject,t.notify)}catch(n){t.reject(n)}return t.promise}function d(e){return f(function(t,n){for(var i=0,r=e.length;r>i;i++)p(e[i]).then(t,n)})}function m(e,t,n){void 0===t&&(t=function(e){return S(new Error("Promise does not support operation: "+e))}),void 0===n&&(n=function(){return{state:"unknown"}});var i=ee(m.prototype);if(i.promiseDispatch=function(n,r,a){var o;try{o=e[r]?e[r].apply(i,a):t.call(i,r,a)}catch(s){o=S(s)}n&&n(o)},i.inspect=n,n){var r=n();"rejected"===r.state&&(i.exception=r.reason),i.valueOf=function(){var e=n();return"pending"===e.state||"rejected"===e.state?i:e.value}}return i}function y(e,t,n,i){return p(e).then(t,n,i)}function g(e){if(v(e)){var t=e.inspect();if("fulfilled"===t.state)return t.value}return e}function v(e){return e instanceof m}function b(e){return n(e)&&"function"==typeof e.then}function w(e){return v(e)&&"pending"===e.inspect().state}function x(e){return!v(e)||"fulfilled"===e.inspect().state}function A(e){return v(e)&&"rejected"===e.inspect().state}function j(){ae.length=0,oe.length=0,le||(le=!0)}function O(t,n){le&&("object"==typeof e&&"function"==typeof e.emit&&p.nextTick.runAfter(function(){-1!==G(oe,t)&&(e.emit("unhandledRejection",n,t),se.push(t))}),oe.push(t),n&&"undefined"!=typeof n.stack?ae.push(n.stack):ae.push("(no stack) "+n))}function _(t){if(le){var n=G(oe,t);-1!==n&&("object"==typeof e&&"function"==typeof e.emit&&p.nextTick.runAfter(function(){var i=G(se,t);-1!==i&&(e.emit("rejectionHandled",ae[n],t),se.splice(i,1))}),oe.splice(n,1),ae.splice(n,1))}}function S(e){var t=m({when:function(t){return t&&_(this),t?t(e):this}},function(){return this},function(){return{state:"rejected",reason:e}});return O(t,e),t}function k(e){return m({when:function(){return e},get:function(t){return e[t]},set:function(t,n){e[t]=n},"delete":function(t){delete e[t]},post:function(t,n){return null===t||void 0===t?e.apply(void 0,n):e[t].apply(e,n)},apply:function(t,n){return e.apply(t,n)},keys:function(){return ne(e)}},void 0,function(){return{state:"fulfilled",value:e}})}function C(e){var t=h();return p.nextTick(function(){try{e.then(t.resolve,t.reject,t.notify)}catch(n){t.reject(n)}}),t.promise}function E(e){return m({isDef:function(){}},function(t,n){return P(e,t,n)},function(){return p(e).inspect()})}function I(e,t,n){return p(e).spread(t,n)}function T(e){return function(){function t(e,t){var o;if("undefined"==typeof StopIteration){try{o=n[e](t)}catch(s){return S(s)}return o.done?p(o.value):y(o.value,r,a)}try{o=n[e](t)}catch(s){return i(s)?p(s.value):S(s)}return y(o,r,a)}var n=e.apply(this,arguments),r=t.bind(t,"next"),a=t.bind(t,"throw");return r()}}function $(e){p.done(p.async(e)())}function M(e){throw new H(e)}function U(e){return function(){return I([this,L(arguments)],function(t,n){return e.apply(t,n)})}}function P(e,t,n){return p(e).dispatch(t,n)}function L(e){return y(e,function(e){var t=0,n=h();return X(e,function(i,r,a){var o;v(r)&&"fulfilled"===(o=r.inspect()).state?e[a]=o.value:(++t,y(r,function(i){e[a]=i,0===--t&&n.resolve(e)},n.reject,function(e){n.notify({index:a,value:e})}))},void 0),0===t&&n.resolve(e),n.promise})}function D(e){if(0===e.length)return p.resolve();var t=p.defer(),n=0;return X(e,function(i,r,a){function o(e){t.resolve(e)}function s(){n--,0===n&&t.reject(new Error("Can't get fulfillment value from any promise, all promises were rejected."))}function l(e){t.notify({index:a,value:e})}var u=e[a];n++,y(u,o,s,l)},void 0),t.promise}function R(e){return y(e,function(e){return e=Z(e,p),y(L(Z(e,function(e){return y(e,Q,Q)})),function(){return e})})}function N(e){return p(e).allSettled()}function F(e,t){return p(e).then(void 0,void 0,t)}function B(e,t){return p(e).nodeify(t)}var q=!1;try{throw new Error}catch(V){q=!!V.stack}var z,H,J=u(),Q=function(){},Y=function(){function t(){for(var e,t;i.next;)i=i.next,e=i.task,i.task=void 0,t=i.domain,t&&(i.domain=void 0,t.enter()),n(e,t);for(;l.length;)e=l.pop(),n(e);a=!1}function n(e,n){try{e()}catch(i){if(s)throw n&&n.exit(),setTimeout(t,0),n&&n.enter(),i;setTimeout(function(){throw i},0)}n&&n.exit()}var i={task:void 0,next:null},r=i,a=!1,o=void 0,s=!1,l=[];if(Y=function(t){r=r.next={task:t,domain:s&&e.domain,next:null},a||(a=!0,o())},"object"==typeof e&&"[object process]"===e.toString()&&e.nextTick)s=!0,o=function(){e.nextTick(t)};else if("function"==typeof setImmediate)o="undefined"!=typeof window?setImmediate.bind(window,t):function(){setImmediate(t)};else if("undefined"!=typeof MessageChannel){var u=new MessageChannel;u.port1.onmessage=function(){o=c,u.port1.onmessage=t,t()};var c=function(){u.port2.postMessage(0)};o=function(){setTimeout(t,0),c()}}else o=function(){setTimeout(t,0)};return Y.runAfter=function(e){l.push(e),a||(a=!0,o())},Y}(),K=Function.call,W=t(Array.prototype.slice),X=t(Array.prototype.reduce||function(e,t){var n=0,i=this.length;if(1===arguments.length)for(;;){if(n in this){t=this[n++];break}if(++n>=i)throw new TypeError}for(;i>n;n++)n in this&&(t=e(t,this[n],n));return t}),G=t(Array.prototype.indexOf||function(e){for(var t=0;t<this.length;t++)if(this[t]===e)return t;return-1}),Z=t(Array.prototype.map||function(e,t){var n=this,i=[];return X(n,function(r,a,o){i.push(e.call(t,a,o,n))},void 0),i}),ee=Object.create||function(e){function t(){}return t.prototype=e,new t},te=t(Object.prototype.hasOwnProperty),ne=Object.keys||function(e){var t=[];for(var n in e)te(e,n)&&t.push(n);return t},ie=t(Object.prototype.toString);H="undefined"!=typeof ReturnValue?ReturnValue:function(e){this.value=e};var re="From previous event:";p.resolve=p,p.nextTick=Y,p.longStackSupport=!1,"object"==typeof e&&e&&e.env&&e.env.Q_DEBUG&&(p.longStackSupport=!0),p.defer=h,h.prototype.makeNodeResolver=function(){var e=this;return function(t,n){t?e.reject(t):arguments.length>2?e.resolve(W(arguments,1)):e.resolve(n)}},p.Promise=f,p.promise=f,f.race=d,f.all=L,f.reject=S,f.resolve=p,p.passByCopy=function(e){return e},m.prototype.passByCopy=function(){return this},p.join=function(e,t){return p(e).join(t)},m.prototype.join=function(e){return p([this,e]).spread(function(e,t){if(e===t)return e;throw new Error("Can't join: not the same: "+e+" "+t)})},p.race=d,m.prototype.race=function(){return this.then(p.race)},p.makePromise=m,m.prototype.toString=function(){return"[object Promise]"},m.prototype.then=function(e,t,n){function i(t){try{return"function"==typeof e?e(t):t}catch(n){return S(n)}}function a(e){if("function"==typeof t){r(e,s);try{return t(e)}catch(n){return S(n)}}return S(e)}function o(e){return"function"==typeof n?n(e):e}var s=this,l=h(),u=!1;return p.nextTick(function(){s.promiseDispatch(function(e){u||(u=!0,l.resolve(i(e)))},"when",[function(e){u||(u=!0,l.resolve(a(e)))}])}),s.promiseDispatch(void 0,"when",[void 0,function(e){var t,n=!1;try{t=o(e)}catch(i){if(n=!0,!p.onerror)throw i;p.onerror(i)}n||l.notify(t)}]),l.promise},p.tap=function(e,t){return p(e).tap(t)},m.prototype.tap=function(e){return e=p(e),this.then(function(t){return e.fcall(t).thenResolve(t)})},p.when=y,m.prototype.thenResolve=function(e){return this.then(function(){return e})},p.thenResolve=function(e,t){return p(e).thenResolve(t)},m.prototype.thenReject=function(e){return this.then(function(){throw e})},p.thenReject=function(e,t){return p(e).thenReject(t)},p.nearer=g,p.isPromise=v,p.isPromiseAlike=b,p.isPending=w,m.prototype.isPending=function(){return"pending"===this.inspect().state},p.isFulfilled=x,m.prototype.isFulfilled=function(){return"fulfilled"===this.inspect().state},p.isRejected=A,m.prototype.isRejected=function(){return"rejected"===this.inspect().state};var ae=[],oe=[],se=[],le=!0;p.resetUnhandledRejections=j,p.getUnhandledReasons=function(){return ae.slice()},p.stopUnhandledRejectionTracking=function(){j(),le=!1},j(),p.reject=S,p.fulfill=k,p.master=E,p.spread=I,m.prototype.spread=function(e,t){return this.all().then(function(t){return e.apply(void 0,t)},t)},p.async=T,p.spawn=$,p["return"]=M,p.promised=U,p.dispatch=P,m.prototype.dispatch=function(e,t){var n=this,i=h();return p.nextTick(function(){n.promiseDispatch(i.resolve,e,t)}),i.promise},p.get=function(e,t){return p(e).dispatch("get",[t])},m.prototype.get=function(e){return this.dispatch("get",[e])},p.set=function(e,t,n){return p(e).dispatch("set",[t,n])},m.prototype.set=function(e,t){return this.dispatch("set",[e,t])},p.del=p["delete"]=function(e,t){return p(e).dispatch("delete",[t])},m.prototype.del=m.prototype["delete"]=function(e){return this.dispatch("delete",[e])},p.mapply=p.post=function(e,t,n){return p(e).dispatch("post",[t,n])},m.prototype.mapply=m.prototype.post=function(e,t){return this.dispatch("post",[e,t])},p.send=p.mcall=p.invoke=function(e,t){return p(e).dispatch("post",[t,W(arguments,2)])},m.prototype.send=m.prototype.mcall=m.prototype.invoke=function(e){return this.dispatch("post",[e,W(arguments,1)])},p.fapply=function(e,t){return p(e).dispatch("apply",[void 0,t])},m.prototype.fapply=function(e){return this.dispatch("apply",[void 0,e])},p["try"]=p.fcall=function(e){return p(e).dispatch("apply",[void 0,W(arguments,1)])},m.prototype.fcall=function(){return this.dispatch("apply",[void 0,W(arguments)])},p.fbind=function(e){var t=p(e),n=W(arguments,1);return function(){return t.dispatch("apply",[this,n.concat(W(arguments))])}},m.prototype.fbind=function(){var e=this,t=W(arguments);return function(){return e.dispatch("apply",[this,t.concat(W(arguments))])}},p.keys=function(e){return p(e).dispatch("keys",[])},m.prototype.keys=function(){return this.dispatch("keys",[])},p.all=L,m.prototype.all=function(){return L(this)},p.any=D,m.prototype.any=function(){return D(this)},p.allResolved=c(R,"allResolved","allSettled"),m.prototype.allResolved=function(){return R(this)},p.allSettled=N,m.prototype.allSettled=function(){return this.then(function(e){return L(Z(e,function(e){function t(){return e.inspect()}return e=p(e),e.then(t,t)}))})},p.fail=p["catch"]=function(e,t){return p(e).then(void 0,t)},m.prototype.fail=m.prototype["catch"]=function(e){
+return this.then(void 0,e)},p.progress=F,m.prototype.progress=function(e){return this.then(void 0,void 0,e)},p.fin=p["finally"]=function(e,t){return p(e)["finally"](t)},m.prototype.fin=m.prototype["finally"]=function(e){return e=p(e),this.then(function(t){return e.fcall().then(function(){return t})},function(t){return e.fcall().then(function(){throw t})})},p.done=function(e,t,n,i){return p(e).done(t,n,i)},m.prototype.done=function(t,n,i){var a=function(e){p.nextTick(function(){if(r(e,o),!p.onerror)throw e;p.onerror(e)})},o=t||n||i?this.then(t,n,i):this;"object"==typeof e&&e&&e.domain&&(a=e.domain.bind(a)),o.then(void 0,a)},p.timeout=function(e,t,n){return p(e).timeout(t,n)},m.prototype.timeout=function(e,t){var n=h(),i=setTimeout(function(){t&&"string"!=typeof t||(t=new Error(t||"Timed out after "+e+" ms"),t.code="ETIMEDOUT"),n.reject(t)},e);return this.then(function(e){clearTimeout(i),n.resolve(e)},function(e){clearTimeout(i),n.reject(e)},n.notify),n.promise},p.delay=function(e,t){return void 0===t&&(t=e,e=void 0),p(e).delay(t)},m.prototype.delay=function(e){return this.then(function(t){var n=h();return setTimeout(function(){n.resolve(t)},e),n.promise})},p.nfapply=function(e,t){return p(e).nfapply(t)},m.prototype.nfapply=function(e){var t=h(),n=W(e);return n.push(t.makeNodeResolver()),this.fapply(n).fail(t.reject),t.promise},p.nfcall=function(e){var t=W(arguments,1);return p(e).nfapply(t)},m.prototype.nfcall=function(){var e=W(arguments),t=h();return e.push(t.makeNodeResolver()),this.fapply(e).fail(t.reject),t.promise},p.nfbind=p.denodeify=function(e){var t=W(arguments,1);return function(){var n=t.concat(W(arguments)),i=h();return n.push(i.makeNodeResolver()),p(e).fapply(n).fail(i.reject),i.promise}},m.prototype.nfbind=m.prototype.denodeify=function(){var e=W(arguments);return e.unshift(this),p.denodeify.apply(void 0,e)},p.nbind=function(e,t){var n=W(arguments,2);return function(){function i(){return e.apply(t,arguments)}var r=n.concat(W(arguments)),a=h();return r.push(a.makeNodeResolver()),p(i).fapply(r).fail(a.reject),a.promise}},m.prototype.nbind=function(){var e=W(arguments,0);return e.unshift(this),p.nbind.apply(void 0,e)},p.nmapply=p.npost=function(e,t,n){return p(e).npost(t,n)},m.prototype.nmapply=m.prototype.npost=function(e,t){var n=W(t||[]),i=h();return n.push(i.makeNodeResolver()),this.dispatch("post",[e,n]).fail(i.reject),i.promise},p.nsend=p.nmcall=p.ninvoke=function(e,t){var n=W(arguments,2),i=h();return n.push(i.makeNodeResolver()),p(e).dispatch("post",[t,n]).fail(i.reject),i.promise},m.prototype.nsend=m.prototype.nmcall=m.prototype.ninvoke=function(e){var t=W(arguments,1),n=h();return t.push(n.makeNodeResolver()),this.dispatch("post",[e,t]).fail(n.reject),n.promise},p.nodeify=B,m.prototype.nodeify=function(e){return e?void this.then(function(t){p.nextTick(function(){e(null,t)})},function(t){p.nextTick(function(){e(t)})}):this},p.noConflict=function(){throw new Error("Q.noConflict only works when Q is used as a global")};var ue=u();return p})}).call(this,e("_process"))},{_process:12}],158:[function(e,t,n){function i(){}function r(e){var t={}.toString.call(e);switch(t){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function a(e){if(!b(e))return e;var t=[];for(var n in e)null!=e[n]&&o(t,n,e[n]);return t.join("&")}function o(e,t,n){return Array.isArray(n)?n.forEach(function(n){o(e,t,n)}):void e.push(encodeURIComponent(t)+"="+encodeURIComponent(n))}function s(e){for(var t,n,i={},r=e.split("&"),a=0,o=r.length;o>a;++a)n=r[a],t=n.split("="),i[decodeURIComponent(t[0])]=decodeURIComponent(t[1]);return i}function l(e){var t,n,i,r,a=e.split(/\r?\n/),o={};a.pop();for(var s=0,l=a.length;l>s;++s)n=a[s],t=n.indexOf(":"),i=n.slice(0,t).toLowerCase(),r=x(n.slice(t+1)),o[i]=r;return o}function u(e){return/[\/+]json\b/.test(e)}function c(e){return e.split(/ *; */).shift()}function p(e){return g(e.split(/ *; */),function(e,t){var n=t.split(/ *= */),i=n.shift(),r=n.shift();return i&&r&&(e[i]=r),e},{})}function h(e,t){t=t||{},this.req=e,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method&&(""===this.xhr.responseType||"text"===this.xhr.responseType)||"undefined"==typeof this.xhr.responseType?this.xhr.responseText:null,this.statusText=this.req.xhr.statusText,this.setStatusProperties(this.xhr.status),this.header=this.headers=l(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text?this.text:this.xhr.response):null}function f(e,t){var n=this;this._query=this._query||[],this.method=e,this.url=t,this.header={},this._header={},this.on("end",function(){var e=null,t=null;try{t=new h(n)}catch(i){return e=new Error("Parser is unable to parse the response"),e.parse=!0,e.original=i,e.rawResponse=n.xhr&&n.xhr.responseText?n.xhr.responseText:null,e.statusCode=n.xhr&&n.xhr.status?n.xhr.status:null,n.callback(e)}if(n.emit("response",t),e)return n.callback(e,t);if(t.status>=200&&t.status<300)return n.callback(e,t);var r=new Error(t.statusText||"Unsuccessful HTTP response");r.original=e,r.response=t,r.status=t.status,n.callback(r,t)})}function d(e,t){var n=w("DELETE",e);return t&&n.end(t),n}var m,y=e("emitter"),g=e("reduce"),v=e("./request-base"),b=e("./is-object");m="undefined"!=typeof window?window:"undefined"!=typeof self?self:this;var w=t.exports=e("./request").bind(null,f);w.getXHR=function(){if(!(!m.XMLHttpRequest||m.location&&"file:"==m.location.protocol&&m.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}return!1};var x="".trim?function(e){return e.trim()}:function(e){return e.replace(/(^\s*|\s*$)/g,"")};w.serializeObject=a,w.parseString=s,w.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},w.serialize={"application/x-www-form-urlencoded":a,"application/json":JSON.stringify},w.parse={"application/x-www-form-urlencoded":s,"application/json":JSON.parse},h.prototype.get=function(e){return this.header[e.toLowerCase()]},h.prototype.setHeaderProperties=function(e){var t=this.header["content-type"]||"";this.type=c(t);var n=p(t);for(var i in n)this[i]=n[i]},h.prototype.parseBody=function(e){var t=w.parse[this.type];return!t&&u(this.type)&&(t=w.parse["application/json"]),t&&e&&(e.length||e instanceof Object)?t(e):null},h.prototype.setStatusProperties=function(e){1223===e&&(e=204);var t=e/100|0;this.status=this.statusCode=e,this.statusType=t,this.info=1==t,this.ok=2==t,this.clientError=4==t,this.serverError=5==t,this.error=4==t||5==t?this.toError():!1,this.accepted=202==e,this.noContent=204==e,this.badRequest=400==e,this.unauthorized=401==e,this.notAcceptable=406==e,this.notFound=404==e,this.forbidden=403==e},h.prototype.toError=function(){var e=this.req,t=e.method,n=e.url,i="cannot "+t+" "+n+" ("+this.status+")",r=new Error(i);return r.status=this.status,r.method=t,r.url=n,r},w.Response=h,y(f.prototype);for(var A in v)f.prototype[A]=v[A];f.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},f.prototype.type=function(e){return this.set("Content-Type",w.types[e]||e),this},f.prototype.responseType=function(e){return this._responseType=e,this},f.prototype.accept=function(e){return this.set("Accept",w.types[e]||e),this},f.prototype.auth=function(e,t,n){switch(n||(n={type:"basic"}),n.type){case"basic":var i=btoa(e+":"+t);this.set("Authorization","Basic "+i);break;case"auto":this.username=e,this.password=t}return this},f.prototype.query=function(e){return"string"!=typeof e&&(e=a(e)),e&&this._query.push(e),this},f.prototype.attach=function(e,t,n){return this._getFormData().append(e,t,n||t.name),this},f.prototype._getFormData=function(){return this._formData||(this._formData=new m.FormData),this._formData},f.prototype.send=function(e){var t=b(e),n=this._header["content-type"];if(t&&b(this._data))for(var i in e)this._data[i]=e[i];else"string"==typeof e?(n||this.type("form"),n=this._header["content-type"],"application/x-www-form-urlencoded"==n?this._data=this._data?this._data+"&"+e:e:this._data=(this._data||"")+e):this._data=e;return!t||r(e)?this:(n||this.type("json"),this)},h.prototype.parse=function(e){return m.console&&console.warn("Client-side parse() method has been renamed to serialize(). This method is not compatible with superagent v2.0"),this.serialize(e),this},h.prototype.serialize=function(e){return this._parser=e,this},f.prototype.callback=function(e,t){var n=this._callback;this.clearTimeout(),n(e,t)},f.prototype.crossDomainError=function(){var e=new Error("Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.");e.crossDomain=!0,e.status=this.status,e.method=this.method,e.url=this.url,this.callback(e)},f.prototype.timeoutError=function(){var e=this._timeout,t=new Error("timeout of "+e+"ms exceeded");t.timeout=e,this.callback(t)},f.prototype.withCredentials=function(){return this._withCredentials=!0,this},f.prototype.end=function(e){var t=this,n=this.xhr=w.getXHR(),a=this._query.join("&"),o=this._timeout,s=this._formData||this._data;this._callback=e||i,n.onreadystatechange=function(){if(4==n.readyState){var e;try{e=n.status}catch(i){e=0}if(0==e){if(t.timedout)return t.timeoutError();if(t.aborted)return;return t.crossDomainError()}t.emit("end")}};var l=function(e){e.total>0&&(e.percent=e.loaded/e.total*100),e.direction="download",t.emit("progress",e)};this.hasListeners("progress")&&(n.onprogress=l);try{n.upload&&this.hasListeners("progress")&&(n.upload.onprogress=l)}catch(c){}if(o&&!this._timer&&(this._timer=setTimeout(function(){t.timedout=!0,t.abort()},o)),a&&(a=w.serializeObject(a),this.url+=~this.url.indexOf("?")?"&"+a:"?"+a),this.username&&this.password?n.open(this.method,this.url,!0,this.username,this.password):n.open(this.method,this.url,!0),this._withCredentials&&(n.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof s&&!r(s)){var p=this._header["content-type"],h=this._parser||w.serialize[p?p.split(";")[0]:""];!h&&u(p)&&(h=w.serialize["application/json"]),h&&(s=h(s))}for(var f in this.header)null!=this.header[f]&&n.setRequestHeader(f,this.header[f]);return this._responseType&&(n.responseType=this._responseType),this.emit("request",this),n.send("undefined"!=typeof s?s:null),this},w.Request=f,w.get=function(e,t,n){var i=w("GET",e);return"function"==typeof t&&(n=t,t=null),t&&i.query(t),n&&i.end(n),i},w.head=function(e,t,n){var i=w("HEAD",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.del=d,w["delete"]=d,w.patch=function(e,t,n){var i=w("PATCH",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.post=function(e,t,n){var i=w("POST",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i},w.put=function(e,t,n){var i=w("PUT",e);return"function"==typeof t&&(n=t,t=null),t&&i.send(t),n&&i.end(n),i}},{"./is-object":159,"./request":161,"./request-base":160,emitter:162,reduce:163}],159:[function(e,t,n){function i(e){return null!=e&&"object"==typeof e}t.exports=i},{}],160:[function(e,t,n){var i=e("./is-object");n.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},n.parse=function(e){return this._parser=e,this},n.timeout=function(e){return this._timeout=e,this},n.then=function(e,t){return this.end(function(n,i){n?t(n):e(i)})},n.use=function(e){return e(this),this},n.get=function(e){return this._header[e.toLowerCase()]},n.getHeader=n.get,n.set=function(e,t){if(i(e)){for(var n in e)this.set(n,e[n]);return this}return this._header[e.toLowerCase()]=t,this.header[e]=t,this},n.unset=function(e){return delete this._header[e.toLowerCase()],delete this.header[e],this},n.field=function(e,t){return this._getFormData().append(e,t),this}},{"./is-object":159}],161:[function(e,t,n){function i(e,t,n){return"function"==typeof n?new e("GET",t).end(n):2==arguments.length?new e("GET",t):new e(t,n)}t.exports=i},{}],162:[function(e,t,n){function i(e){return e?r(e):void 0}function r(e){for(var t in i.prototype)e[t]=i.prototype[t];return e}"undefined"!=typeof t&&(t.exports=i),i.prototype.on=i.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},i.prototype.once=function(e,t){function n(){this.off(e,n),t.apply(this,arguments)}return n.fn=t,this.on(e,n),this},i.prototype.off=i.prototype.removeListener=i.prototype.removeAllListeners=i.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+e];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var i,r=0;r<n.length;r++)if(i=n[r],i===t||i.fn===t){n.splice(r,1);break}return this},i.prototype.emit=function(e){this._callbacks=this._callbacks||{};var t=[].slice.call(arguments,1),n=this._callbacks["$"+e];if(n){n=n.slice(0);for(var i=0,r=n.length;r>i;++i)n[i].apply(this,t)}return this},i.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},i.prototype.hasListeners=function(e){return!!this.listeners(e).length}},{}],163:[function(e,t,n){t.exports=function(e,t,n){for(var i=0,r=e.length,a=3==arguments.length?n:e[i++];r>i;)a=t.call(null,a,e[i],++i,e);return a}},{}]},{},[1])(1)}),window.SwaggerUi=Backbone.Router.extend({dom_id:"swagger_ui",options:null,api:null,headerView:null,mainView:null,initialize:function(e){e=e||{},"model"!==e.defaultModelRendering&&(e.defaultModelRendering="schema"),e.highlightSizeThreshold||(e.highlightSizeThreshold=1e5),e.dom_id&&(this.dom_id=e.dom_id,delete e.dom_id),e.supportedSubmitMethods||(e.supportedSubmitMethods=["get","put","post","delete","head","options","patch"]),"string"==typeof e.oauth2RedirectUrl&&(window.oAuthRedirectUrl=e.redirectUrl),$("#"+this.dom_id).length||$("body").append('<div id="'+this.dom_id+'"></div>'),this.options=e,marked.setOptions({gfm:!0});var t=this;this.options.success=function(){return t.render()},this.options.progress=function(e){return t.showMessage(e)},this.options.failure=function(e){return t.onLoadFailure(e)},this.headerView=new SwaggerUi.Views.HeaderView({el:$("#header")}),this.headerView.on("update-swagger-ui",function(e){return t.updateSwaggerUi(e)}),JSONEditor.defaults.iconlibs.swagger=JSONEditor.AbstractIconLib.extend({mapping:{collapse:"collapse",expand:"expand"},icon_prefix:"swagger-"})},setOption:function(e,t){this.options[e]=t},getOption:function(e){return this.options[e]},updateSwaggerUi:function(e){this.options.url=e.url,this.load()},load:function(){this.mainView&&this.mainView.clear(),this.authView&&this.authView.remove();var e=this.options.url;e&&0!==e.indexOf("http")&&(e=this.buildUrl(window.location.href.toString(),e)),this.api&&(this.options.authorizations=this.api.clientAuthorizations.authz),this.options.url=e,this.headerView.update(e),this.api=new SwaggerClient(this.options)},collapseAll:function(){Docs.collapseEndpointListForResource("")},listAll:function(){Docs.collapseOperationsForResource("")},expandAll:function(){Docs.expandOperationsForResource("")},render:function(){var e;switch(this.showMessage("Finished Loading Resource Information. Rendering Swagger UI..."),this.mainView=new SwaggerUi.Views.MainView({model:this.api,el:$("#"+this.dom_id),swaggerOptions:this.options,router:this}).render(),_.isEmpty(this.api.securityDefinitions)||(e=_.map(this.api.securityDefinitions,function(e,t){var n={};return n[t]=e,n}),this.authView=new SwaggerUi.Views.AuthButtonView({data:SwaggerUi.utils.parseSecurityDefinitions(e),router:this}),$("#auth_container").append(this.authView.render().el)),this.showMessage(),this.options.docExpansion){case"full":this.expandAll();break;case"list":this.listAll()}this.renderGFM(),this.options.onComplete&&this.options.onComplete(this.api,this),setTimeout(Docs.shebang.bind(this),100)},buildUrl:function(e,t){if(0===t.indexOf("/")){var n=e.split("/");return e=n[0]+"//"+n[2],e+t}var i=e.length;return e.indexOf("?")>-1&&(i=Math.min(i,e.indexOf("?"))),e.indexOf("#")>-1&&(i=Math.min(i,e.indexOf("#"))),e=e.substring(0,i),-1!==e.indexOf("/",e.length-1)?e+t:e+"/"+t},showMessage:function(e){void 0===e&&(e="");var t=$("#message-bar");t.removeClass("message-fail"),t.addClass("message-success"),t.text(e),window.SwaggerTranslator&&window.SwaggerTranslator.translate(t)},onLoadFailure:function(e){void 0===e&&(e=""),$("#message-bar").removeClass("message-success"),$("#message-bar").addClass("message-fail");var t=$("#message-bar").text(e);return this.options.onFailure&&this.options.onFailure(e),t},renderGFM:function(){$(".markdown").each(function(){$(this).html(marked($(this).html()))}),$(".propDesc",".model-signature .description").each(function(){$(this).html(marked($(this).html())).addClass("markdown")})}}),window.SwaggerUi.Views={},window.SwaggerUi.Models={},window.SwaggerUi.Collections={},window.SwaggerUi.partials={},window.SwaggerUi.utils={},function(){function e(e){"console"in window&&"function"==typeof window.console.warn&&console.warn(e)}window.authorizations={add:function(){if(e("Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add()."),"undefined"==typeof window.swaggerUi)throw new TypeError("window.swaggerUi is not defined");window.swaggerUi instanceof SwaggerUi&&window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations,arguments)}},window.ApiKeyAuthorization=function(){e("window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization."),SwaggerClient.ApiKeyAuthorization.apply(window,arguments)},window.PasswordAuthorization=function(){e("window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization."),SwaggerClient.PasswordAuthorization.apply(window,arguments)}}(),function(e,t){"function"==typeof define&&define.amd?define(["b"],function(n){return e.SwaggerUi=t(n)}):"object"==typeof exports?module.exports=t(require("b")):e.SwaggerUi=t(e.b)}(this,function(){return SwaggerUi}),window.SwaggerUi.utils={parseSecurityDefinitions:function(e){var t=Object.assign({},window.swaggerUi.api.authSchemes||window.swaggerUi.api.securityDefinitions),n=[],i=[],r=[],a=window.SwaggerUi.utils;return Array.isArray(e)?(e.forEach(function(e){var o={},s={};for(var l in e)if(Array.isArray(e[l])){if(!t[l])continue;if(t[l]=t[l]||{},"oauth2"===t[l].type){s[l]=Object.assign({},t[l]),s[l].scopes=Object.assign({},t[l].scopes);for(var u in s[l].scopes)e[l].indexOf(u)<0&&delete s[l].scopes[u];s[l].scopes=a.parseOauth2Scopes(s[l].scopes),r=_.merge(r,s[l].scopes)}else o[l]=Object.assign({},t[l])}else"oauth2"===e[l].type?(s[l]=Object.assign({},e[l]),s[l].scopes=a.parseOauth2Scopes(s[l].scopes),r=_.merge(r,s[l].scopes)):o[l]=e[l];_.isEmpty(o)||i.push(o),_.isEmpty(s)||n.push(s)}),{auths:i,oauth2:n,scopes:r}):null},parseOauth2Scopes:function(e){var t,n=Object.assign({},e),i=[];for(t in n)i.push({scope:t,description:n[t]});return i}},SwaggerUi.Models.ApiKeyAuthModel=Backbone.Model.extend({defaults:{"in":"",name:"",title:"",value:""},initialize:function(){this.on("change",this.validate)},validate:function(){var e=!!this.get("value");return this.set("valid",e),e}}),SwaggerUi.Views.ApiKeyAuthView=Backbone.View.extend({events:{"change .input_apiKey_entry":"apiKeyChange"},selectors:{apikeyInput:".input_apiKey_entry"},template:Handlebars.templates.apikey_auth,initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){return this.$el.html(this.template(this.model.toJSON())),this},apiKeyChange:function(e){var t=$(e.target).val();t&&this.$(this.selectors.apikeyInput).removeClass("error"),this.model.set("value",t)},isValid:function(){return this.model.validate()},highlightInvalid:function(){this.isValid()||this.$(this.selectors.apikeyInput).addClass("error")}}),SwaggerUi.Views.AuthButtonView=Backbone.View.extend({events:{"click .authorize__btn":"authorizeBtnClick"},tpls:{popup:Handlebars.templates.popup,authBtn:Handlebars.templates.auth_button,authBtnOperation:Handlebars.templates.auth_button_operation},initialize:function(e){this.options=e||{},this.options.data=this.options.data||{},this.isOperation=this.options.isOperation,this.model=this.model||{},this.router=this.options.router,this.auths=this.options.data.oauth2.concat(this.options.data.auths)},render:function(){var e=this.isOperation?"authBtnOperation":"authBtn";return this.$authEl=this.renderAuths(this.auths),this.$el.html(this.tpls[e](this.model)),this},authorizeBtnClick:function(e){var t;e.preventDefault(),t={title:"Available authorizations",content:this.$authEl},this.render(),this.popup=new SwaggerUi.Views.PopupView({model:t}),this.popup.render()},renderAuths:function(e){var t=$("<div>"),n=!1;return e.forEach(function(e){var i=new SwaggerUi.Views.AuthView({data:e,router:this.router}),r=i.render().el;t.append(r),i.isLogout&&(n=!0)},this),this.model.isLogout=n,t}}),SwaggerUi.Collections.AuthsCollection=Backbone.Collection.extend({constructor:function(){var e=Array.prototype.slice.call(arguments);e[0]=this.parse(e[0]),Backbone.Collection.apply(this,e)},add:function(e){var t=Array.prototype.slice.call(arguments);Array.isArray(e)?t[0]=_.map(e,function(e){return this.handleOne(e)},this):t[0]=this.handleOne(e),Backbone.Collection.prototype.add.apply(this,t)},handleOne:function(e){var t=e;if(!(e instanceof Backbone.Model))switch(e.type){case"oauth2":t=new SwaggerUi.Models.Oauth2Model(e);break;case"basic":t=new SwaggerUi.Models.BasicAuthModel(e);break;case"apiKey":t=new SwaggerUi.Models.ApiKeyAuthModel(e);break;default:t=new Backbone.Model(e)}return t},isValid:function(){var e=!0;return this.models.forEach(function(t){t.validate()||(e=!1)}),e},isAuthorized:function(){return this.length===this.where({isLogout:!0}).length},isPartiallyAuthorized:function(){return this.where({isLogout:!0}).length>0},parse:function(e){var t=Object.assign({},window.swaggerUi.api.clientAuthorizations.authz);return _.map(e,function(e,n){var i=t[n]&&"basic"===e.type&&t[n].username&&t[n].password;return _.extend(e,{title:n}),(t[n]||i)&&_.extend(e,{isLogout:!0,value:i?void 0:t[n].value,username:i?t[n].username:void 0,password:i?t[n].password:void 0,valid:!0}),e})}}),SwaggerUi.Views.AuthsCollectionView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.options.data=this.options.data||{},this.router=this.options.router,this.collection=new SwaggerUi.Collections.AuthsCollection(e.data),this.$innerEl=$("<div>"),this.authViews=[]},render:function(){return this.collection.each(function(e){this.renderOneAuth(e)},this),this.$el.html(this.$innerEl.html()?this.$innerEl:""),this},renderOneAuth:function(e){var t,n,i,r=e.get("type");"apiKey"===r?i="ApiKeyAuthView":"basic"===r&&0===this.$innerEl.find(".basic_auth_container").length?i="BasicAuthView":"oauth2"===r&&(i="Oauth2View"),i&&(n=new SwaggerUi.Views[i]({model:e,router:this.router}),t=n.render().el,this.authViews.push(n)),this.$innerEl.append(t)},highlightInvalid:function(){this.authViews.forEach(function(e){e.highlightInvalid()},this)}}),SwaggerUi.Views.AuthView=Backbone.View.extend({events:{"click .auth_submit__button":"authorizeClick","click .auth_logout__button":"logoutClick"},tpls:{main:Handlebars.templates.auth_view},selectors:{innerEl:".auth_inner",authBtn:".auth_submit__button"},initialize:function(e){this.options=e||{},e.data=e.data||{},this.router=this.options.router,this.authsCollectionView=new SwaggerUi.Views.AuthsCollectionView({data:e.data}),this.$el.html(this.tpls.main({isLogout:this.authsCollectionView.collection.isAuthorized(),isAuthorized:this.authsCollectionView.collection.isPartiallyAuthorized()})),this.$innerEl=this.$(this.selectors.innerEl),this.isLogout=this.authsCollectionView.collection.isPartiallyAuthorized()},render:function(){return this.$innerEl.html(this.authsCollectionView.render().el),this},authorizeClick:function(e){e.preventDefault(),e.stopPropagation(),this.authsCollectionView.collection.isValid()?this.authorize():this.authsCollectionView.highlightInvalid()},authorize:function(){this.authsCollectionView.collection.forEach(function(e){var t,n,i=e.get("type");"apiKey"===i?(t=new SwaggerClient.ApiKeyAuthorization(e.get("name"),e.get("value"),e.get("in")),this.router.api.clientAuthorizations.add(e.get("title"),t)):"basic"===i?(n=new SwaggerClient.PasswordAuthorization(e.get("username"),e.get("password")),this.router.api.clientAuthorizations.add(e.get("title"),n)):"oauth2"===i&&this.handleOauth2Login(e)},this),this.router.load()},logoutClick:function(e){e.preventDefault(),this.authsCollectionView.collection.forEach(function(e){window.swaggerUi.api.clientAuthorizations.remove(e.get("title"))}),this.router.load()},handleOauth2Login:function(e){var t,n,i,r=window.location,a=location.pathname.substring(0,location.pathname.lastIndexOf("/")),o=r.protocol+"//"+r.host+a+"/o2c.html",s=window.oAuthRedirectUrl||o,l=null,u=_.map(e.get("scopes"),function(e){return e.scope});window.OAuthSchemeKey=e.get("title"),window.enabledScopes=u;var c=e.get("flow");if("oauth2"!==e.get("type")||!c||"implicit"!==c&&"accessCode"!==c){if("oauth2"===e.get("type")&&c&&"application"===c)return n=e.attributes,window.swaggerUi.tokenName=n.tokenName||"access_token",void this.clientCredentialsFlow(u,n.tokenUrl,window.OAuthSchemeKey);if(e.get("grantTypes")){var p=e.get("grantTypes");for(var h in p)p.hasOwnProperty(h)&&"implicit"===h?(n=p[h],i=n.loginEndpoint.url,l=n.loginEndpoint.url+"?response_type=token",window.swaggerUi.tokenName=n.tokenName):p.hasOwnProperty(h)&&"accessCode"===h&&(n=p[h],i=n.tokenRequestEndpoint.url,l=n.tokenRequestEndpoint.url+"?response_type=code",window.swaggerUi.tokenName=n.tokenName)}}else n=e.attributes,l=n.authorizationUrl+"?response_type="+("implicit"===c?"token":"code"),window.swaggerUi.tokenName=n.tokenName||"access_token",window.swaggerUi.tokenUrl="accessCode"===c?n.tokenUrl:null,t=window.OAuthSchemeKey;l+="&redirect_uri="+encodeURIComponent(s),l+="&realm="+encodeURIComponent(realm),l+="&client_id="+encodeURIComponent(clientId),l+="&scope="+encodeURIComponent(u.join(scopeSeparator)),l+="&state="+encodeURIComponent(t);for(var f in additionalQueryStringParams)l+="&"+f+"="+encodeURIComponent(additionalQueryStringParams[f]);window.open(l)},clientCredentialsFlow:function(e,t,n){var i={client_id:clientId,client_secret:clientSecret,scope:e.join(" "),grant_type:"client_credentials"};$.ajax({url:t,type:"POST",data:i,success:function(e){onOAuthComplete(e,n)},error:function(){onOAuthComplete("")}})}}),SwaggerUi.Models.BasicAuthModel=Backbone.Model.extend({defaults:{username:"",password:"",title:"basic"},initialize:function(){this.on("change",this.validate)},validate:function(){var e=!!this.get("password")&&!!this.get("username");return this.set("valid",e),e}}),SwaggerUi.Views.BasicAuthView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},events:{"change .auth_input":"inputChange"},selectors:{usernameInput:".basic_auth__username",passwordInput:".basic_auth__password"},cls:{error:"error"},template:Handlebars.templates.basic_auth,render:function(){return $(this.el).html(this.template(this.model.toJSON())),this},inputChange:function(e){var t=$(e.target),n=t.val(),i=t.prop("name");n&&t.removeClass(this.cls.error),this.model.set(i,n)},isValid:function(){return this.model.validate()},highlightInvalid:function(){this.model.get("username")||this.$(this.selectors.usernameInput).addClass(this.cls.error)}}),SwaggerUi.Views.ContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.contentTypeId="ct"+Math.random(),$(this.el).html(Handlebars.templates.content_type(this.model)),this}}),SwaggerUi.Views.HeaderView=Backbone.View.extend({events:{"click #show-pet-store-icon":"showPetStore","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"},initialize:function(){},showPetStore:function(){this.trigger("update-swagger-ui",{url:"http://petstore.swagger.io/v2/swagger.json"})},showCustomOnKeyup:function(e){13===e.keyCode&&this.showCustom()},showCustom:function(e){e&&e.preventDefault(),this.trigger("update-swagger-ui",{url:$("#input_baseUrl").val()})},update:function(e,t,n){void 0===n&&(n=!1),$("#input_baseUrl").val(e),n&&this.trigger("update-swagger-ui",{url:e})}}),SwaggerUi.Views.MainView=Backbone.View.extend({apisSorter:{alpha:function(e,t){return e.name.localeCompare(t.name)}},operationsSorters:{alpha:function(e,t){return e.path.localeCompare(t.path)},method:function(e,t){return e.method.localeCompare(t.method)}},initialize:function(e){var t,n,i,r;if(e=e||{},this.router=e.router,e.swaggerOptions.apisSorter&&(t=e.swaggerOptions.apisSorter,n=_.isFunction(t)?t:this.apisSorter[t],_.isFunction(n)&&this.model.apisArray.sort(n)),e.swaggerOptions.operationsSorter&&(t=e.swaggerOptions.operationsSorter,n=_.isFunction(t)?t:this.operationsSorters[t],_.isFunction(n)))for(i in this.model.apisArray)this.model.apisArray[i].operationsArray.sort(n);this.model.auths=[];for(i in this.model.securityDefinitions)r=this.model.securityDefinitions[i],this.model.auths.push({name:i,type:r.type,value:r});"validatorUrl"in e.swaggerOptions?this.model.validatorUrl=e.swaggerOptions.validatorUrl:this.model.url.indexOf("localhost")>0||this.model.url.indexOf("127.0.0.1")>0?this.model.validatorUrl=null:"https:"===window.location.protocol?this.model.validatorUrl="https://online.swagger.io/validator":this.model.validatorUrl="http://online.swagger.io/validator";var a;for(a in this.model.definitions)this.model.definitions[a].type||(this.model.definitions[a].type="object")},render:function(){$(this.el).html(Handlebars.templates.main(this.model)),this.info=this.$(".info")[0],this.info&&this.info.addEventListener("click",this.onLinkClick,!0),this.model.securityDefinitions=this.model.securityDefinitions||{};for(var e={},t=0,n=0;n<this.model.apisArray.length;n++){for(var i=this.model.apisArray[n],r=i.name;"undefined"!=typeof e[r];)r=r+"_"+t,t+=1;i.id=r,e[r]=i,this.addResource(i,this.model.auths)}return $(".propWrap").hover(function(){$(".optionsWrapper",$(this)).show()},function(){$(".optionsWrapper",$(this)).hide()}),this},addResource:function(e,t){e.id=e.id.replace(/\s/g,"_"),e.definitions=this.model.definitions;var n=new SwaggerUi.Views.ResourceView({model:e,router:this.router,tagName:"li",id:"resource_"+e.id,className:"resource",auths:t,swaggerOptions:this.options.swaggerOptions});$("#resources",this.el).append(n.render().el)},clear:function(){$(this.el).html("")},onLinkClick:function(e){var t=e.target;"A"===t.tagName&&t.href&&!t.target&&(e.preventDefault(),window.open(t.href,"_blank"))}}),SwaggerUi.Models.Oauth2Model=Backbone.Model.extend({defaults:{scopes:{}},initialize:function(){this.on("change",this.validate)},setScopes:function(e,t){var n=_.extend({},this.attributes),i=_.findIndex(n.scopes,function(t){return t.scope===e});n.scopes[i].checked=t,this.set(n),this.validate()},validate:function(){var e=!1,t=this.get("scopes"),n=_.findIndex(t,function(e){return e.checked===!0});return t.length>0&&n>=0&&(e=!0),0===t.length&&(e=!0),this.set("valid",e),e}}),SwaggerUi.Views.Oauth2View=Backbone.View.extend({events:{"change .oauth-scope":"scopeChange"},template:Handlebars.templates.oauth2,render:function(){return this.$el.html(this.template(this.model.toJSON())),this},scopeChange:function(e){var t=$(e.target).prop("checked"),n=$(e.target).data("scope");this.model.setScopes(n,t)}}),SwaggerUi.Views.OperationView=Backbone.View.extend({invocationUrl:null,events:{"submit .sandbox":"submitOperation",
+"click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent","mouseenter .api-ic":"mouseEnter","dblclick .curl":"selectText","change [name=responseContentType]":"showSnippet"},initialize:function(e){return e=e||{},this.router=e.router,this.auths=e.auths,this.parentId=this.model.parentId,this.nickname=this.model.nickname,this.model.encodedParentId=encodeURIComponent(this.parentId),e.swaggerOptions&&(this.model.defaultRendering=e.swaggerOptions.defaultModelRendering,e.swaggerOptions.showRequestHeaders&&(this.model.showRequestHeaders=!0)),this},selectText:function(e){var t,n,i=document,r=e.target.firstChild;i.body.createTextRange?(t=document.body.createTextRange(),t.moveToElementText(r),t.select()):window.getSelection&&(n=window.getSelection(),t=document.createRange(),t.selectNodeContents(r),n.removeAllRanges(),n.addRange(t))},mouseEnter:function(e){var t=$(this.el).find(".content"),n=e.pageX,i=e.pageY,r=$(window).scrollLeft(),a=$(window).scrollTop(),o=r+$(window).width(),s=a+$(window).height(),l=t.width(),u=t.height();n+l>o&&(n=o-l),r>n&&(n=r),i+u>s&&(i=s-u),a>i&&(i=a);var c={};c.top=i,c.left=n,t.css(c)},render:function(){var e,t,n,i,r,a,o,s,l,u,c,p,h,f,d,m,y,g,v,b,w,x,A,j,O,S,k,C,E,I,T,M,U,P,L,D,R,N,F,B,q;if(a=jQuery.inArray(this.model.method,this.model.supportedSubmitMethods())>=0,a||(this.model.isReadOnly=!0),this.model.description=this.model.description||this.model.notes,this.model.oauth=null,m=this.model.authorizations||this.model.security)if(Array.isArray(m))for(l=0,u=m.length;u>l;l++){n=m[l];for(s in n)for(e in this.auths)if(t=this.auths[e],s===t.name&&"oauth2"===t.type){this.model.oauth={},this.model.oauth.scopes=[],A=t.value.scopes;for(o in A)R=A[o],M=n[s].indexOf(o),M>=0&&(g={scope:o,description:R},this.model.oauth.scopes.push(g))}}else for(o in m)if(R=m[o],"oauth2"===o)for(null===this.model.oauth&&(this.model.oauth={}),void 0===this.model.oauth.scopes&&(this.model.oauth.scopes=[]),d=0,c=R.length;c>d;d++)g=R[d],this.model.oauth.scopes.push(g);if("undefined"!=typeof this.model.responses){this.model.responseMessages=[],j=this.model.responses;for(i in j)N=j[i],I=null,T=this.model.responses[i].schema,T&&T.$ref&&(I=T.$ref,-1!==I.indexOf("#/definitions/")&&(I=I.replace(/^.*#\/definitions\//,""))),this.model.responseMessages.push({code:i,message:N.description,responseModel:I,headers:N.headers,schema:T})}if("undefined"==typeof this.model.responseMessages&&(this.model.responseMessages=[]),U=null,F=this.model.produces,B=this.contains(F,"xml"),q=B?this.contains(F,"json"):!0,this.model.successResponse){L=this.model.successResponse;for(s in L)N=L[s],this.model.successCode=s,"object"==typeof N&&"function"==typeof N.createJSONSample?(this.model.successDescription=N.description,this.model.headers=this.parseResponseHeaders(N.headers),U={sampleJSON:q?JSON.stringify(SwaggerUi.partials.signature.createJSONSample(N),void 0,2):!1,isParam:!1,sampleXML:B?SwaggerUi.partials.signature.createXMLSample(N.name,N.definition,N.models):!1,signature:SwaggerUi.partials.signature.getModelSignature(N.name,N.definition,N.models,N.modelPropertyMacro)}):U={signature:SwaggerUi.partials.signature.getPrimitiveSignature(N)}}else this.model.responseClassSignature&&"string"!==this.model.responseClassSignature&&(U={sampleJSON:this.model.responseSampleJSON,isParam:!1,signature:this.model.responseClassSignature});for($(this.el).html(Handlebars.templates.operation(this.model)),U?(U.defaultRendering=this.model.defaultRendering,E=new SwaggerUi.Views.SignatureView({model:U,router:this.router,tagName:"div"}),$(".model-signature",$(this.el)).append(E.render().el)):(this.model.responseClassSignature="string",$(".model-signature",$(this.el)).html(this.model.type)),r={isParam:!1},r.consumes=this.model.consumes,r.produces=this.model.produces,O=this.model.parameters,y=0,p=O.length;p>y;y++)b=O[y],D=b.type||b.dataType||"","undefined"==typeof D&&(I=b.schema,I&&I.$ref&&(x=I.$ref,D=0===x.indexOf("#/definitions/")?x.substring("#/definitions/".length):x)),D&&"file"===D.toLowerCase()&&(r.consumes||(r.consumes="multipart/form-data")),b.type=D;for(C=new SwaggerUi.Views.ResponseContentTypeView({model:r,router:this.router}),$(".response-content-type",$(this.el)).append(C.render().el),S=this.model.parameters,v=0,h=S.length;h>v;v++)b=S[v],this.addParameter(b,r.consumes);for(k=this.model.responseMessages,w=0,f=k.length;f>w;w++)P=k[w],P.isXML=B,P.isJSON=q,_.isUndefined(P.headers)||(P.headers=this.parseHeadersType(P.headers)),this.addStatusCode(P);if(Array.isArray(this.model.security)){var V=SwaggerUi.utils.parseSecurityDefinitions(this.model.security);V.isLogout=!_.isEmpty(window.swaggerUi.api.clientAuthorizations.authz),this.authView=new SwaggerUi.Views.AuthButtonView({data:V,router:this.router,isOperation:!0,model:{scopes:V.scopes}}),this.$(".authorize-wrapper").append(this.authView.render().el)}return this.showSnippet(),this},parseHeadersType:function(e){var t={string:{"date-time":"dateTime",date:"date"}};return _.forEach(e,function(e){var n;e=e||{},n=t[e.type]&&t[e.type][e.format],_.isUndefined(n)||(e.type=n)}),e},contains:function(e,t){return e.filter(function(e){return e.indexOf(t)>-1?!0:void 0}).length},parseResponseHeaders:function(e){var t="; ",n=_.clone(e);return _.forEach(n,function(e){var n=[];_.forEach(e,function(e,t){var i=["type","description"];-1===i.indexOf(t.toLowerCase())&&n.push(t+": "+e)}),n.join(t),e.other=n}),n},addParameter:function(e,t){e.consumes=t,e.defaultRendering=this.model.defaultRendering,e.schema&&($.extend(!0,e.schema,this.model.definitions[e.type]),e.schema.definitions=this.model.definitions,e.schema.type||(e.schema.type="object"),e.schema.title||(e.schema.title=" "));var n=new SwaggerUi.Views.ParameterView({model:e,tagName:"tr",readOnly:this.model.isReadOnly,swaggerOptions:this.options.swaggerOptions});$(".operation-params",$(this.el)).append(n.render().el)},addStatusCode:function(e){e.defaultRendering=this.model.defaultRendering;var t=new SwaggerUi.Views.StatusCodeView({model:e,tagName:"tr",router:this.router});$(".operation-status",$(this.el)).append(t.render().el)},submitOperation:function(e){var t,n,i,r,a;if(null!==e&&e.preventDefault(),n=$(".sandbox",$(this.el)),t=!0,n.find("input.required").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){$(e).focus()}}(this)}),t=!1)}),n.find("textarea.required:visible").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){return $(e).focus()}}(this)}),t=!1)}),n.find("select.required").each(function(){$(this).removeClass("error"),-1===this.selectedIndex&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){$(e).focus()}}(this)}),t=!1)}),t){if(r=this.getInputMap(n),i=this.isFileUpload(n),a={parent:this},this.options.swaggerOptions)for(var o in this.options.swaggerOptions)a[o]=this.options.swaggerOptions[o];var s;for(s=0;s<this.model.parameters.length;s++){var l=this.model.parameters[s];if(l.jsonEditor&&l.jsonEditor.isEnabled()){var u=l.jsonEditor.getValue();r[l.name]=JSON.stringify(u)}}return a.responseContentType=$("div select[name=responseContentType]",$(this.el)).val(),a.requestContentType=$("div select[name=parameterContentType]",$(this.el)).val(),$(".response_throbber",$(this.el)).show(),i?($(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(this.invocationUrl),a.useJQuery=!0,r.parameterContentType="multipart/form-data",this.map=r,this.model.execute(r,a,this.showCompleteStatus,this.showErrorStatus,this)):(this.map=r,this.model.execute(r,a,this.showCompleteStatus,this.showErrorStatus,this))}},getInputMap:function(e){var t,n,i,r,a,o,s,l,u,c,p,h;for(t={},n=e.find("input"),i=0,r=n.length;r>i;i++)a=n[i],null!==a.value&&jQuery.trim(a.value).length>0&&(t[a.name]=a.value),"file"===a.type&&(t[a.name]=a.files[0]);for(o=e.find("textarea"),s=0,l=o.length;l>s;s++)a=o[s],u=this.getTextAreaValue(a),null!==u&&jQuery.trim(u).length>0&&(t[a.name]=u);for(c=e.find("select"),p=0,h=c.length;h>p;p++)a=c[p],u=this.getSelectedValue(a),null!==u&&jQuery.trim(u).length>0&&(t[a.name]=u);return t},isFileUpload:function(e){var t,n,i,r,a=!1;for(t=e.find("input"),n=0,i=t.length;i>n;n++)r=t[n],"file"===r.type&&(a=!0);return a},success:function(e,t){t.showCompleteStatus(e)},wrap:function(e){var t,n,i,r,a,o,s;for(i={},n=e.getAllResponseHeaders().split("\r"),a=0,o=n.length;o>a;a++)r=n[a],t=r.match(/^([^:]*?):(.*)$/),t||(t=[]),t.shift(),void 0!==t[0]&&void 0!==t[1]&&(i[t[0].trim()]=t[1].trim());return s={},s.content={},s.content.data=e.responseText,s.headers=i,s.request={},s.request.url=this.invocationUrl,s.status=e.status,s},getSelectedValue:function(e){if(e.multiple){for(var t=[],n=0,i=e.options.length;i>n;n++){var r=e.options[n];r.selected&&t.push(r.value)}return t.length>0?t:null}return e.value},hideResponse:function(e){e&&e.preventDefault(),$(".response",$(this.el)).slideUp(),$(".response_hider",$(this.el)).fadeOut()},showResponse:function(e){var t=JSON.stringify(e,null,"	").replace(/\n/g,"<br>");$(".response_body",$(this.el)).html(_.escape(t))},showErrorStatus:function(e,t){t.showStatus(e)},showCompleteStatus:function(e,t){t.showStatus(e)},formatXml:function(e){var t,n,i,r,a,o,s,l,u,c,p,h,f;for(p=/(>)(<)(\/*)/g,f=/[ ]*(.*)[ ]+\n/g,t=/(<.+>)(.+\n)/g,e=e.replace(/\r\n/g,"\n").replace(p,"$1\n$2$3").replace(f,"$1\n").replace(t,"$1\n$2"),c=0,i="",l=e.split("\n"),r=0,o="other",h={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0},n=function(e){var t,n,a,s,l,u,c;u={single:Boolean(e.match(/<.+\/>/)),closing:Boolean(e.match(/<\/.+>/)),opening:Boolean(e.match(/<[^!?].*>/))},l=function(){var e;e=[];for(a in u)c=u[a],c&&e.push(a);return e}()[0],l=void 0===l?"other":l,t=o+"->"+l,o=l,s="",r+=h[t],s=function(){var e,t,i;for(i=[],n=e=0,t=r;t>=0?t>e:e>t;n=t>=0?++e:--e)i.push("  ");return i}().join(""),"opening->closing"===t?i=i.substr(0,i.length-1)+e+"\n":i+=s+e+"\n"},a=0,s=l.length;s>a;a++)u=l[a],n(u);return i},showStatus:function(e){var t,n;void 0===e.content?(n=e.data,t=e.url):(n=e.content.data,t=e.request.url);var i=e.headers;n=jQuery.trim(n);var r=null;i&&(r=i["Content-Type"]||i["content-type"],r&&(r=r.split(";")[0].trim())),$(".response_body",$(this.el)).removeClass("json"),$(".response_body",$(this.el)).removeClass("xml");var a,o,s=function(e){var t=document.createElement("audio");return!(!t.canPlayType||!t.canPlayType(e).replace(/no/,""))};if(n)if("application/json"===r||/\+json$/.test(r)){var l=null;try{l=JSON.stringify(JSON.parse(n),null,"  ")}catch(u){l="can't parse JSON.  Raw result:\n\n"+n}o=$("<code />").text(l),a=$('<pre class="json" />').append(o)}else if("application/xml"===r||/\+xml$/.test(r))o=$("<code />").text(this.formatXml(n)),a=$('<pre class="xml" />').append(o);else if("text/html"===r)o=$("<code />").html(_.escape(n)),a=$('<pre class="xml" />').append(o);else if(/text\/plain/.test(r))o=$("<code />").text(n),a=$('<pre class="plain" />').append(o);else if(/^image\//.test(r))a=$("<img>").attr("src",t);else if(/^audio\//.test(r)&&s(r))a=$("<audio controls>").append($("<source>").attr("src",t).attr("type",r));else if(i["Content-Disposition"]&&/attachment/.test(i["Content-Disposition"])||i["content-disposition"]&&/attachment/.test(i["content-disposition"])||i["Content-Description"]&&/File Transfer/.test(i["Content-Description"])||i["content-description"]&&/File Transfer/.test(i["content-description"]))if("Blob"in window){var c=r||"text/html",p=new Blob([n],{type:c}),h=document.createElement("a"),f=window.URL.createObjectURL(p),d=e.url.substr(e.url.lastIndexOf("/")+1),m=[c,d,f].join(":"),y=i["content-disposition"]||i["Content-Disposition"];if("undefined"!=typeof y){var g=/filename=([^;]*);?/.exec(y);null!==g&&g.length>1&&(m=g[1])}h.setAttribute("href",f),h.setAttribute("download",m),h.innerText="Download "+d,a=$("<div/>").append(h)}else a=$('<pre class="json" />').append("Download headers detected but your browser does not support downloading binary via XHR (Blob).");else i.location||i.Location?window.location=e.url:(o=$("<code />").text(n),a=$('<pre class="json" />').append(o));else o=$("<code />").text("no content"),a=$('<pre class="json" />').append(o);var v=a;$(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(t),$(".response_code",$(this.el)).html("<pre>"+e.status+"</pre>"),$(".response_body",$(this.el)).html(v),$(".response_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(e.headers,null,"  ")).replace(/\n/g,"<br>")+"</pre>"),$(".response",$(this.el)).slideDown(),$(".response_hider",$(this.el)).show(),$(".response_throbber",$(this.el)).hide();var b=this.model.asCurl(this.map,{responseContentType:r});b=b.replace("!","&#33;"),$("div.curl",$(this.el)).html("<pre>"+_.escape(b)+"</pre>");var w=this.options.swaggerOptions;if(w.showRequestHeaders){var x=$(".sandbox",$(this.el)),A=this.getInputMap(x),j=this.model.getHeaderParams(A);delete j["Content-Type"],$(".request_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(j,null,"  ")).replace(/\n/g,"<br>")+"</pre>")}var O=$(".response_body",$(this.el))[0];return w.highlightSizeThreshold&&"undefined"!=typeof e.data&&e.data.length>w.highlightSizeThreshold?O:hljs.highlightBlock(O)},toggleOperationContent:function(e){var t=$("#"+Docs.escapeResourceName(this.parentId+"_"+this.nickname+"_content"));t.is(":visible")?($.bbq.pushState("#/",2),e.preventDefault(),Docs.collapseOperation(t)):Docs.expandOperation(t)},getTextAreaValue:function(e){var t,n,i,r;if(null===e.value||0===jQuery.trim(e.value).length)return null;if(t=this.getParamByName(e.name),t&&t.type&&"array"===t.type.toLowerCase()){for(n=e.value.split("\n"),i=[],r=0;r<n.length;r++)null!==n[r]&&jQuery.trim(n[r]).length>0&&i.push(n[r]);return i.length>0?i:null}return e.value},showSnippet:function(){var e,t=this.$("[name=responseContentType]"),n=this.$(".operation-status .snippet_xml, .response-class .snippet_xml"),i=this.$(".operation-status .snippet_json, .response-class .snippet_json");t.length&&(e=t.val(),e.indexOf("xml")>-1?(n.show(),i.hide()):(i.show(),n.hide()))},getParamByName:function(e){var t;if(this.model.parameters)for(t=0;t<this.model.parameters.length;t++)if(this.model.parameters[t].name===e)return this.model.parameters[t];return null}}),SwaggerUi.Views.ParameterContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.parameterContentTypeId="pct"+Math.random(),$(this.el).html(Handlebars.templates.parameter_content_type(this.model)),this}}),SwaggerUi.Views.ParameterView=Backbone.View.extend({events:{"change [name=parameterContentType]":"toggleParameterSnippet"},initialize:function(){Handlebars.registerHelper("isArray",function(e,t){var n=e.type&&e.type.toLowerCase();return"array"===n||e.allowMultiple?t.fn(this):t.inverse(this)})},render:function(){var e,t,n=this.model.type||this.model.dataType,i=this.model.modelSignature.type,r=this.model.modelSignature.definitions,a=this.model.schema||{},o=this.model.consumes||[];if("undefined"==typeof n&&a.$ref){var s=a.$ref;n=0===s.indexOf("#/definitions/")?s.substring("#/definitions/".length):s}this.model.type=n,this.model.paramType=this.model["in"]||this.model.paramType,this.model.isBody="body"===this.model.paramType||"body"===this.model["in"],this.model.isFile=n&&"file"===n.toLowerCase(),"undefined"==typeof this.model["default"]&&(this.model["default"]=this.model.defaultValue),this.model.hasDefault="undefined"!=typeof this.model["default"],this.model.valueId="m"+this.model.name+Math.random(),this.model.allowableValues&&(this.model.isList=!0);var l=this.contains(o,"xml"),u=l?this.contains(o,"json"):!0;e=SwaggerUi.partials.signature.createParameterJSONSample(i,r);var c=this.template();$(this.el).html(c(this.model));var p={sampleJSON:u?e:!1,sampleXML:e&&l?SwaggerUi.partials.signature.createXMLSample("",a,r,!0):!1,isParam:!0,signature:SwaggerUi.partials.signature.getParameterModelSignature(i,r),defaultRendering:this.model.defaultRendering};e?(t=new SwaggerUi.Views.SignatureView({model:p,tagName:"div"}),$(".model-signature",$(this.el)).append(t.render().el)):$(".model-signature",$(this.el)).html(this.model.signature);var h=!1;if(this.options.swaggerOptions.jsonEditor&&this.model.isBody&&this.model.schema){var f=$(this.el);this.model.jsonEditor=new JSONEditor($(".editor_holder",f)[0],{schema:this.model.schema,startval:this.model["default"],ajax:!0,disable_properties:!0,disable_edit_json:!0,iconlib:"swagger"}),p.jsonEditor=this.model.jsonEditor,$(".body-textarea",f).hide(),$(".editor_holder",f).show(),$(".parameter-content-type",f).change(function(e){"application/xml"===e.target.value?($(".body-textarea",f).show(),$(".editor_holder",f).hide(),this.model.jsonEditor.disable()):($(".body-textarea",f).hide(),$(".editor_holder",f).show(),this.model.jsonEditor.enable())})}this.model.isBody&&(h=!0);var d={isParam:h};if(d.consumes=this.model.consumes,h){var m=new SwaggerUi.Views.ParameterContentTypeView({model:d});$(".parameter-content-type",$(this.el)).append(m.render().el),this.toggleParameterSnippet()}else{var y=new SwaggerUi.Views.ResponseContentTypeView({model:d});$(".response-content-type",$(this.el)).append(y.render().el),this.toggleResponseSnippet()}return this},contains:function(e,t){return e.filter(function(e){return e.indexOf(t)>-1?!0:void 0}).length},toggleParameterSnippet:function(){var e=this.$("[name=parameterContentType]").val();this.toggleSnippet(e)},toggleResponseSnippet:function(){var e=this.$("[name=responseContentType]");e.length&&this.toggleSnippet(e.val())},toggleSnippet:function(e){e=e||"",e.indexOf("xml")>-1?(this.$(".snippet_xml").show(),this.$(".snippet_json").hide()):(this.$(".snippet_json").show(),this.$(".snippet_xml").hide())},template:function(){return this.model.isList?Handlebars.templates.param_list:this.options.readOnly?this.model.required?Handlebars.templates.param_readonly_required:Handlebars.templates.param_readonly:this.model.required?Handlebars.templates.param_required:Handlebars.templates.param}}),SwaggerUi.partials.signature=function(){function e(e){var t,r=e.name,a=e.definition,o=e.config,s=e.models,l=e.config.isParam,u=[],c=a.properties,p=a.additionalProperties,h=a.xml,f=b(h);return f&&u.push(f),c||p?(c=c||{},t=_.map(c,function(e,t){var n,r;return l&&e.readOnly?"":(n=e.xml||{},r=i(t,e,s,o),n.attribute?(u.push(r),""):r)}).join(""),p&&(t+="<!-- additional elements allowed -->"),g(r,t,u)):n()}function t(e){return"<!-- Infinite loop $ref:"+e+" -->"}function n(e){return e=e?": "+e:"","<!-- invalid XML"+e+" -->"}function i(i,r,s,l){var u,c,p=_.isObject(r)?r.$ref:null;l=l||{},l.modelsToIgnore=l.modelsToIgnore||[];var h=_.isString(p)?a(p,i,s,l):o(i,r,s,l);if(!h)return n();switch(h.type){case"array":u=w(h);break;case"object":u=e(h);break;case"loop":u=t(h.name);break;default:u=A(h)}return p&&(c=l.modelsToIgnore.indexOf(p),c>-1&&l.modelsToIgnore.splice(c,1)),u}function r(e,t,n,i,r){if(arguments.length<4)throw new Error;this.config=r||{},this.config.modelsToIgnore=this.config.modelsToIgnore||[],this.name=v(e,n.xml),this.definition=n,this.models=i,this.type=t}function a(e,t,n,i){var a=u(e),o=n[a]||{},s=o.definition&&o.definition.type?o.definition.type:"object";return t=t||o.name,i.modelsToIgnore.indexOf(e)>-1?(s="loop",t=a):i.modelsToIgnore.push(e),o.definition?new r(t,s,o.definition,n,i):null}function o(e,t,n,i){var a=t.type||"object";return t?new r(e,a,t,n,i):null}function s(e,t,n,r){var a='<?xml version="1.0"?>';return p(a+i(e,t,n,{isParam:r}))}var l=function(e){return _.isPlainObject(e.schema)&&(e=l(e.schema)),e},u=function(e){return"undefined"==typeof e?null:0===e.indexOf("#/definitions/")?e.substring("#/definitions/".length):e},c=function(e){if(/^Inline Model \d+$/.test(e)&&this.inlineModels){var t=parseInt(e.substr("Inline Model".length).trim(),10),n=this.inlineModels[t];return n}return null},p=function(e){var t,n,i,r,a,o,s,l,u,c,p,h,f;for(p=/(>)(<)(\/*)/g,f=/[ ]*(.*)[ ]+\n/g,t=/(<.+>)(.+\n)/g,e=e.replace(p,"$1\n$2$3").replace(f,"$1\n").replace(t,"$1\n$2"),c=0,i="",l=e.split("\n"),r=0,o="other",h={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0},n=function(e){var t,n,a,s,l,u,c;u={single:Boolean(e.match(/<.+\/>/)),closing:Boolean(e.match(/<\/.+>/)),opening:Boolean(e.match(/<[^!?].*>/))},l=function(){var e;e=[];for(a in u)c=u[a],c&&e.push(a);return e}()[0],l=void 0===l?"other":l,t=o+"->"+l,o=l,s="",r+=h[t],s=function(){var e,t,i;for(i=[],n=e=0,t=r;t>=0?t>e:e>t;n=t>=0?++e:--e)i.push("  ");return i}().join(""),"opening->closing"===t?i=i.substr(0,i.length-1)+e+"\n":i+=s+e+"\n"},a=0,s=l.length;s>a;a++)u=l[a],n(u);return i},h=function(e,t,n,i){function r(e,t,i){var r,a=t;return e.$ref?(a=e.title||u(e.$ref),r=n[u(e.$ref)]):_.isUndefined(t)&&(a=e.title||"Inline Model "+ ++m,r={definition:e}),i!==!0&&(f[a]=_.isUndefined(r)?{}:r.definition),a}function a(e){var t='<span class="propType">',n=e.type||"object";return e.$ref?t+=r(e,u(e.$ref)):"object"===n?t+=_.isUndefined(e.properties)?"object":r(e):"array"===n?(t+="Array[",_.isArray(e.items)?t+=_.map(e.items,r).join(","):_.isPlainObject(e.items)?t+=_.isUndefined(e.items.$ref)?_.isUndefined(e.items.type)||-1!==_.indexOf(["array","object"],e.items.type)?r(e.items):e.items.type:r(e.items,u(e.items.$ref)):(console.log("Array type's 'items' schema is not an array or an object, cannot process"),t+="object"),t+="]"):t+=e.type,t+="</span>"}function o(e,t){var n="",i=e.type||"object",r="array"===i;switch(_.isUndefined(e.description)||(t+=': <span class="propDesc">'+e.description+"</span>"),e["enum"]&&(t+=' = <span class="propVals">[\''+e["enum"].join("', '")+"']</span>"),r&&(i=_.isPlainObject(e.items)&&!_.isUndefined(e.items.type)?e.items.type:"object"),_.isUndefined(e["default"])||(n+=h("Default",e["default"])),i){case"string":e.minLength&&(n+=h("Min. Length",e.minLength)),e.maxLength&&(n+=h("Max. Length",e.maxLength)),e.pattern&&(n+=h("Reg. Exp.",e.pattern));break;case"integer":case"number":e.minimum&&(n+=h("Min. Value",e.minimum)),e.exclusiveMinimum&&(n+=h("Exclusive Min.","true")),e.maximum&&(n+=h("Max. Value",e.maximum)),e.exclusiveMaximum&&(n+=h("Exclusive Max.","true")),e.multipleOf&&(n+=h("Multiple Of",e.multipleOf))}if(r&&(e.minItems&&(n+=h("Min. Items",e.minItems)),e.maxItems&&(n+=h("Max. Items",e.maxItems)),e.uniqueItems&&(n+=h("Unique Items","true")),e.collectionFormat&&(n+=h("Coll. Format",e.collectionFormat))),_.isUndefined(e.items)&&_.isArray(e["enum"])){var a;a="number"===i||"integer"===i?e["enum"].join(", "):'"'+e["enum"].join('", "')+'"',n+=h("Enum",a)}return n.length>0&&(t='<span class="propWrap">'+t+'<table class="optionsWrapper"><tr><th colspan="2">'+i+"</th></tr>"+n+"</table></span>"),t}function s(e,t){var s,h=e.type||"object",f="array"===e.type,m=c+t+" "+(f?"[":"{")+p;return t&&d.push(t),f?_.isArray(e.items)?m+="<div>"+_.map(e.items,function(e){var t=e.type||"object";return _.isUndefined(e.$ref)?_.indexOf(["array","object"],t)>-1?"object"===t&&_.isUndefined(e.properties)?"object":r(e):o(e,t):r(e,u(e.$ref))}).join(",</div><div>"):_.isPlainObject(e.items)?m+=_.isUndefined(e.items.$ref)?_.indexOf(["array","object"],e.items.type||"object")>-1?(_.isUndefined(e.items.type)||"object"===e.items.type)&&_.isUndefined(e.items.properties)?"<div>object</div>":"<div>"+r(e.items)+"</div>":"<div>"+o(e.items,e.items.type)+"</div>":"<div>"+r(e.items,u(e.items.$ref))+"</div>":(console.log("Array type's 'items' property is not an array or an object, cannot process"),m+="<div>object</div>"):e.$ref?m+="<div>"+r(e,t)+"</div>":"object"===h?(_.isPlainObject(e.properties)&&(s=_.map(e.properties,function(t,r){var s,c=_.indexOf(e.required,r)>=0,p=_.cloneDeep(t),h=c?"required":"",f='<span class="propName '+h+'">'+r+"</span> (";return p["default"]=i(p),p=l(p),_.isUndefined(p.$ref)||(s=n[u(p.$ref)],_.isUndefined(s)||-1!==_.indexOf([void 0,"array","object"],s.definition.type)||(p=l(s.definition))),f+=a(p),c||(f+=', <span class="propOptKey">optional</span>'),t.readOnly&&(f+=', <span class="propReadOnly">read only</span>'),f+=")","<div"+(t.readOnly?' class="readOnly"':"")+">"+o(p,f)}).join(",</div>")),s&&(m+=s+"</div>")):m+="<div>"+o(e,h)+"</div>",m+c+(f?"]":"}")+p}var c='<span class="strong">',p="</span>",h=function(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"};if(_.isObject(arguments[0])&&(e=void 0,t=arguments[0],n=arguments[1],i=arguments[2]),n=n||{},t=l(t),_.isEmpty(t))return c+"Empty"+p;if("string"==typeof t.$ref&&(e=u(t.$ref),t=n[e],"undefined"==typeof t))return c+e+" is not defined!"+p;"string"!=typeof e&&(e=t.title||"Inline Model"),t.definition&&(t=t.definition),"function"!=typeof i&&(i=function(e){return(e||{})["default"]});for(var f={},d=[],m=0,y=s(t,e);_.keys(f).length>0;)_.forEach(f,function(e,t){var n=_.indexOf(d,t)>-1;delete f[t],n||(d.push(t),y+="<br />"+s(e,t))});return y},f=function(e,t,n,i){e=l(e),"function"!=typeof i&&(i=function(e){return(e||{})["default"]}),n=n||{};var r,a,o=e.type||"object",s=e.format;return _.isUndefined(e.example)?_.isUndefined(e.items)&&_.isArray(e["enum"])&&(a=e["enum"][0]):a=e.example,_.isUndefined(a)&&(e.$ref?(r=t[u(e.$ref)],_.isUndefined(r)||(_.isUndefined(n[r.name])?(n[r.name]=r,a=f(r.definition,t,n,i),delete n[r.name]):a="array"===r.type?[]:{})):_.isUndefined(e["default"])?"string"===o?a="date-time"===s?(new Date).toISOString():"date"===s?(new Date).toISOString().split("T")[0]:"string":"integer"===o?a=0:"number"===o?a=0:"boolean"===o?a=!0:"object"===o?(a={},_.forEach(e.properties,function(e,r){var o=_.cloneDeep(e);o["default"]=i(e),a[r]=f(o,t,n,i)})):"array"===o&&(a=[],_.isArray(e.items)?_.forEach(e.items,function(e){a.push(f(e,t,n,i))}):_.isPlainObject(e.items)?a.push(f(e.items,t,n,i)):_.isUndefined(e.items)?a.push({}):console.log("Array type's 'items' property is not an array or an object, cannot process")):a=e["default"]),a},d=function(e,t){return t=t||{},t[e.name]=e,e.examples&&_.isPlainObject(e.examples)&&e.examples["application/json"]?(e.definition.example=e.examples["application/json"],_.isString(e.definition.example)&&(e.definition.example=jsyaml.safeLoad(e.definition.example))):e.definition.example||(e.definition.example=e.examples),f(e.definition,e.models,t,e.modelPropertyMacro)},m=function(e,t){var n,i;return e instanceof Array&&(i=!0,e=e[0]),"undefined"==typeof e?(e="undefined",n=!0):t[e]?(e=t[e],n=!1):c(e)?(e=c(e),n=!1):n=!0,n?i?"Array["+e+"]":e.toString():i?"Array["+h(e.name,e.definition,e.models,e.modelPropertyMacro)+"]":h(e.name,e.definition,e.models,e.modelPropertyMacro)},y=function(e,t){var n,i,r;if(t=t||{},n=e instanceof Array,r=n?e[0]:e,t[r]?i=d(t[r]):c(r)&&(i=d(c(r))),i){if(i=n?[i]:i,"string"==typeof i)return i;if(_.isObject(i)){var a=i;if(i instanceof Array&&i.length>0&&(a=i[0]),a.nodeName&&"Node"==typeof a){var o=(new XMLSerializer).serializeToString(a);return p(o)}return JSON.stringify(i,null,2)}return i}},g=function(e,t,i){var r,a;return i=i||[],a=i.map(function(e){return" "+e.name+'="'+e.value+'"'}).join(""),e?(r=["<",e,a,">",t,"</",e,">"],r.join("")):n("Node name is not provided")},v=function(e,t){var n=e||"";return t=t||{},t.name&&(n=t.name),t.prefix&&(n=t.prefix+":"+n),n},b=function(e){var t="",n="xmlns";return e=e||{},e.namespace?(t=e.namespace,e.prefix&&(n+=":"+e.prefix),{name:n,value:t}):t},w=function(e){var t,r=e.name,a=e.config,o=e.definition,s=e.models,l=o.items,u=o.xml||{},c=b(u),p=[];return l?(t=i(r,l,s,a),c&&p.push(c),u.wrapped&&(t=g(r,t,p)),t):n()},x=function(e){var t,n;switch(e=e||{},n=e.items||{},t=e.type||""){case"object":return"Object is not a primitive";case"array":return"Array["+(n.format||n.type)+"]";default:return e.format||t}},A=function(e){var t,i=e.name,r=e.definition,a={string:{date:new Date(1).toISOString().split("T")[0],"date-time":new Date(1).toISOString(),"default":"string"},integer:{"default":1},number:{"default":1.1},"boolean":{"default":!0}},o=r.type,s=r.format,l=r.xml||{},u=b(l),c=[];return _.keys(a).indexOf(o)<0?n():(t=_.isArray(r["enum"])?r["enum"][0]:r.example||a[o][s]||a[o]["default"],l.attribute?{name:i,value:t}:(u&&c.push(u),g(i,t,c)))};return{getModelSignature:h,createJSONSample:d,getParameterModelSignature:m,createParameterJSONSample:y,createSchemaXML:i,createXMLSample:s,getPrimitiveSignature:x}}(),SwaggerUi.Views.PopupView=Backbone.View.extend({events:{"click .api-popup-cancel":"cancelClick"},template:Handlebars.templates.popup,className:"api-popup-dialog",selectors:{content:".api-popup-content",main:"#swagger-ui-container"},initialize:function(){this.$el.html(this.template(this.model))},render:function(){return this.$(this.selectors.content).append(this.model.content),$(this.selectors.main).first().append(this.el),this.showPopup(),this},showPopup:function(){this.$el.show()},cancelClick:function(){this.remove()}}),SwaggerUi.Views.ResourceView=Backbone.View.extend({initialize:function(e){e=e||{},this.router=e.router,this.auths=e.auths,""===this.model.description&&(this.model.description=null),this.model.description&&(this.model.summary=this.model.description)},render:function(){var e={};$(this.el).html(Handlebars.templates.resource(this.model));for(var t=0;t<this.model.operationsArray.length;t++){for(var n=this.model.operationsArray[t],i=0,r=n.nickname;"undefined"!=typeof e[r];)r=r+"_"+i,i+=1;e[r]=n,n.nickname=r,n.parentId=this.model.id,n.definitions=this.model.definitions,this.addOperation(n)}return $(".toggleEndpointList",this.el).click(this.callDocs.bind(this,"toggleEndpointListForResource")),$(".collapseResource",this.el).click(this.callDocs.bind(this,"collapseOperationsForResource")),$(".expandResource",this.el).click(this.callDocs.bind(this,"expandOperationsForResource")),this},addOperation:function(e){e.number=this.number;var t=new SwaggerUi.Views.OperationView({model:e,router:this.router,tagName:"li",className:"endpoint",swaggerOptions:this.options.swaggerOptions,auths:this.auths});$(".endpoints",$(this.el)).append(t.render().el),this.number++},callDocs:function(e,t){t.preventDefault(),Docs[e](t.currentTarget.getAttribute("data-id"))}}),SwaggerUi.Views.ResponseContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return this.model.responseContentTypeId="rct"+Math.random(),$(this.el).html(Handlebars.templates.response_content_type(this.model)),this}}),SwaggerUi.Views.SignatureView=Backbone.View.extend({events:{"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet_json":"jsonSnippetMouseDown","mousedown .snippet_xml":"xmlSnippetMouseDown"},initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.signature(this.model)),"model"===this.model.defaultRendering?this.switchToDescription():this.switchToSnippet(),this},switchToDescription:function(e){e&&e.preventDefault(),$(".snippet",$(this.el)).hide(),$(".description",$(this.el)).show(),$(".description-link",$(this.el)).addClass("selected"),$(".snippet-link",$(this.el)).removeClass("selected")},switchToSnippet:function(e){e&&e.preventDefault(),$(".snippet",$(this.el)).show(),$(".description",$(this.el)).hide(),$(".snippet-link",$(this.el)).addClass("selected"),$(".description-link",$(this.el)).removeClass("selected")},snippetToTextArea:function(e){var t=$("textarea",$(this.el.parentNode.parentNode.parentNode));""!==$.trim(t.val())&&t.prop("placeholder")!==t.val()||(t.val(e),this.model.jsonEditor&&this.model.jsonEditor.isEnabled()&&this.model.jsonEditor.setValue(JSON.parse(this.model.sampleJSON)));
+},jsonSnippetMouseDown:function(e){this.model.isParam&&(e&&e.preventDefault(),this.snippetToTextArea(this.model.sampleJSON))},xmlSnippetMouseDown:function(e){this.model.isParam&&(e&&e.preventDefault(),this.snippetToTextArea(this.model.sampleXML))}}),SwaggerUi.Views.StatusCodeView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){var e,t,n=this.router.api.models[this.model.responseModel];return $(this.el).html(Handlebars.templates.status_code(this.model)),e=this.router.api.models.hasOwnProperty(this.model.responseModel)?{sampleJSON:JSON.stringify(SwaggerUi.partials.signature.createJSONSample(n),void 0,2),sampleXML:this.model.isXML?SwaggerUi.partials.signature.createXMLSample("",this.model.schema,this.router.api.models):!1,isParam:!1,signature:SwaggerUi.partials.signature.getModelSignature(this.model.responseModel,n,this.router.api.models),defaultRendering:this.model.defaultRendering}:{signature:SwaggerUi.partials.signature.getPrimitiveSignature(this.model.schema)},t=new SwaggerUi.Views.SignatureView({model:e,tagName:"div"}),$(".model-signature",this.$el).append(t.render().el),this}})}).call(this);
\ No newline at end of file

profiles/pom.xml 2(+1 -1)

diff --git a/profiles/pom.xml b/profiles/pom.xml
index 80bd043..22c7a33 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 4d54e6a..77e04bb 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>
@@ -50,6 +50,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -59,6 +74,11 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
@@ -138,6 +158,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/alignment/BaseAligner.java b/subscription/src/main/java/org/killbill/billing/subscription/alignment/BaseAligner.java
index 57ce150..e0a1128 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/alignment/BaseAligner.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/alignment/BaseAligner.java
@@ -1,11 +1,13 @@
 /*
- * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2010-2014 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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
+ *    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
@@ -17,7 +19,6 @@
 package org.killbill.billing.subscription.alignment;
 
 import org.joda.time.DateTime;
-
 import org.killbill.billing.catalog.api.Duration;
 
 public class BaseAligner {
@@ -30,25 +31,7 @@ public class BaseAligner {
         return addOrRemoveDuration(input, duration, false);
     }
 
-    private DateTime addOrRemoveDuration(final DateTime input, final Duration duration, boolean add) {
-        DateTime result = input;
-        switch (duration.getUnit()) {
-            case DAYS:
-                result = add ? result.plusDays(duration.getNumber()) : result.minusDays(duration.getNumber());
-                ;
-                break;
-
-            case MONTHS:
-                result = add ? result.plusMonths(duration.getNumber()) : result.minusMonths(duration.getNumber());
-                break;
-
-            case YEARS:
-                result = add ? result.plusYears(duration.getNumber()) : result.minusYears(duration.getNumber());
-                break;
-            case UNLIMITED:
-            default:
-                throw new RuntimeException("Trying to move to unlimited time period");
-        }
-        return result;
+    private DateTime addOrRemoveDuration(final DateTime input, final Duration duration, final boolean add) {
+        return add ? input.plus(duration.toJodaPeriod()) : input.minus(duration.toJodaPeriod());
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
index 2a51037..0d9d275 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/alignment/PlanAligner.java
@@ -38,7 +38,6 @@ import org.killbill.billing.catalog.api.PlanAlignmentCreate;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
-import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionData;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -116,7 +115,7 @@ public class PlanAligner extends BaseAligner {
                                                    final String priceList,
                                                    final DateTime effectiveDate,
                                                    final InternalTenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
-        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.CURRENT, context);
+        return getTimedPhaseOnChange(subscription, plan, effectiveDate, WhichPhase.CURRENT, context);
     }
 
     /**
@@ -136,7 +135,7 @@ public class PlanAligner extends BaseAligner {
                                                 final String priceList,
                                                 final DateTime effectiveDate,
                                                 final InternalTenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
-        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.NEXT, context);
+        return getTimedPhaseOnChange(subscription, plan, effectiveDate, WhichPhase.NEXT, context);
     }
 
     /**
@@ -155,9 +154,7 @@ public class PlanAligner extends BaseAligner {
 
             switch (lastPlanTransition.getTransitionType()) {
                 // If we never had any Plan change, borrow the logic for createPlan alignment
-                case MIGRATE_ENTITLEMENT:
                 case CREATE:
-                case RE_CREATE:
                 case TRANSFER:
                     final List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getAlignStartDate(),
                                                                                subscription.getBundleStartDate(),
@@ -172,9 +169,7 @@ public class PlanAligner extends BaseAligner {
                                                  subscription.getBundleStartDate(),
                                                  lastPlanTransition.getPreviousPhase(),
                                                  lastPlanTransition.getPreviousPlan(),
-                                                 lastPlanTransition.getPreviousPriceList().getName(),
                                                  lastPlanTransition.getNextPlan(),
-                                                 lastPlanTransition.getNextPriceList().getName(),
                                                  effectiveDate,
                                                  lastPlanTransition.getEffectiveTransitionTime(),
                                                  subscription.getAllTransitions().get(0).getNextPhase().getPhaseType(),
@@ -197,12 +192,9 @@ public class PlanAligner extends BaseAligner {
                                                    final DateTime effectiveDate,
                                                    final InternalTenantContext context)
             throws CatalogApiException, SubscriptionBaseApiException {
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
 
-        final PlanSpecifier planSpecifier = new PlanSpecifier(plan.getProduct().getName(),
-                                                              plan.getProduct().getCategory(),
-                                                              plan.getRecurringBillingPeriod(),
-                                                              priceList);
+        final PlanSpecifier planSpecifier = new PlanSpecifier(plan.getName());
 
         final DateTime planStartDate;
         final PlanAlignmentCreate alignment = catalog.planCreateAlignment(planSpecifier, effectiveDate);
@@ -222,7 +214,6 @@ public class PlanAligner extends BaseAligner {
 
     private TimedPhase getTimedPhaseOnChange(final DefaultSubscriptionBase subscription,
                                              final Plan nextPlan,
-                                             final String nextPriceList,
                                              final DateTime effectiveDate,
                                              final WhichPhase which,
                                              final InternalTenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
@@ -230,9 +221,7 @@ public class PlanAligner extends BaseAligner {
                                      subscription.getBundleStartDate(),
                                      subscription.getCurrentPhase(),
                                      subscription.getCurrentPlan(),
-                                     subscription.getCurrentPriceList().getName(),
                                      nextPlan,
-                                     nextPriceList,
                                      effectiveDate,
                                      // This method is only called while doing the change, hence we want to pass the change effective date
                                      effectiveDate,
@@ -245,27 +234,17 @@ public class PlanAligner extends BaseAligner {
                                              final DateTime bundleStartDate,
                                              final PlanPhase currentPhase,
                                              final Plan currentPlan,
-                                             final String currentPriceList,
                                              final Plan nextPlan,
-                                             final String priceList,
                                              final DateTime effectiveDate,
                                              final DateTime lastOrCurrentChangeEffectiveDate,
                                              final PhaseType originalInitialPhase,
                                              final WhichPhase which,
                                              final InternalTenantContext context) throws CatalogApiException, SubscriptionBaseApiException {
-        final Catalog catalog = catalogService.getFullCatalog(context);
-        final ProductCategory currentCategory = currentPlan.getProduct().getCategory();
-        final PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                                 currentCategory,
-                                                                                 currentPlan.getRecurringBillingPeriod(),
-                                                                                 currentPriceList,
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
+        final PlanPhaseSpecifier fromPlanPhaseSpecifier = new PlanPhaseSpecifier(currentPlan.getName(),
                                                                                  currentPhase.getPhaseType());
 
-        final PlanSpecifier toPlanSpecifier = new PlanSpecifier(nextPlan.getProduct().getName(),
-                                                                nextPlan.getProduct().getCategory(),
-                                                                nextPlan.getRecurringBillingPeriod(),
-                                                                priceList);
-
+        final PlanSpecifier toPlanSpecifier = new PlanSpecifier(nextPlan.getName());
         final PhaseType initialPhase;
         final DateTime planStartDate;
         final PlanAlignmentChange alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier, effectiveDate);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
index e576d0e..3734c92 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionApiBase.java
@@ -62,7 +62,7 @@ public class SubscriptionApiBase {
     protected DefaultSubscriptionBase createSubscriptionForApiUse(SubscriptionBuilder builder, List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, apiService, clock);
         if (events.size() > 0) {
-            subscription.rebuildTransitions(events, catalogService.getFullCatalog(context));
+            subscription.rebuildTransitions(events, catalogService.getFullCatalog(true, true, context));
         }
         return subscription;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index b803399..dc345c6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -31,7 +31,7 @@ import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanChangeResult;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBuilder;
@@ -47,11 +47,7 @@ public interface SubscriptionBaseApiService {
                                               CallContext context)
             throws SubscriptionBaseApiException;
 
-    public DefaultSubscriptionBase createPlans(Iterable<SubscriptionSpecifier> subscriptions, CallContext context)
-            throws SubscriptionBaseApiException;
-
-    @Deprecated
-    public boolean recreatePlan(DefaultSubscriptionBase subscription, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs, CallContext context)
+    public List<DefaultSubscriptionBase> createPlans(Iterable<SubscriptionSpecifier> subscriptions, CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean cancel(DefaultSubscriptionBase subscription, CallContext context)
@@ -70,36 +66,33 @@ public interface SubscriptionBaseApiService {
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime dryRunChangePlan(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                                     String priceList, DateTime requestedDate, BillingActionPolicy policy, TenantContext context) throws SubscriptionBaseApiException;
+    public DateTime dryRunChangePlan(DefaultSubscriptionBase subscription, PlanSpecifier spec, DateTime requestedDate, BillingActionPolicy policy, TenantContext context) throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlan(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                               String priceList, List<PlanPhasePriceOverride> overrides, CallContext context)
+    public DateTime changePlan(DefaultSubscriptionBase subscription, PlanSpecifier spec, List<PlanPhasePriceOverride> overrides, CallContext context)
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlanWithRequestedDate(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                                                String priceList, List<PlanPhasePriceOverride> overrides, DateTime requestedDate, CallContext context)
+    public DateTime changePlanWithRequestedDate(DefaultSubscriptionBase subscription, PlanSpecifier spec,
+                                                List<PlanPhasePriceOverride> overrides, DateTime requestedDate, CallContext context)
             throws SubscriptionBaseApiException;
 
     // Return the effective date of the change
-    public DateTime changePlanWithPolicy(DefaultSubscriptionBase subscription, String productName, BillingPeriod term,
-                                         String priceList, List<PlanPhasePriceOverride> overrides, BillingActionPolicy policy, CallContext context)
+    public DateTime changePlanWithPolicy(DefaultSubscriptionBase subscription, PlanSpecifier spec,
+                                         List<PlanPhasePriceOverride> overrides, BillingActionPolicy policy, CallContext context)
             throws SubscriptionBaseApiException;
 
     public int cancelAddOnsIfRequiredOnBasePlanEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent event, final CallContext context) throws CatalogApiException;
 
-    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
-                                                final BillingPeriod term, final String priceList, final DateTime effectiveDate, TenantContext context) throws SubscriptionBaseApiException;
+    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, PlanSpecifier spec, final DateTime effectiveDate, TenantContext context) throws SubscriptionBaseApiException;
 
     //
     // Lower level APIs for dryRun functionality
     //
-    public List<SubscriptionBaseEvent> getEventsOnCreation(UUID bundleId, UUID subscriptionId, DateTime alignStartDate, DateTime bundleStartDate, long activeVersion,
+    public List<SubscriptionBaseEvent> getEventsOnCreation(UUID bundleId, UUID subscriptionId, DateTime alignStartDate, DateTime bundleStartDate,
                                                            Plan plan, PhaseType initialPhase,
                                                            String realPriceList, DateTime effectiveDate, DateTime processedDate,
-                                                           boolean reCreate, InternalTenantContext context)
+                                                           InternalTenantContext context)
             throws CatalogApiException, SubscriptionBaseApiException;
 
     public List<SubscriptionBaseEvent> getEventsOnChangePlan(DefaultSubscriptionBase subscription, Plan newPlan,
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 278959e..4b18a23 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -19,6 +19,8 @@
 package org.killbill.billing.subscription.api.svcs;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -28,11 +30,13 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.BillingAlignment;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
@@ -42,7 +46,7 @@ import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
@@ -70,9 +74,11 @@ import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseServ
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.engine.dao.model.SubscriptionBundleModelDao;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
-import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
+import org.killbill.billing.subscription.events.bcd.BCDEvent;
+import org.killbill.billing.subscription.events.bcd.BCDEventData;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.UUIDs;
+import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -107,6 +113,20 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     private final NotificationQueueService notificationQueueService;
 
+    public static final Comparator<SubscriptionBase> SUBSCRIPTIONS_COMPARATOR = new Comparator<SubscriptionBase>() {
+
+        @Override
+        public int compare(final SubscriptionBase o1, final SubscriptionBase o2) {
+            if (o1.getCategory() == ProductCategory.BASE) {
+                return -1;
+            } else if (o2.getCategory() == ProductCategory.BASE) {
+                return 1;
+            } else {
+                return ((DefaultSubscriptionBase) o1).getAlignStartDate().compareTo(((DefaultSubscriptionBase) o2).getAlignStartDate());
+            }
+        }
+    };
+
     @Inject
     public DefaultSubscriptionInternalApi(final SubscriptionDao dao,
                                           final DefaultSubscriptionBaseApiService apiService,
@@ -122,9 +142,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     }
 
     @Override
-    public SubscriptionBase createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionBaseApiException {
+    public SubscriptionBase createSubscription(final UUID bundleId, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final boolean isMigrated, final InternalCallContext context) throws SubscriptionBaseApiException {
         try {
-            final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
             final DateTime now = clock.getUTCNow();
             final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
             /*
@@ -134,14 +153,14 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             */
 
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, true, context);
             final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, callContext);
 
-            final Plan plan = catalog.createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
+            final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
             final PlanPhase phase = plan.getAllPhases()[0];
             if (phase == null) {
                 throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
-                                                              spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
+                                                              spec.getProductName(), spec.getBillingPeriod().toString(), plan.getPriceListName()));
             }
 
             final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
@@ -150,51 +169,85 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             }
 
             final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
+
+            // verify the number of subscriptions (of the same kind) allowed per bundle
+            if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
+                if (plan.getPlansAllowedInBundle() != -1
+                    && plan.getPlansAllowedInBundle() > 0
+                    && addonUtils.countExistingAddOnsWithSamePlanName(getSubscriptionsForBundle(bundleId, null, context), plan.getName())
+                       >= plan.getPlansAllowedInBundle()) {
+                    // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
+                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
+                }
+            }
+
             final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, effectiveDate, context);
             return apiService.createPlan(new SubscriptionBuilder()
                                                  .setId(UUIDs.randomUUID())
                                                  .setBundleId(bundleId)
                                                  .setCategory(plan.getProduct().getCategory())
                                                  .setBundleStartDate(bundleStartDate)
-                                                 .setAlignStartDate(effectiveDate),
-                                         plan, spec.getPhaseType(), realPriceList, effectiveDate, now, callContext);
+                                                 .setAlignStartDate(effectiveDate)
+                                                 .setMigrated(isMigrated),
+                                         plan, spec.getPhaseType(), plan.getPriceListName(), effectiveDate, now, callContext);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
     @Override
-    public SubscriptionBase createBaseSubscriptionWithAddOns(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final DateTime requestedDateWithMs, final InternalCallContext context) throws SubscriptionBaseApiException {
+    public List<SubscriptionBase> createBaseSubscriptionWithAddOns(final UUID bundleId, final Iterable<EntitlementSpecifier> entitlements, final DateTime requestedDateWithMs, final boolean isMigrated, final InternalCallContext context) throws SubscriptionBaseApiException {
 
         final DateTime now = clock.getUTCNow();
         final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
 
         try {
             final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
-            final Catalog catalog = catalogService.getFullCatalog(context);
+            final Catalog catalog = catalogService.getFullCatalog(true, true, context);
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
 
+            final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
+            if (bundle == null) {
+                throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
+            }
+
+            boolean first = true;
+            final List<SubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundleId, null, context);
+
             for (EntitlementSpecifier entitlement : entitlements) {
 
                 final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
-                final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
 
                 final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(entitlement.getOverrides(), callContext);
 
-                final Plan plan = catalog.createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
+                final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
                 final PlanPhase phase = plan.getAllPhases()[0];
                 if (phase == null) {
                     throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
-                                                                  spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
+                                                                  spec.getProductName(), spec.getBillingPeriod().toString(), plan.getPriceListName()));
                 }
 
-                final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
-                if (bundle == null) {
-                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
+                if (first) {
+                    first = false;
+                    if (plan.getProduct().getCategory() != ProductCategory.BASE) {
+                        throw new SubscriptionBaseApiException(new IllegalArgumentException(), ErrorCode.SUB_CREATE_NO_BP.getCode(), "Missing Base Subscription.");
+                    }
+                }
+
+                // verify the number of subscriptions (of the same kind) allowed per bundle and the existing ones
+                if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
+                    if (plan.getPlansAllowedInBundle() != -1 && plan.getPlansAllowedInBundle() > 0) {
+                        int existingAddOnsWithSamePlanName = addonUtils.countExistingAddOnsWithSamePlanName(subscriptionsForBundle, plan.getName());
+                        int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlements, catalog, plan.getName(), effectiveDate, callContext);
+                        if ((existingAddOnsWithSamePlanName + currentAddOnsWithSamePlanName) > plan.getPlansAllowedInBundle()) {
+                            // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
+                            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
+                        }
+                    }
                 }
 
                 SubscriptionSpecifier subscription = new SubscriptionSpecifier();
-                subscription.setRealPriceList(realPriceList);
+                subscription.setRealPriceList(plan.getPriceListName());
                 subscription.setEffectiveDate(effectiveDate);
                 subscription.setProcessedDate(now);
                 subscription.setPlan(plan);
@@ -204,17 +257,43 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                                                 .setBundleId(bundleId)
                                                 .setCategory(plan.getProduct().getCategory())
                                                 .setBundleStartDate(effectiveDate)
-                                                .setAlignStartDate(effectiveDate));
+                                                .setAlignStartDate(effectiveDate)
+                                                .setMigrated(isMigrated));
 
                 subscriptions.add(subscription);
             }
 
-            return apiService.createPlans(subscriptions, callContext);
+            final List<DefaultSubscriptionBase> result = apiService.createPlans(subscriptions, callContext);
+            return ImmutableList.copyOf(Iterables.transform(result, new Function<DefaultSubscriptionBase, SubscriptionBase>() {
+                @Override
+                public SubscriptionBase apply(final DefaultSubscriptionBase input) {
+                    return (SubscriptionBase) input;
+                }
+            }));
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
+    private int countCurrentAddOnsWithSamePlanName(final Iterable<EntitlementSpecifier> entitlements,
+                                                   final Catalog catalog, final String planName,
+                                                   final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
+        int countCurrentAddOns = 0;
+        for (EntitlementSpecifier entitlement : entitlements) {
+            final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
+            final PlanPhasePriceOverridesWithCallContext overridesWithContext =
+                    new DefaultPlanPhasePriceOverridesWithCallContext(entitlement.getOverrides(), callContext);
+            final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
+
+            if (plan.getName().equalsIgnoreCase(planName)
+                && plan.getProduct().getCategory() != null
+                && ProductCategory.ADD_ON.equals(plan.getProduct().getCategory())) {
+                countCurrentAddOns++;
+            }
+        }
+        return countCurrentAddOns;
+    }
+
     @Override
     public void cancelBaseSubscriptions(final Iterable<SubscriptionBase> subscriptions, final BillingActionPolicy policy, final InternalCallContext context) throws SubscriptionBaseApiException {
         apiService.cancelWithPolicyNoValidation(Iterables.<SubscriptionBase, DefaultSubscriptionBase>transform(subscriptions,
@@ -239,6 +318,10 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         final DateTime now = clock.getUTCNow();
         final DateTime originalCreatedDate = existingBundles.size() > 0 ? existingBundles.get(0).getCreatedDate() : now;
         final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, originalCreatedDate, now, now);
+
+        if (null != bundleKey && bundleKey.length() > 255) {
+            throw new SubscriptionBaseApiException(ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED);
+        }
         return dao.createSubscriptionBundle(bundle, context);
     }
 
@@ -337,6 +420,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             if (result != null && !result.isEmpty()) {
                 outputSubscriptions.addAll(result);
             }
+            Collections.sort(outputSubscriptions, DefaultSubscriptionInternalApi.SUBSCRIPTIONS_COMPARATOR);
+
             return createSubscriptionsForApiUse(outputSubscriptions);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
@@ -424,14 +509,30 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Override
     public DateTime getDryRunChangePlanEffectiveDate(final SubscriptionBase subscription,
-                                                     final String productName,
-                                                     final BillingPeriod term,
-                                                     final String priceList,
+                                                     final PlanSpecifier spec,
                                                      final DateTime requestedDateWithMs,
                                                      final BillingActionPolicy requestedPolicy,
-                                                     final InternalTenantContext context) throws SubscriptionBaseApiException {
+                                                     final List<PlanPhasePriceOverride> overrides,
+                                                     final InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException {
         final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
-        return apiService.dryRunChangePlan((DefaultSubscriptionBase) subscription, productName, term, priceList, requestedDateWithMs, requestedPolicy, tenantContext);
+        final CallContext callContext = internalCallContextFactory.createCallContext(context);
+
+        // verify the number of subscriptions (of the same kind) allowed per bundle
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
+        final DateTime now = clock.getUTCNow();
+        final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
+        final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, callContext);
+        final Plan plan = catalog.createOrFindPlan(spec, overridesWithContext, effectiveDate);
+        if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
+            if (plan.getPlansAllowedInBundle() != -1
+                && plan.getPlansAllowedInBundle() > 0
+                && addonUtils.countExistingAddOnsWithSamePlanName(getSubscriptionsForBundle(subscription.getBundleId(), null, context), plan.getName())
+                   >= plan.getPlansAllowedInBundle()) {
+                // the plan can be changed to the new value, because it has reached its limit by bundle
+                throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
+            }
+        }
+        return apiService.dryRunChangePlan((DefaultSubscriptionBase) subscription, spec, requestedDateWithMs, requestedPolicy, tenantContext);
     }
 
     @Override
@@ -499,24 +600,28 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         List<SubscriptionBaseEvent> dryRunEvents = null;
         try {
             final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
-            final String realPriceList = (inputSpec != null && inputSpec.getPriceListName() != null) ? inputSpec.getPriceListName() : PriceListSet.DEFAULT_PRICELIST_NAME;
-            final Catalog catalog = catalogService.getFullCatalog(context);
-
-            final PlanPhasePriceOverridesWithCallContext overridesWithContext = null; // TODO not supported to dryRun with custom price
-            final Plan plan = (inputSpec != null && inputSpec.getProductName() != null && inputSpec.getBillingPeriod() != null) ?
-                              catalog.createOrFindPlan(inputSpec.getProductName(), inputSpec.getBillingPeriod(), realPriceList, overridesWithContext, utcNow) : null;
+            final boolean isInputSpecNullOrEmpty = inputSpec == null ||
+                                                   (inputSpec.getPlanName() == null && inputSpec.getProductName() == null && inputSpec.getBillingPeriod() == null);
+            final Catalog catalog = catalogService.getFullCatalog(true, true, context);
+
+            // Create an overridesWithContext with a null context to indicate this is dryRun and no price overriden plan should be created.
+            final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(dryRunArguments.getPlanPhasePriceOverrides(), null);
+            final Plan plan = isInputSpecNullOrEmpty ?
+                              null :
+                              catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow);
             final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
 
+
             if (dryRunArguments != null) {
                 switch (dryRunArguments.getAction()) {
                     case START_BILLING:
 
                         final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
-                        final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? dryRunArguments.getEffectiveDate() : utcNow;
+                        final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : utcNow;
                         final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, context);
                         final UUID subscriptionId = UUIDs.randomUUID();
-                        dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, 1L, plan, inputSpec.getPhaseType(), realPriceList,
-                                                                      startEffectiveDate, utcNow, false, context);
+                        dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), plan.getPriceListName(),
+                                                                      startEffectiveDate, utcNow, context);
                         final SubscriptionBuilder builder = new SubscriptionBuilder()
                                 .setId(subscriptionId)
                                 .setBundleId(bundleId)
@@ -530,35 +635,29 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
                     case CHANGE:
                         final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
-                        DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate();
+                        DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
                         if (changeEffectiveDate == null) {
                             BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
                             if (policy == null) {
-                                final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange,
-                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getProductName(),
-                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getBillingPeriod(),
-                                                                                                         dryRunArguments.getPlanPhaseSpecifier().getPriceListName(), utcNow, tenantContext);
+                                final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
                                 policy = planChangeResult.getPolicy();
                             }
                             changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy);
                         }
-                        dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, realPriceList, changeEffectiveDate, utcNow, true, context);
+                        dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceListName(), changeEffectiveDate, utcNow, true, context);
                         break;
 
                     case STOP_BILLING:
                         final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
-                        DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate();
+                        DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
                         if (dryRunArguments.getEffectiveDate() == null) {
                             BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
                             if (policy == null) {
 
                                 final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
-                                final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                                       currentPlan.getProduct().getCategory(),
-                                                                                       subscriptionForCancellation.getCurrentPlan().getRecurringBillingPeriod(),
-                                                                                       subscriptionForCancellation.getCurrentPriceList().getName(),
+                                final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(),
                                                                                        subscriptionForCancellation.getCurrentPhase().getPhaseType());
-                                policy = catalogService.getFullCatalog(context).planCancelPolicy(spec, utcNow);
+                                policy = catalogService.getFullCatalog(true, true, context).planCancelPolicy(spec, utcNow);
                             }
                             cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy);
                         }
@@ -601,7 +700,15 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         final Iterable<SubscriptionBaseEvent> filteredEvents = Iterables.filter(events, new Predicate<SubscriptionBaseEvent>() {
             @Override
             public boolean apply(final SubscriptionBaseEvent input) {
-                return (eventType == SubscriptionBaseTransitionType.PHASE && input.getType() == EventType.PHASE) || input.getType() != EventType.PHASE;
+                switch (input.getType()) {
+                    case PHASE:
+                        return eventType == SubscriptionBaseTransitionType.PHASE;
+                    case BCD_UPDATE:
+                        return eventType == SubscriptionBaseTransitionType.BCD_CHANGE;
+                    case API_USER:
+                    default:
+                        return true;
+                }
             }
         });
         final Map<UUID, DateTime> result = filteredEvents.iterator().hasNext() ? new HashMap<UUID, DateTime>() : ImmutableMap.<UUID, DateTime>of();
@@ -614,12 +721,63 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         return result;
     }
 
+    @Override
+    public void updateBCD(final UUID subscriptionId, final int bcd, @Nullable final LocalDate effectiveFromDate, final InternalCallContext internalCallContext) throws SubscriptionBaseApiException {
+        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) getSubscriptionFromId(subscriptionId, internalCallContext);
+        final DateTime effectiveDate = getEffectiveDateForNewBCD(bcd, effectiveFromDate, internalCallContext);
+        final BCDEvent bcdEvent = BCDEventData.createBCDEvent(subscription, effectiveDate, bcd);
+        dao.createBCDChangeEvent(subscription, bcdEvent, internalCallContext);
+    }
+
+    @Override
+    public int getDefaultBillCycleDayLocal(final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal, final DateTime effectiveDate, final InternalTenantContext context) throws SubscriptionBaseApiException {
+
+        try {
+            final Catalog catalog = catalogService.getFullCatalog(true, true, context);
+            final BillingAlignment alignment = catalog.billingAlignment(planPhaseSpecifier, effectiveDate);
+            return BillCycleDayCalculator.calculateBcdForAlignment(subscription, baseSubscription, alignment, accountTimeZone, accountBillCycleDayLocal);
+        } catch (final CatalogApiException e) {
+            throw new SubscriptionBaseApiException(e);
+        }
+    }
+
+    private DateTime getEffectiveDateForNewBCD(final int bcd, @Nullable final LocalDate effectiveFromDate, final InternalCallContext internalCallContext) {
+        if (internalCallContext.getAccountRecordId() == null) {
+            throw new IllegalStateException("Need to have a valid context with accountRecordId");
+        }
+
+        // Today as seen by this account
+        final LocalDate startDate = effectiveFromDate != null ? effectiveFromDate : internalCallContext.toLocalDate(clock.getUTCNow());
+
+        // We want to compute a LocalDate in account TZ which maps to the provided 'bcd' and then compute an effectiveDate for when that BCD_CHANGE event needs to be triggered
+        //
+        // There is a bit of complexity to make sure the date we chose exists (e.g: a BCD of 31 in a february month would not make sense).
+        final int currentDay = startDate.getDayOfMonth();
+        final int lastDayOfMonth = startDate.dayOfMonth().getMaximumValue();
+
+        final LocalDate requestedDate;
+        if (bcd < currentDay) {
+            final LocalDate startDatePlusOneMonth = startDate.plusMonths(1);
+            final int lastDayOfNextMonth = startDatePlusOneMonth.dayOfMonth().getMaximumValue();
+            final int originalBCDORLastDayOfMonth = bcd <= lastDayOfNextMonth ? bcd : lastDayOfNextMonth;
+            requestedDate = new LocalDate(startDatePlusOneMonth.getYear(), startDatePlusOneMonth.getMonthOfYear(), originalBCDORLastDayOfMonth);
+        } else if (bcd == currentDay) {
+            // will default to immediate event
+            requestedDate = null;
+        } else if (bcd <= lastDayOfMonth) {
+            requestedDate = new LocalDate(startDate.getYear(), startDate.getMonthOfYear(), bcd);
+        } else /* bcd > lastDayOfMonth && bcd > currentDay */ {
+            requestedDate = new LocalDate(startDate.getYear(), startDate.getMonthOfYear(), lastDayOfMonth);
+        }
+        return requestedDate == null ? clock.getUTCNow() : internalCallContext.toUTCDateTime(requestedDate);
+    }
+
     private DateTime getBundleStartDateWithSanity(final UUID bundleId, @Nullable final DefaultSubscriptionBase baseSubscription, final Plan plan,
                                                   final DateTime effectiveDate, final InternalTenantContext context) throws SubscriptionBaseApiException, CatalogApiException {
         switch (plan.getProduct().getCategory()) {
             case BASE:
                 if (baseSubscription != null &&
-                    baseSubscription.getState() == EntitlementState.ACTIVE) {
+                    (baseSubscription.getState() == EntitlementState.ACTIVE || baseSubscription.getState() == EntitlementState.PENDING)) {
                     throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
                 }
                 return effectiveDate;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
index fc33b62..9c677fd 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimeline.java
@@ -37,6 +37,7 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionData;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
+import org.killbill.billing.subscription.events.bcd.BCDEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventType;
@@ -45,12 +46,10 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
 
     private final UUID id;
     private final List<ExistingEvent> existingEvents;
-    private final long activeVersion;
 
     public DefaultSubscriptionBaseTimeline(final DefaultSubscriptionBase input, final Catalog catalog) throws CatalogApiException {
         this.id = input.getId();
-        this.existingEvents = toExistingEvents(catalog, input.getActiveVersion(), input.getCategory(), input.getEvents());
-        this.activeVersion = input.getActiveVersion();
+        this.existingEvents = toExistingEvents(catalog, input.getCategory(), input.getEvents());
     }
 
     private BillingPeriod getBillingPeriod(final Catalog catalog, @Nullable final String phaseName, final DateTime effectiveDate, DateTime startDate) throws CatalogApiException {
@@ -61,7 +60,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
         return phase.getRecurring() != null ? phase.getRecurring().getBillingPeriod() : BillingPeriod.NO_BILLING_PERIOD;
     }
 
-    private List<ExistingEvent> toExistingEvents(final Catalog catalog, final long activeVersion, final ProductCategory category, final List<SubscriptionBaseEvent> events)
+    private List<ExistingEvent> toExistingEvents(final Catalog catalog, final ProductCategory category, final List<SubscriptionBaseEvent> events)
             throws CatalogApiException {
 
         final List<ExistingEvent> result = new LinkedList<SubscriptionBaseTimeline.ExistingEvent>();
@@ -76,11 +75,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
 
         for (final SubscriptionBaseEvent cur : events) {
 
-            // First active event is used to figure out which catalog version to use.
-            //startDate = (startDate == null && cur.getActiveVersion() == activeVersion) ?  cur.getEffectiveDate() : startDate;
-
-            // STEPH that needs to be reviewed if we support multi version events
-            if (cur.getActiveVersion() != activeVersion || !cur.isActive()) {
+            if (!cur.isActive()) {
                 continue;
             }
             startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
@@ -91,6 +86,7 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
             PhaseType phaseType = null;
             String planName = null;
             String planPhaseName = null;
+            Integer billCycleDayLocal = null;
 
             ApiEventType apiType = null;
             switch (cur.getType()) {
@@ -105,6 +101,11 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                     priceListName = prevPriceListName;
                     break;
 
+                case BCD_UPDATE:
+                    final BCDEvent bcdEvent = (BCDEvent) cur;
+                    billCycleDayLocal = bcdEvent.getBillCycleDayLocal();
+                    break;
+
                 case API_USER:
                     final ApiEvent userEV = (ApiEvent) cur;
                     apiType = userEV.getApiEventType();
@@ -122,7 +123,8 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
 
             final String planNameWithClosure = planName;
             final String planPhaseNameWithClosure = planPhaseName;
-            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+            final Integer billCycleDayLocalWithClosure = billCycleDayLocal;
+            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, phaseType);
             result.add(new ExistingEvent() {
                 @Override
                 public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
@@ -130,8 +132,8 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                 }
 
                 @Override
-                public DateTime getRequestedDate() {
-                    return cur.getEffectiveDate();
+                public ProductCategory getProductCategory() {
+                    return category;
                 }
 
                 @Override
@@ -158,6 +160,11 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
                 public String getPlanPhaseName() {
                     return planPhaseNameWithClosure;
                 }
+
+                @Override
+                public Integer getBillCycleDayLocal() {
+                    return billCycleDayLocalWithClosure;
+                }
             });
 
             prevPlanName = planName;
@@ -191,11 +198,6 @@ public class DefaultSubscriptionBaseTimeline implements SubscriptionBaseTimeline
         return existingEvents;
     }
 
-    @Override
-    public long getActiveVersion() {
-        return activeVersion;
-    }
-
     private void sortExistingEvent(final List<ExistingEvent> events) {
         if (events != null) {
             Collections.sort(events, new Comparator<ExistingEvent>() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
index 4dca0a1..49a7ae7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/timeline/DefaultSubscriptionBaseTimelineApi.java
@@ -136,7 +136,7 @@ public class DefaultSubscriptionBaseTimelineApi extends SubscriptionApiBase impl
 
         for (final SubscriptionBase cur : subscriptions) {
             if (!repairIds.contains(cur.getId())) {
-                result.add(new DefaultSubscriptionBaseTimeline((DefaultSubscriptionBase) cur, catalogService.getFullCatalog(tenantContext)));
+                result.add(new DefaultSubscriptionBaseTimeline((DefaultSubscriptionBase) cur, catalogService.getFullCatalog(true, true, tenantContext)));
             }
         }
         return result;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
index 49c3ca6..bd62893 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/transfer/DefaultSubscriptionBaseTransferApi.java
@@ -37,8 +37,6 @@ import org.killbill.clock.Clock;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.subscription.api.SubscriptionApiBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
 import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
 import org.killbill.billing.subscription.api.timeline.BundleBaseTimeline;
 import org.killbill.billing.subscription.api.timeline.SubscriptionBaseRepairException;
@@ -85,7 +83,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
         SubscriptionBaseEvent newEvent = null;
 
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
 
         final DateTime effectiveDate = existingEvent.getEffectiveDate().isBefore(transferDate) ? transferDate : existingEvent.getEffectiveDate();
 
@@ -101,14 +99,11 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                 .setEventPlan(existingEvent.getPlanName())
                 .setEventPlanPhase(currentPhase.getName())
                 .setEventPriceList(spec.getPriceListName())
-                .setActiveVersion(subscription.getActiveVersion())
                 .setEffectiveDate(effectiveDate)
                 .setFromDisk(true);
 
         switch (existingEvent.getSubscriptionTransitionType()) {
             case TRANSFER:
-            case MIGRATE_ENTITLEMENT:
-            case RE_CREATE:
             case CREATE:
                 newEvent = new ApiEventTransfer(apiBuilder);
                 break;
@@ -120,15 +115,9 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
             case PHASE:
                 newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) :
-                           PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), currentPhase.getName(), effectiveDate);
+                           PhaseEventData.createNextPhaseEvent(subscription.getId(), currentPhase.getName(), effectiveDate);
                 break;
 
-            // Ignore these events except if it's the first event for the new subscription
-            case MIGRATE_BILLING:
-                if (firstEvent) {
-                    newEvent = new ApiEventTransfer(apiBuilder);
-                }
-                break;
             case CANCEL:
                 break;
 
@@ -213,7 +202,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
             final DefaultSubscriptionBaseBundle subscriptionBundleData = new DefaultSubscriptionBaseBundle(bundleKey, destAccountId, effectiveTransferDate,
                                                                                                            bundle.getOriginalCreatedDate(), clock.getUTCNow(), clock.getUTCNow());
-            final List<SubscriptionMigrationData> subscriptionMigrationDataList = new LinkedList<SubscriptionMigrationData>();
+            final List<SubscriptionTransferData> subscriptionTransferDataList = new LinkedList<SubscriptionTransferData>();
 
             final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
 
@@ -226,7 +215,7 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                     continue;
                 }
                 final List<ExistingEvent> existingEvents = cur.getExistingEvents();
-                final ProductCategory productCategory = existingEvents.get(0).getPlanPhaseSpecifier().getProductCategory();
+                final ProductCategory productCategory = existingEvents.get(0).getProductCategory();
 
                 // For future add-on cancellations, don't add a cancellation on disk right away (mirror the behavior
                 // on base plan cancellations, even though we don't support un-transfer today)
@@ -238,11 +227,10 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
 
                     final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
                                                                                          .setSubscriptionId(cur.getId())
-                                                                                         .setActiveVersion(cur.getActiveVersion())
                                                                                          .setEffectiveDate(effectiveCancelDate)
                                                                                          .setFromDisk(true));
 
-                    TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
+                    final TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
                     transferCancelDataList.add(cancelData);
                 }
 
@@ -266,15 +254,15 @@ public class DefaultSubscriptionBaseTransferApi extends SubscriptionApiBase impl
                                                                                                     ImmutableList.<SubscriptionBaseEvent>of(), fromInternalCallContext);
 
                 final List<SubscriptionBaseEvent> events = toEvents(existingEvents, defaultSubscriptionBase, effectiveTransferDate, fromInternalCallContext);
-                final SubscriptionMigrationData curData = new SubscriptionMigrationData(defaultSubscriptionBase, events, null);
-                subscriptionMigrationDataList.add(curData);
+                final SubscriptionTransferData curData = new SubscriptionTransferData(defaultSubscriptionBase, events, null);
+                subscriptionTransferDataList.add(curData);
             }
-            BundleMigrationData bundleMigrationData = new BundleMigrationData(subscriptionBundleData, subscriptionMigrationDataList);
+            BundleTransferData bundleTransferData = new BundleTransferData(subscriptionBundleData, subscriptionTransferDataList);
 
             // Atomically cancelWithRequestedDate all subscription on old account and create new bundle, subscriptions, events for new account
-            dao.transfer(sourceAccountId, destAccountId, bundleMigrationData, transferCancelDataList, fromInternalCallContext, toInternalCallContext);
+            dao.transfer(sourceAccountId, destAccountId, bundleTransferData, transferCancelDataList, fromInternalCallContext, toInternalCallContext);
 
-            return bundleMigrationData.getData();
+            return bundleTransferData.getData();
         } catch (SubscriptionBaseRepairException e) {
             throw new SubscriptionBaseTransferApiException(e);
         } catch (CatalogApiException e) {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
index aca165c..4e4d7b7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultEffectiveSubscriptionEvent.java
@@ -42,10 +42,12 @@ public class DefaultEffectiveSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("previousPlan") final String previousPlan,
                                              @JsonProperty("previousPhase") final String previousPhase,
                                              @JsonProperty("previousPriceList") final String previousPriceList,
+                                             @JsonProperty("previousBillCycleDayLocal") final Integer previousBillCycleDayLocal,
                                              @JsonProperty("nextState") final EntitlementState nextState,
                                              @JsonProperty("nextPlan") final String nextPlan,
                                              @JsonProperty("nextPhase") final String nextPhase,
                                              @JsonProperty("nextPriceList") final String nextPriceList,
+                                             @JsonProperty("nextBillCycleDayLocal") final Integer nextBillCycleDayLocal,
                                              @JsonProperty("totalOrdering") final Long totalOrdering,
                                              @JsonProperty("transitionType") final SubscriptionBaseTransitionType transitionType,
                                              @JsonProperty("remainingEventsForUserOperation") final Integer remainingEventsForUserOperation,
@@ -54,7 +56,7 @@ public class DefaultEffectiveSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("searchKey2") final Long searchKey2,
                                              @JsonProperty("userToken") final UUID userToken) {
         super(eventId, subscriptionId, bundleId, effectiveTransitionTime, effectiveTransitionTime, previousState, previousPlan,
-              previousPhase, previousPriceList, nextState, nextPlan, nextPhase, nextPriceList, totalOrdering,
+              previousPhase, previousPriceList, previousBillCycleDayLocal, nextState, nextPlan, nextPhase, nextPriceList, nextBillCycleDayLocal, totalOrdering,
               transitionType, remainingEventsForUserOperation, startDate, searchKey1, searchKey2, userToken);
     }
 }
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 2c78684..1e918ef 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
@@ -40,10 +40,12 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("previousPlan") final String previousPlan,
                                              @JsonProperty("previousPhase") final String previousPhase,
                                              @JsonProperty("previousPriceList") final String previousPriceList,
+                                             @JsonProperty("previousBillCycleDayLocal") final Integer previousBillCycleDayLocal,
                                              @JsonProperty("nextState") final EntitlementState nextState,
                                              @JsonProperty("nextPlan") final String nextPlan,
                                              @JsonProperty("nextPhase") final String nextPhase,
                                              @JsonProperty("nextPriceList") final String nextPriceList,
+                                             @JsonProperty("nextBillCycleDayLocal") final Integer nextBillCycleDayLocal,
                                              @JsonProperty("totalOrdering") final Long totalOrdering,
                                              @JsonProperty("transitionType") final SubscriptionBaseTransitionType transitionType,
                                              @JsonProperty("remainingEventsForUserOperation") final Integer remainingEventsForUserOperation,
@@ -52,7 +54,7 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
                                              @JsonProperty("searchKey2") final Long searchKey2,
                                              @JsonProperty("userToken") final UUID userToken) {
         super(eventId, subscriptionId, bundleId, requestedTransitionTime, effectiveTransitionTime, previousState, previousPlan,
-              previousPhase, previousPriceList, nextState, nextPlan, nextPhase, nextPriceList, totalOrdering,
+              previousPhase, previousPriceList, previousBillCycleDayLocal, nextState, nextPlan, nextPhase, nextPriceList, nextBillCycleDayLocal, totalOrdering,
               transitionType, remainingEventsForUserOperation, startDate, searchKey1, searchKey2, userToken);
     }
 
@@ -63,6 +65,6 @@ public class DefaultRequestedSubscriptionEvent extends DefaultSubscriptionEvent 
                                              final Long searchKey2,
                                              final UUID userToken) {
         this(nextEvent.getId(), nextEvent.getSubscriptionId(), subscription.getBundleId(), nextEvent.getEffectiveDate(), nextEvent.getEffectiveDate(),
-             null, null, null, null, null, null, null, null, nextEvent.getTotalOrdering(), transitionType, 0, null, searchKey1, searchKey2, userToken);
+             null, null, 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/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index 6f04266..90a581e 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -20,6 +20,8 @@ package org.killbill.billing.subscription.api.user;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
@@ -31,10 +33,11 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
+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.catalog.api.PlanPhasePriceOverride;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
@@ -44,12 +47,12 @@ import org.killbill.billing.entity.EntityBase;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionDataIterator.Kind;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionDataIterator.Order;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionDataIterator.TimeLimit;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransitionDataIterator.Visibility;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
+import org.killbill.billing.subscription.events.bcd.BCDEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventType;
@@ -59,6 +62,9 @@ import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 public class DefaultSubscriptionBase extends EntityBase implements SubscriptionBase {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultSubscriptionBase.class);
@@ -73,12 +79,12 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     private final DateTime alignStartDate;
     private final DateTime bundleStartDate;
     private final ProductCategory category;
+    private final boolean migrated;
 
     //
     // Those can be modified through non User APIs, and a new SubscriptionBase
     // object would be created
     //
-    private final long activeVersion;
     private final DateTime chargedThroughDate;
 
     //
@@ -108,8 +114,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.alignStartDate = builder.getAlignStartDate();
         this.bundleStartDate = builder.getBundleStartDate();
         this.category = builder.getCategory();
-        this.activeVersion = builder.getActiveVersion();
         this.chargedThroughDate = builder.getChargedThroughDate();
+        this.migrated = builder.isMigrated();
     }
 
     // Used for API to make sure we have a clock and an apiService set before we return the object
@@ -121,8 +127,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         this.alignStartDate = internalSubscription.getAlignStartDate();
         this.bundleStartDate = internalSubscription.getBundleStartDate();
         this.category = internalSubscription.getCategory();
-        this.activeVersion = internalSubscription.getActiveVersion();
         this.chargedThroughDate = internalSubscription.getChargedThroughDate();
+        this.migrated = internalSubscription.isMigrated();
         this.transitions = new LinkedList<SubscriptionBaseTransition>(internalSubscription.getAllTransitions());
         this.events = internalSubscription.getEvents();
     }
@@ -139,8 +145,19 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
     @Override
     public EntitlementState getState() {
-        return (getPreviousTransition() == null) ? null
-                                                 : getPreviousTransition().getNextState();
+
+        final SubscriptionBaseTransition previousTransition = getPreviousTransition();
+        if (previousTransition != null) {
+            return previousTransition.getNextState();
+        }
+
+        final SubscriptionBaseTransition pendingTransition = getPendingTransition();
+        if (pendingTransition != null &&
+            (pendingTransition.getTransitionType().equals(SubscriptionBaseTransitionType.CREATE) ||
+             pendingTransition.getTransitionType().equals(SubscriptionBaseTransitionType.TRANSFER))) {
+            return EntitlementState.PENDING;
+        }
+        throw new IllegalStateException("Should return a valid EntitlementState");
     }
 
     @Override
@@ -148,15 +165,16 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         if (transitions == null) {
             return null;
         }
-        final SubscriptionBaseTransitionData initialTransition = (SubscriptionBaseTransitionData) transitions.get(0);
-        switch (initialTransition.getApiEventType()) {
-            case MIGRATE_BILLING:
-            case MIGRATE_ENTITLEMENT:
-                return EntitlementSourceType.MIGRATED;
-            case TRANSFER:
-                return EntitlementSourceType.TRANSFERRED;
-            default:
-                return EntitlementSourceType.NATIVE;
+        if (isMigrated()) {
+            return EntitlementSourceType.MIGRATED;
+        } else {
+            final SubscriptionBaseTransitionData initialTransition = (SubscriptionBaseTransitionData) transitions.get(0);
+            switch (initialTransition.getApiEventType()) {
+                case TRANSFER:
+                    return EntitlementSourceType.TRANSFERRED;
+                default:
+                    return EntitlementSourceType.NATIVE;
+            }
         }
     }
 
@@ -195,7 +213,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         }
 
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.ASC_FROM_PAST, Kind.SUBSCRIPTION,
+                clock, transitions, Order.ASC_FROM_PAST,
                 Visibility.ALL, TimeLimit.FUTURE_ONLY);
         while (it.hasNext()) {
             final SubscriptionBaseTransition cur = it.next();
@@ -206,11 +224,6 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return null;
     }
 
-    public boolean recreate(final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate,
-                            final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.recreatePlan(this, spec, overrides, requestedDate, context);
-    }
-
     @Override
     public boolean cancel(final CallContext context) throws SubscriptionBaseApiException {
         return apiService.cancel(this, context);
@@ -233,21 +246,21 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     }
 
     @Override
-    public DateTime changePlan(final String productName, final BillingPeriod term, final String priceList,
+    public DateTime changePlan(final PlanSpecifier spec,
                                final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.changePlan(this, productName, term, priceList, overrides, context);
+        return apiService.changePlan(this, spec, overrides, context);
     }
 
     @Override
-    public DateTime changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides,
+    public DateTime changePlanWithDate(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides,
                                        final DateTime requestedDate, final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.changePlanWithRequestedDate(this, productName, term, priceList, overrides, requestedDate, context);
+        return apiService.changePlanWithRequestedDate(this, spec, overrides, requestedDate, context);
     }
 
     @Override
-    public DateTime changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
-                                         final List<PlanPhasePriceOverride> overrides, final BillingActionPolicy policy,  final CallContext context) throws SubscriptionBaseApiException {
-        return apiService.changePlanWithPolicy(this, productName, term, priceList, overrides, policy, context);
+    public DateTime changePlanWithPolicy(final PlanSpecifier spec,
+                                         final List<PlanPhasePriceOverride> overrides, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+        return apiService.changePlanWithPolicy(this, spec, overrides, policy, context);
     }
 
     @Override
@@ -256,7 +269,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             return null;
         }
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.ASC_FROM_PAST, Kind.SUBSCRIPTION,
+                clock, transitions, Order.ASC_FROM_PAST,
                 Visibility.ALL, TimeLimit.FUTURE_ONLY);
         return it.hasNext() ? it.next() : null;
     }
@@ -333,8 +346,9 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             return null;
         }
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.DESC_FROM_FUTURE, Kind.SUBSCRIPTION,
+                clock, transitions, Order.DESC_FROM_FUTURE,
                 Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
+
         return it.hasNext() ? it.next() : null;
     }
 
@@ -343,6 +357,21 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return category;
     }
 
+    @Override
+    public Integer getBillCycleDayLocal() {
+
+        final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
+                clock, transitions, Order.DESC_FROM_FUTURE,
+                Visibility.FROM_DISK_ONLY, TimeLimit.PAST_OR_PRESENT_ONLY);
+        while (it.hasNext()) {
+            final SubscriptionBaseTransition cur = it.next();
+            if (cur.getTransitionType() == SubscriptionBaseTransitionType.BCD_CHANGE) {
+                return cur.getNextBillingCycleDayLocal();
+            }
+        }
+        return null;
+    }
+
     public DateTime getBundleStartDate() {
         return bundleStartDate;
     }
@@ -353,12 +382,17 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
     }
 
     @Override
+    public boolean isMigrated() {
+        return migrated;
+    }
+
+    @Override
     public List<SubscriptionBaseTransition> getAllTransitions() {
         if (transitions == null) {
             return Collections.emptyList();
         }
         final List<SubscriptionBaseTransition> result = new ArrayList<SubscriptionBaseTransition>();
-        final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(clock, transitions, Order.ASC_FROM_PAST, Kind.ALL, Visibility.ALL, TimeLimit.ALL);
+        final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(clock, transitions, Order.ASC_FROM_PAST, Visibility.ALL, TimeLimit.ALL);
         while (it.hasNext()) {
             result.add(it.next());
         }
@@ -396,6 +430,14 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         return true;
     }
 
+    @Override
+    public DateTime getDateOfFirstRecurringNonZeroCharge() {
+        final Plan initialPlan = !transitions.isEmpty() ? transitions.get(0).getNextPlan() : null;
+        final PlanPhase initialPhase = !transitions.isEmpty() ? transitions.get(0).getNextPhase() : null;
+        final PhaseType initialPhaseType = initialPhase != null ? initialPhase.getPhaseType() : null;
+        return initialPlan.dateOfFirstRecurringNonZeroCharge(getStartDate(), initialPhaseType);
+    }
+
     public SubscriptionBaseTransitionData getTransitionFromEvent(final SubscriptionBaseEvent event, final int seqId) {
         if (transitions == null || event == null) {
             return null;
@@ -427,15 +469,11 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
     public long getLastEventOrderedId() {
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.DESC_FROM_FUTURE, Kind.SUBSCRIPTION,
+                clock, transitions, Order.DESC_FROM_FUTURE,
                 Visibility.FROM_DISK_ONLY, TimeLimit.ALL);
         return it.hasNext() ? ((SubscriptionBaseTransitionData) it.next()).getTotalOrdering() : -1L;
     }
 
-    public long getActiveVersion() {
-        return activeVersion;
-    }
-
     public List<SubscriptionBaseTransition> getBillingTransitions() {
 
         if (transitions == null) {
@@ -443,16 +481,15 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         }
         final List<SubscriptionBaseTransition> result = new ArrayList<SubscriptionBaseTransition>();
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.ASC_FROM_PAST, Kind.BILLING,
+                clock, transitions, Order.ASC_FROM_PAST,
                 Visibility.ALL, TimeLimit.ALL);
-        // Remove anything prior to first CREATE or MIGRATE_BILLING
+        // Remove anything prior to first CREATE
         boolean foundInitialEvent = false;
         while (it.hasNext()) {
             final SubscriptionBaseTransitionData curTransition = (SubscriptionBaseTransitionData) it.next();
             if (!foundInitialEvent) {
                 foundInitialEvent = curTransition.getEventType() == EventType.API_USER &&
                                     (curTransition.getApiEventType() == ApiEventType.CREATE ||
-                                     curTransition.getApiEventType() == ApiEventType.MIGRATE_BILLING ||
                                      curTransition.getApiEventType() == ApiEventType.TRANSFER);
             }
             if (foundInitialEvent) {
@@ -470,17 +507,14 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(clock,
                                                                                                      transitions,
                                                                                                      Order.DESC_FROM_FUTURE,
-                                                                                                     Kind.SUBSCRIPTION,
                                                                                                      Visibility.ALL,
                                                                                                      TimeLimit.PAST_OR_PRESENT_ONLY);
 
         while (it.hasNext()) {
             final SubscriptionBaseTransitionData cur = (SubscriptionBaseTransitionData) it.next();
             if (cur.getTransitionType() == SubscriptionBaseTransitionType.CREATE
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE
                 || cur.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+                || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE) {
                 return cur;
             }
         }
@@ -497,7 +531,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         final DateTime candidateResult;
         switch (policy) {
             case IMMEDIATE:
-                candidateResult =  clock.getUTCNow();
+                candidateResult = clock.getUTCNow();
                 break;
             case END_OF_TERM:
                 //
@@ -506,7 +540,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
                 // 1. account is not being invoiced, for e.g AUTO_INVOICING_OFF nis set
                 // 2. In the case if FIXED item CTD is set using startDate of the service period
                 //
-                candidateResult =  (chargedThroughDate != null && chargedThroughDate.isAfter(clock.getUTCNow())) ? chargedThroughDate : clock.getUTCNow();
+                candidateResult = (chargedThroughDate != null && chargedThroughDate.isAfter(clock.getUTCNow())) ? chargedThroughDate : clock.getUTCNow();
                 break;
             default:
                 throw new SubscriptionBaseError(String.format(
@@ -522,7 +556,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
                     "No transitions for subscription %s", getId()));
         }
         final SubscriptionBaseTransitionDataIterator it = new SubscriptionBaseTransitionDataIterator(
-                clock, transitions, Order.DESC_FROM_FUTURE, Kind.SUBSCRIPTION,
+                clock, transitions, Order.DESC_FROM_FUTURE,
                 Visibility.ALL, TimeLimit.PAST_OR_PRESENT_ONLY);
         while (it.hasNext()) {
             final SubscriptionBaseTransitionData cur = (SubscriptionBaseTransitionData) it.next();
@@ -530,9 +564,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             if (cur.getTransitionType() == SubscriptionBaseTransitionType.PHASE
                 || cur.getTransitionType() == SubscriptionBaseTransitionType.TRANSFER
                 || cur.getTransitionType() == SubscriptionBaseTransitionType.CREATE
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.RE_CREATE
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE
-                || cur.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT) {
+                || cur.getTransitionType() == SubscriptionBaseTransitionType.CHANGE) {
                 return cur.getEffectiveTransitionTime();
             }
         }
@@ -548,6 +580,8 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
         this.events = inputEvents;
 
+        filterOutDuplicateCancelEvents(events);
+
         UUID nextUserToken = null;
 
         UUID nextEventId = null;
@@ -555,7 +589,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         EntitlementState nextState = null;
         String nextPlanName = null;
         String nextPhaseName = null;
-        String nextPriceListName = null;
+        Integer nextBillingCycleDayLocal = null;
 
         UUID prevEventId = null;
         DateTime prevCreatedDate = null;
@@ -563,17 +597,18 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
         PriceList previousPriceList = null;
         Plan previousPlan = null;
         PlanPhase previousPhase = null;
+        Integer previousBillingCycleDayLocal = null;
 
         transitions = new LinkedList<SubscriptionBaseTransition>();
 
         for (final SubscriptionBaseEvent cur : inputEvents) {
 
-            if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
+            if (!cur.isActive()) {
                 continue;
             }
 
-            ApiEventType apiEventType = null;
 
+            ApiEventType apiEventType = null;
             boolean isFromDisk = true;
 
             nextEventId = cur.getId();
@@ -586,6 +621,11 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
                     nextPhaseName = phaseEV.getPhase();
                     break;
 
+                case BCD_UPDATE:
+                    final BCDEvent bcdEvent = (BCDEvent) cur;
+                    nextBillingCycleDayLocal = bcdEvent.getBillCycleDayLocal();
+                    break;
+
                 case API_USER:
                     final ApiEvent userEV = (ApiEvent) cur;
                     apiEventType = userEV.getApiEventType();
@@ -593,10 +633,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
                     switch (apiEventType) {
                         case TRANSFER:
-                        case MIGRATE_BILLING:
-                        case MIGRATE_ENTITLEMENT:
                         case CREATE:
-                        case RE_CREATE:
                             prevEventId = null;
                             prevCreatedDate = null;
                             previousState = null;
@@ -606,12 +643,10 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
                             nextState = EntitlementState.ACTIVE;
                             nextPlanName = userEV.getEventPlan();
                             nextPhaseName = userEV.getEventPlanPhase();
-                            nextPriceListName = userEV.getPriceList();
                             break;
                         case CHANGE:
                             nextPlanName = userEV.getEventPlan();
                             nextPhaseName = userEV.getEventPlanPhase();
-                            nextPriceListName = userEV.getPriceList();
                             break;
                         case CANCEL:
                             nextState = EntitlementState.CANCELLED;
@@ -636,10 +671,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
 
             nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : null;
             nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getEffectiveDate(), getAlignStartDate()) : null;
-
-            // See issue https://github.com/killbill/killbill/issues/464
-            final DateTime catalogEffectiveDateForPriceList = transitions.isEmpty() ? cur.getEffectiveDate() : transitions.get(0).getEffectiveTransitionTime();
-            nextPriceList = (nextPriceListName != null) ? catalog.findPriceList(nextPriceListName, catalogEffectiveDateForPriceList) : null;
+            nextPriceList = (nextPlan != null) ? catalog.findPriceListForPlan(nextPlanName, cur.getEffectiveDate(), getAlignStartDate()) : null;
 
             final SubscriptionBaseTransitionData transition = new SubscriptionBaseTransitionData(
                     cur.getId(), id, bundleId, cur.getType(), apiEventType,
@@ -647,9 +679,12 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
                     prevEventId, prevCreatedDate,
                     previousState, previousPlan, previousPhase,
                     previousPriceList,
+                    previousBillingCycleDayLocal,
                     nextEventId, nextCreatedDate,
                     nextState, nextPlan, nextPhase,
-                    nextPriceList, cur.getTotalOrdering(),
+                    nextPriceList,
+                    nextBillingCycleDayLocal,
+                    cur.getTotalOrdering(),
                     cur.getCreatedDate(),
                     nextUserToken,
                     isFromDisk);
@@ -662,7 +697,72 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             previousPriceList = nextPriceList;
             prevEventId = nextEventId;
             prevCreatedDate = nextCreatedDate;
+            previousBillingCycleDayLocal = nextBillingCycleDayLocal;
+
+        }
+    }
 
+    //
+    // Hardening against data integrity issues where we have multiple active CANCEL (See #619):
+    // We skip any cancel events after the first one (subscription cannot be cancelled multiple times).
+    // The code should prevent such cases from happening but because of #619, some invalid data could be there so to be safe we added this code
+    //
+    // Also we remove !onDisk cancel events when there is an onDisk cancel event (can happen during the path where we process the base plan cancel notification, and are
+    // in the process of adding the new cancel events for the AO)
+    //
+    private void filterOutDuplicateCancelEvents(final List<SubscriptionBaseEvent> inputEvents) {
+
+        Collections.sort(inputEvents, new Comparator<SubscriptionBaseEvent>() {
+            @Override
+            public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
+                int res = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+                if (res == 0) {
+                    res = o1.getTotalOrdering() < (o2.getTotalOrdering()) ? -1 : 1;
+                }
+                return res;
+            }
+        });
+
+        final boolean isCancelled = Iterables.any(inputEvents, new Predicate<SubscriptionBaseEvent>() {
+            @Override
+            public boolean apply(final SubscriptionBaseEvent input) {
+                if (input.isActive() && input.getType() == EventType.API_USER) {
+                    final ApiEvent userEV = (ApiEvent) input;
+                    if (userEV.getApiEventType() == ApiEventType.CANCEL && userEV.isFromDisk()) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        });
+
+        if (!isCancelled) {
+            return;
+        }
+
+
+        boolean foundFirstOnDiskCancel = false;
+        final Iterator<SubscriptionBaseEvent> it =  inputEvents.iterator();
+        while(it.hasNext()) {
+            final SubscriptionBaseEvent input = it.next();
+            if (!input.isActive()) {
+                continue;
+            }
+
+            if (input.getType() == EventType.API_USER) {
+                final ApiEvent userEV = (ApiEvent) input;
+                if (userEV.getApiEventType() == ApiEventType.CANCEL) {
+                    if (userEV.isFromDisk()) {
+                        if (!foundFirstOnDiskCancel) {
+                            foundFirstOnDiskCancel = true;
+                        } else {
+                            it.remove();
+                        }
+                    } else {
+                        it.remove();
+                    }
+                }
+            }
         }
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index b12935f..0fee918 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -41,13 +41,11 @@ import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanChangeResult;
-import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceList;
-import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -66,9 +64,7 @@ import org.killbill.billing.subscription.events.user.ApiEventBuilder;
 import org.killbill.billing.subscription.events.user.ApiEventCancel;
 import org.killbill.billing.subscription.events.user.ApiEventChange;
 import org.killbill.billing.subscription.events.user.ApiEventCreate;
-import org.killbill.billing.subscription.events.user.ApiEventReCreate;
 import org.killbill.billing.subscription.events.user.ApiEventUncancel;
-import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
@@ -108,12 +104,21 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                               final CallContext context) throws SubscriptionBaseApiException {
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder, this, clock);
 
-        createFromSubscription(subscription, plan, initialPhase, realPriceList, effectiveDate, processedDate, false, context);
-        return subscription;
+        final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
+
+        try {
+            final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getBundleId(), subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(),
+                                                                           plan, initialPhase, realPriceList, effectiveDate, processedDate, internalCallContext);
+            dao.createSubscription(subscription, events, internalCallContext);
+            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(true, true, internalCallContext));
+            return subscription;
+        } catch (final CatalogApiException e) {
+            throw new SubscriptionBaseApiException(e);
+        }
     }
 
     @Override
-    public DefaultSubscriptionBase createPlans(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context) throws SubscriptionBaseApiException {
+    public List<DefaultSubscriptionBase> createPlans(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context) throws SubscriptionBaseApiException {
 
         Map<UUID, List<SubscriptionBaseEvent>> eventsMap = new HashMap<UUID, List<SubscriptionBaseEvent>>();
         List<DefaultSubscriptionBase> subscriptionBaseList = new ArrayList<DefaultSubscriptionBase>();
@@ -123,10 +128,9 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(subscription.getBuilder(), this, clock);
                 final InternalCallContext internalCallContext = createCallContextFromBundleId(subscriptionBase.getBundleId(), context);
                 final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscriptionBase.getBundleId(), subscriptionBase.getId(), subscriptionBase.getAlignStartDate(),
-                                                                               subscriptionBase.getBundleStartDate(), subscriptionBase.getActiveVersion(), subscription.getPlan(),
+                                                                               subscriptionBase.getBundleStartDate(), subscription.getPlan(),
                                                                                subscription.getInitialPhase(), subscription.getRealPriceList(),
-                                                                               subscription.getEffectiveDate(), subscription.getProcessedDate(), false, internalCallContext);
-
+                                                                               subscription.getEffectiveDate(), subscription.getProcessedDate(), internalCallContext);
                 eventsMap.put(subscriptionBase.getId(), events);
                 subscriptionBaseList.add(subscriptionBase);
 
@@ -141,12 +145,20 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final DefaultSubscriptionBase baseSubscription = findBaseSubscription(subscriptionBaseList);
         try {
             baseSubscription.rebuildTransitions(dao.getEventsForSubscription(baseSubscription.getId(), internalCallContext),
-                                                catalogService.getFullCatalog(internalCallContext));
+                                                catalogService.getFullCatalog(true, true, internalCallContext));
+
+            for (final DefaultSubscriptionBase input : subscriptionBaseList) {
+                if (input.getId().equals(baseSubscription.getId())) {
+                    continue;
+                }
+
+                input.rebuildTransitions(dao.getEventsForSubscription(input.getId(), internalCallContext),
+                                         catalogService.getFullCatalog(true, true, internalCallContext));
+            }
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
-
-        return baseSubscription;
+        return subscriptionBaseList;
     }
 
     private DefaultSubscriptionBase findBaseSubscription(final List<DefaultSubscriptionBase> subscriptionBaseList) {
@@ -158,74 +170,20 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }).orNull();
     }
 
-    @Deprecated
-    @Override
-    public boolean recreatePlan(final DefaultSubscriptionBase subscription, final PlanPhaseSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDateWithMs, final CallContext context)
-            throws SubscriptionBaseApiException {
-        final EntitlementState currentState = subscription.getState();
-        if (currentState != null && currentState != EntitlementState.CANCELLED) {
-            throw new SubscriptionBaseApiException(ErrorCode.SUB_RECREATE_BAD_STATE, subscription.getId(), currentState);
-        }
-
-        final DateTime now = clock.getUTCNow();
-        final DateTime effectiveDate = (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
-        validateEffectiveDate(subscription, effectiveDate);
-
-        try {
-            final String realPriceList = (spec.getPriceListName() == null) ? PriceListSet.DEFAULT_PRICELIST_NAME : spec.getPriceListName();
-            final InternalTenantContext internalCallContext = createTenantContextFromBundleId(subscription.getBundleId(), context);
-            final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
-            final Plan plan = catalogService.getFullCatalog(internalCallContext).createOrFindPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, overridesWithContext, effectiveDate);
-            final PlanPhase phase = plan.getAllPhases()[0];
-            if (phase == null) {
-                throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
-                                                              spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
-            }
-
-            createFromSubscription(subscription, plan, spec.getPhaseType(), realPriceList, effectiveDate, now, true, context);
-            return true;
-        } catch (final CatalogApiException e) {
-            throw new SubscriptionBaseApiException(e);
-        }
-    }
-
-    private void createFromSubscription(final DefaultSubscriptionBase subscription, final Plan plan, final PhaseType initialPhase,
-                                        final String realPriceList, final DateTime effectiveDate, final DateTime processedDate,
-                                        final boolean reCreate, final CallContext context) throws SubscriptionBaseApiException {
-        final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-
-        try {
-            final List<SubscriptionBaseEvent> events = getEventsOnCreation(subscription.getBundleId(), subscription.getId(), subscription.getAlignStartDate(), subscription.getBundleStartDate(), subscription.getActiveVersion(),
-                                                                           plan, initialPhase, realPriceList, effectiveDate, processedDate, reCreate, internalCallContext);
-            if (reCreate) {
-                dao.recreateSubscription(subscription, events, internalCallContext);
-            } else {
-                dao.createSubscription(subscription, events, internalCallContext);
-            }
-            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalogService.getFullCatalog(internalCallContext));
-        } catch (final CatalogApiException e) {
-            throw new SubscriptionBaseApiException(e);
-        }
-    }
-
     @Override
     public boolean cancel(final DefaultSubscriptionBase subscription, final CallContext context) throws SubscriptionBaseApiException {
         final EntitlementState currentState = subscription.getState();
-        if (currentState != null && currentState != EntitlementState.ACTIVE) {
+        if (currentState == EntitlementState.CANCELLED) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
         }
         final DateTime now = clock.getUTCNow();
 
         final Plan currentPlan = subscription.getCurrentPlan();
-        final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                    currentPlan.getProduct().getCategory(),
-                                                                    subscription.getCurrentPlan().getRecurringBillingPeriod(),
-                                                                    subscription.getCurrentPriceList().getName(),
-                                                                    subscription.getCurrentPhase().getPhaseType());
+        final PlanPhaseSpecifier planPhase = new PlanPhaseSpecifier(currentPlan.getName(), null);
 
         try {
             final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
-            final BillingActionPolicy policy = catalogService.getFullCatalog(internalCallContext).planCancelPolicy(planPhase, now);
+            final BillingActionPolicy policy = catalogService.getFullCatalog(true, true, internalCallContext).planCancelPolicy(planPhase, now);
             final DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy);
 
             return doCancelPlan(ImmutableMap.<DefaultSubscriptionBase, DateTime>of(subscription, effectiveDate), now, internalCallContext);
@@ -237,7 +195,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     @Override
     public boolean cancelWithRequestedDate(final DefaultSubscriptionBase subscription, final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
         final EntitlementState currentState = subscription.getState();
-        if (currentState != null && currentState != EntitlementState.ACTIVE) {
+        if (currentState == EntitlementState.CANCELLED) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
         }
         final DateTime now = clock.getUTCNow();
@@ -250,7 +208,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     @Override
     public boolean cancelWithPolicy(final DefaultSubscriptionBase subscription, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
         final EntitlementState currentState = subscription.getState();
-        if (currentState != null && currentState != EntitlementState.ACTIVE) {
+        if (currentState == EntitlementState.CANCELLED) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CANCEL_BAD_STATE, subscription.getId(), currentState);
         }
 
@@ -292,7 +250,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
             boolean allSubscriptionsCancelled = true;
             for (final DefaultSubscriptionBase subscription : subscriptions.keySet()) {
-                final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+                final Catalog fullCatalog = catalogService.getFullCatalog(true, true, internalCallContext);
                 subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
                 allSubscriptionsCancelled = allSubscriptionsCancelled && (subscription.getState() == EntitlementState.CANCELLED);
             }
@@ -312,7 +270,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final DateTime now = clock.getUTCNow();
         final SubscriptionBaseEvent uncancelEvent = new ApiEventUncancel(new ApiEventBuilder()
                                                                                  .setSubscriptionId(subscription.getId())
-                                                                                 .setActiveVersion(subscription.getActiveVersion())
                                                                                  .setEffectiveDate(now)
                                                                                  .setFromDisk(true));
 
@@ -322,7 +279,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
         final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, internalCallContext);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                          PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(), nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
+                                          PhaseEventData.createNextPhaseEvent(subscription.getId(), nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
                                           null;
         if (nextPhaseEvent != null) {
             uncancelEvents.add(nextPhaseEvent);
@@ -330,7 +287,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         dao.uncancelSubscription(subscription, uncancelEvents, internalCallContext);
         try {
-            final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+            final Catalog fullCatalog = catalogService.getFullCatalog(true, true, internalCallContext);
             subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
             return true;
         } catch (final CatalogApiException e) {
@@ -340,9 +297,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
     @Override
     public DateTime dryRunChangePlan(final DefaultSubscriptionBase subscription,
-                                     final String productName,
-                                     final BillingPeriod term,
-                                     final String priceList,
+                                     final PlanSpecifier spec,
                                      @Nullable final DateTime requestedDateWithMs,
                                      @Nullable final BillingActionPolicy requestedPolicy,
                                      final TenantContext context) throws SubscriptionBaseApiException {
@@ -350,7 +305,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         BillingActionPolicy policyMaybeNull = requestedPolicy;
         if (requestedDateWithMs == null && requestedPolicy == null) {
-            final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, productName, term, priceList, now, context);
+            final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, spec, now, context);
             policyMaybeNull = planChangeResult.getPolicy();
         }
 
@@ -364,18 +319,17 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public DateTime changePlan(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
-                               final String priceList, final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
+    public DateTime changePlan(final DefaultSubscriptionBase subscription, final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
         final DateTime now = clock.getUTCNow();
 
         validateEntitlementState(subscription);
 
-        final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, productName, term, priceList, now, context);
-        final DateTime effectiveDate = dryRunChangePlan(subscription, productName, term, priceList, null, planChangeResult.getPolicy(), context);
+        final PlanChangeResult planChangeResult = getPlanChangeResult(subscription, spec, now, context);
+        final DateTime effectiveDate = dryRunChangePlan(subscription, spec, null, planChangeResult.getPolicy(), context);
         validateEffectiveDate(subscription, effectiveDate);
 
         try {
-            doChangePlan(subscription, productName, term, planChangeResult.getNewPriceList().getName(), overrides, effectiveDate, context);
+            doChangePlan(subscription, spec, overrides, effectiveDate, context);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -384,15 +338,14 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public DateTime changePlanWithRequestedDate(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
-                                                final String priceList, final List<PlanPhasePriceOverride> overrides,
+    public DateTime changePlanWithRequestedDate(final DefaultSubscriptionBase subscription, final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides,
                                                 final DateTime requestedDateWithMs, final CallContext context) throws SubscriptionBaseApiException {
-        final DateTime effectiveDate = dryRunChangePlan(subscription, productName, term, priceList, requestedDateWithMs, null, context);
+        final DateTime effectiveDate = dryRunChangePlan(subscription, spec, requestedDateWithMs, null, context);
         validateEffectiveDate(subscription, effectiveDate);
         validateEntitlementState(subscription);
 
         try {
-            doChangePlan(subscription, productName, term, priceList, overrides, effectiveDate, context);
+            doChangePlan(subscription, spec, overrides, effectiveDate, context);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -401,13 +354,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public DateTime changePlanWithPolicy(final DefaultSubscriptionBase subscription, final String productName, final BillingPeriod term,
-                                         final String priceList, final List<PlanPhasePriceOverride> overrides, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
+    public DateTime changePlanWithPolicy(final DefaultSubscriptionBase subscription, final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
         validateEntitlementState(subscription);
 
-        final DateTime effectiveDate = dryRunChangePlan(subscription, productName, term, priceList, null, policy, context);
+        final DateTime effectiveDate = dryRunChangePlan(subscription, spec, null, policy, context);
         try {
-            doChangePlan(subscription, productName, term, priceList, overrides, effectiveDate, context);
+            doChangePlan(subscription, spec, overrides, effectiveDate, context);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -416,25 +368,16 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final String productName,
-                                                final BillingPeriod term, final String priceList, final DateTime effectiveDate, final TenantContext context) throws SubscriptionBaseApiException {
+    public PlanChangeResult getPlanChangeResult(final DefaultSubscriptionBase subscription, final PlanSpecifier toPlanPhase, final DateTime effectiveDate, final TenantContext context) throws SubscriptionBaseApiException {
         final PlanChangeResult planChangeResult;
         try {
             final InternalTenantContext internalCallContext = createTenantContextFromBundleId(subscription.getBundleId(), context);
-            final Product destProduct = catalogService.getFullCatalog(internalCallContext).findProduct(productName, effectiveDate);
             final Plan currentPlan = subscription.getCurrentPlan();
+
             final PriceList currentPriceList = subscription.getCurrentPriceList();
-            final PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
-                                                                            currentPlan.getProduct().getCategory(),
-                                                                            currentPlan.getRecurringBillingPeriod(),
-                                                                            currentPriceList.getName(),
+            final PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getName(),
                                                                             subscription.getCurrentPhase().getPhaseType());
-            final PlanSpecifier toPlanPhase = new PlanSpecifier(productName,
-                                                                destProduct.getCategory(),
-                                                                term,
-                                                                priceList);
-
-            planChangeResult = catalogService.getFullCatalog(internalCallContext).planChange(fromPlanPhase, toPlanPhase, effectiveDate);
+            planChangeResult = catalogService.getFullCatalog(true, true, internalCallContext).planChange(fromPlanPhase, toPlanPhase, effectiveDate);
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
@@ -443,15 +386,23 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     private void doChangePlan(final DefaultSubscriptionBase subscription,
-                              final String newProductName,
-                              final BillingPeriod newBillingPeriod,
-                              final String newPriceList,
+                              final PlanSpecifier spec,
                               final List<PlanPhasePriceOverride> overrides,
                               final DateTime effectiveDate,
                               final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
         final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
         final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
-        final Plan newPlan = catalogService.getFullCatalog(internalCallContext).createOrFindPlan(newProductName, newBillingPeriod, newPriceList, overridesWithContext, effectiveDate, subscription.getStartDate());
+        final Plan newPlan = catalogService.getFullCatalog(true, true, internalCallContext).createOrFindPlan(spec, overridesWithContext, effectiveDate, subscription.getStartDate());
+
+        if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(newPlan.getProduct().getCategory().toString())) {
+            if (newPlan.getPlansAllowedInBundle() != -1
+                && newPlan.getPlansAllowedInBundle() > 0
+                && addonUtils.countExistingAddOnsWithSamePlanName(dao.getSubscriptions(subscription.getBundleId(), null, internalCallContext), newPlan.getName())
+                   >= newPlan.getPlansAllowedInBundle()) {
+                // the plan can be changed to the new value, because it has reached its limit by bundle
+                throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, newPlan.getName());
+            }
+        }
 
         if (newPlan.getProduct().getCategory() != subscription.getCategory()) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_INVALID, subscription.getId());
@@ -459,19 +410,19 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
 
         final List<DefaultSubscriptionBase> addOnSubscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
         final List<SubscriptionBaseEvent> addOnCancelEvents = new ArrayList<SubscriptionBaseEvent>();
-        final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPriceList, effectiveDate, true, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalCallContext);
+        final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPlan.getPriceListName(), effectiveDate, true, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalCallContext);
 
         dao.changePlan(subscription, changeEvents, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalCallContext);
 
-        final Catalog fullCatalog = catalogService.getFullCatalog(internalCallContext);
+        final Catalog fullCatalog = catalogService.getFullCatalog(true, true, internalCallContext);
         subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
     }
 
     @Override
-    public List<SubscriptionBaseEvent> getEventsOnCreation(final UUID bundleId, final UUID subscriptionId, final DateTime alignStartDate, final DateTime bundleStartDate, final long activeVersion,
+    public List<SubscriptionBaseEvent> getEventsOnCreation(final UUID bundleId, final UUID subscriptionId, final DateTime alignStartDate, final DateTime bundleStartDate,
                                                            final Plan plan, final PhaseType initialPhase,
                                                            final String realPriceList, final DateTime effectiveDate, final DateTime processedDate,
-                                                           final boolean reCreate, final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
+                                                           final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
         final TimedPhase[] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(alignStartDate, bundleStartDate, plan, initialPhase,
                                                                                               realPriceList, effectiveDate, internalTenantContext);
 
@@ -480,14 +431,13 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 .setEventPlan(plan.getName())
                 .setEventPlanPhase(curAndNextPhases[0].getPhase().getName())
                 .setEventPriceList(realPriceList)
-                .setActiveVersion(activeVersion)
                 .setEffectiveDate(effectiveDate)
                 .setFromDisk(true);
-        final ApiEvent creationEvent = (reCreate) ? new ApiEventReCreate(createBuilder) : new ApiEventCreate(createBuilder);
+        final ApiEvent creationEvent = new ApiEventCreate(createBuilder);
 
         final TimedPhase nextTimedPhase = curAndNextPhases[1];
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                          PhaseEventData.createNextPhaseEvent(subscriptionId, activeVersion, nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
+                                          PhaseEventData.createNextPhaseEvent(subscriptionId, nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
                                           null;
         final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
         events.add(creationEvent);
@@ -522,13 +472,12 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                                              .setEventPlan(newPlan.getName())
                                                                              .setEventPlanPhase(currentTimedPhase.getPhase().getName())
                                                                              .setEventPriceList(newPriceList)
-                                                                             .setActiveVersion(subscription.getActiveVersion())
                                                                              .setEffectiveDate(effectiveDate)
                                                                              .setFromDisk(true));
 
         final TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList, effectiveDate, internalTenantContext);
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                          PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+                                          PhaseEventData.createNextPhaseEvent(subscription.getId(),
                                                                               nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
                                           null;
 
@@ -553,7 +502,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         final List<SubscriptionBaseEvent> cancelEvents = new ArrayList<SubscriptionBaseEvent>();
         final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
                                                                              .setSubscriptionId(subscription.getId())
-                                                                             .setActiveVersion(subscription.getActiveVersion())
                                                                              .setEffectiveDate(effectiveDate)
                                                                              .setFromDisk(true));
         cancelEvents.add(cancelEvent);
@@ -609,7 +557,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 //
                 final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
                                                                                      .setSubscriptionId(cur.getId())
-                                                                                     .setActiveVersion(cur.getActiveVersion())
                                                                                      .setEffectiveDate(effectiveDate)
                                                                                      .setFromDisk(true));
                 subscriptionsToBeCancelled.add(cur);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
index 04601ac..89f8aa7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionEvent.java
@@ -41,14 +41,17 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     private final String previousPriceList;
     private final String previousPlan;
     private final String previousPhase;
+    private final Integer previousBillCycleDayLocal;
     private final EntitlementState nextState;
     private final String nextPriceList;
     private final String nextPlan;
     private final String nextPhase;
+    private final Integer nextBillCycleDayLocal;
     private final Integer remainingEventsForUserOperation;
     private final SubscriptionBaseTransitionType transitionType;
     private final DateTime startDate;
 
+
     public DefaultSubscriptionEvent(final SubscriptionBaseTransitionData in, final DateTime startDate,
                                     final Long searchKey1,
                                     final Long searchKey2,
@@ -62,10 +65,12 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
              (in.getPreviousPlan() != null) ? in.getPreviousPlan().getName() : null,
              (in.getPreviousPhase() != null) ? in.getPreviousPhase().getName() : null,
              (in.getPreviousPriceList() != null) ? in.getPreviousPriceList().getName() : null,
+             in.getPreviousBillingCycleDayLocal(),
              in.getNextState(),
              (in.getNextPlan() != null) ? in.getNextPlan().getName() : null,
              (in.getNextPhase() != null) ? in.getNextPhase().getName() : null,
              (in.getNextPriceList() != null) ? in.getNextPriceList().getName() : null,
+             in.getNextBillingCycleDayLocal(),
              in.getTotalOrdering(),
              in.getTransitionType(),
              in.getRemainingEventsForUserOperation(),
@@ -85,10 +90,12 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
                                     @JsonProperty("previousPlan") final String previousPlan,
                                     @JsonProperty("previousPhase") final String previousPhase,
                                     @JsonProperty("previousPriceList") final String previousPriceList,
+                                    @JsonProperty("previousBillCycleDayLocal") final Integer previousBillCycleDayLocal,
                                     @JsonProperty("nextState") final EntitlementState nextState,
                                     @JsonProperty("nextPlan") final String nextPlan,
                                     @JsonProperty("nextPhase") final String nextPhase,
                                     @JsonProperty("nextPriceList") final String nextPriceList,
+                                    @JsonProperty("nextBillCycleDayLocal") final Integer nextBillCycleDayLocal,
                                     @JsonProperty("totalOrdering") final Long totalOrdering,
                                     @JsonProperty("transitionType") final SubscriptionBaseTransitionType transitionType,
                                     @JsonProperty("remainingEventsForUserOperation") final Integer remainingEventsForUserOperation,
@@ -104,11 +111,13 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         this.effectiveTransitionTime = effectiveTransitionTime;
         this.previousState = previousState;
         this.previousPriceList = previousPriceList;
+        this.previousBillCycleDayLocal = previousBillCycleDayLocal;
         this.previousPlan = previousPlan;
         this.previousPhase = previousPhase;
         this.nextState = nextState;
         this.nextPlan = nextPlan;
         this.nextPriceList = nextPriceList;
+        this.nextBillCycleDayLocal = nextBillCycleDayLocal;
         this.nextPhase = nextPhase;
         this.totalOrdering = totalOrdering;
         this.transitionType = transitionType;
@@ -154,6 +163,11 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     }
 
     @Override
+    public Integer getPreviousBillCycleDayLocal() {
+        return previousBillCycleDayLocal;
+    }
+
+    @Override
     public String getNextPlan() {
         return nextPlan;
     }
@@ -179,6 +193,11 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
     }
 
     @Override
+    public Integer getNextBillCycleDayLocal() {
+        return nextBillCycleDayLocal;
+    }
+
+    @Override
     public Integer getRemainingEventsForUserOperation() {
         return remainingEventsForUserOperation;
     }
@@ -221,10 +240,12 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         sb.append(", effectiveTransitionTime=").append(effectiveTransitionTime);
         sb.append(", previousState=").append(previousState);
         sb.append(", previousPriceList='").append(previousPriceList).append('\'');
+        sb.append(", previousBillCycleDayLocal='").append(previousBillCycleDayLocal).append('\'');
         sb.append(", previousPlan='").append(previousPlan).append('\'');
         sb.append(", previousPhase='").append(previousPhase).append('\'');
         sb.append(", nextState=").append(nextState);
         sb.append(", nextPriceList='").append(nextPriceList).append('\'');
+        sb.append(", nextBillCycleDayLocal='").append(nextBillCycleDayLocal).append('\'');
         sb.append(", nextPlan='").append(nextPlan).append('\'');
         sb.append(", nextPhase='").append(nextPhase).append('\'');
         sb.append(", remainingEventsForUserOperation=").append(remainingEventsForUserOperation);
@@ -263,6 +284,9 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
             return false;
         }
+        if (nextBillCycleDayLocal != null ? !nextBillCycleDayLocal.equals(that.nextBillCycleDayLocal) : that.nextBillCycleDayLocal != null) {
+            return false;
+        }
         if (nextState != that.nextState) {
             return false;
         }
@@ -275,6 +299,9 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         if (previousPriceList != null ? !previousPriceList.equals(that.previousPriceList) : that.previousPriceList != null) {
             return false;
         }
+        if (previousBillCycleDayLocal != null ? !previousBillCycleDayLocal.equals(that.previousBillCycleDayLocal) : that.previousBillCycleDayLocal != null) {
+            return false;
+        }
         if (previousState != that.previousState) {
             return false;
         }
@@ -309,10 +336,12 @@ public abstract class DefaultSubscriptionEvent extends BusEventBase implements S
         result = 31 * result + (effectiveTransitionTime != null ? effectiveTransitionTime.hashCode() : 0);
         result = 31 * result + (previousState != null ? previousState.hashCode() : 0);
         result = 31 * result + (previousPriceList != null ? previousPriceList.hashCode() : 0);
+        result = 31 * result + (previousBillCycleDayLocal != null ? previousBillCycleDayLocal.hashCode() : 0);
         result = 31 * result + (previousPlan != null ? previousPlan.hashCode() : 0);
         result = 31 * result + (previousPhase != null ? previousPhase.hashCode() : 0);
         result = 31 * result + (nextState != null ? nextState.hashCode() : 0);
         result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+        result = 31 * result + (nextBillCycleDayLocal != null ? nextBillCycleDayLocal.hashCode() : 0);
         result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
         result = 31 * result + (nextPhase != null ? nextPhase.hashCode() : 0);
         result = 31 * result + (remainingEventsForUserOperation != null ? remainingEventsForUserOperation.hashCode() : 0);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
index df2ccad..511b142 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionData.java
@@ -39,6 +39,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
     private final DateTime effectiveTransitionTime;
     private final EntitlementState previousState;
     private final PriceList previousPriceList;
+    private final Integer previousBillingCycleDayLocal;
     private final UUID previousEventId;
     private final DateTime previousEventCreatedDate;
     private final Plan previousPlan;
@@ -47,6 +48,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
     private final DateTime nextEventCreatedDate;
     private final EntitlementState nextState;
     private final PriceList nextPriceList;
+    private final Integer nextBillingCycleDayLocal;
     private final Plan nextPlan;
     private final PlanPhase nextPhase;
     private final Boolean isFromDisk;
@@ -66,12 +68,14 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
                                           final Plan previousPlan,
                                           final PlanPhase previousPhase,
                                           final PriceList previousPriceList,
+                                          final Integer previousBillingCycleDayLocal,
                                           final UUID nextEventId,
                                           final DateTime nextEventCreatedDate,
                                           final EntitlementState nextState,
                                           final Plan nextPlan,
                                           final PlanPhase nextPhase,
                                           final PriceList nextPriceList,
+                                          final Integer nextBillingCycleDayLocal,
                                           final Long totalOrdering,
                                           final DateTime createdDate,
                                           final UUID userToken,
@@ -84,11 +88,13 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         this.effectiveTransitionTime = effectiveTransitionTime;
         this.previousState = previousState;
         this.previousPriceList = previousPriceList;
+        this.previousBillingCycleDayLocal = previousBillingCycleDayLocal;
         this.previousPlan = previousPlan;
         this.previousPhase = previousPhase;
         this.nextState = nextState;
         this.nextPlan = nextPlan;
         this.nextPriceList = nextPriceList;
+        this.nextBillingCycleDayLocal = nextBillingCycleDayLocal;
         this.nextPhase = nextPhase;
         this.totalOrdering = totalOrdering;
         this.previousEventId = previousEventId;
@@ -118,6 +124,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         this.previousEventCreatedDate = input.getPreviousEventCreatedDate();
         this.previousState = input.getPreviousState();
         this.previousPriceList = input.getPreviousPriceList();
+        this.previousBillingCycleDayLocal = input.getPreviousBillingCycleDayLocal();
         this.previousPlan = input.getPreviousPlan();
         this.previousPhase = input.getPreviousPhase();
         this.nextEventId = input.getNextEventId();
@@ -125,6 +132,7 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         this.nextState = input.getNextState();
         this.nextPlan = input.getNextPlan();
         this.nextPriceList = input.getNextPriceList();
+        this.nextBillingCycleDayLocal = input.getNextBillingCycleDayLocal();
         this.nextPhase = input.getNextPhase();
         this.totalOrdering = input.getTotalOrdering();
         this.isFromDisk = input.isFromDisk();
@@ -208,6 +216,16 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         return nextPriceList;
     }
 
+    @Override
+    public Integer getPreviousBillingCycleDayLocal() {
+        return previousBillingCycleDayLocal;
+    }
+
+    @Override
+    public Integer getNextBillingCycleDayLocal() {
+        return nextBillingCycleDayLocal;
+    }
+
     public UUID getUserToken() {
         return userToken;
     }
@@ -227,6 +245,8 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
                 return apiEventType.getSubscriptionTransitionType();
             case PHASE:
                 return SubscriptionBaseTransitionType.PHASE;
+            case BCD_UPDATE:
+                return SubscriptionBaseTransitionType.BCD_CHANGE;
             default:
                 throw new SubscriptionBaseError("Unexpected event type " + eventType);
         }
@@ -270,10 +290,12 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         sb.append(", effectiveTransitionTime=").append(effectiveTransitionTime);
         sb.append(", previousState=").append(previousState);
         sb.append(", previousPriceList=").append(previousPriceList);
+        sb.append(", previousBillingCycleDayLocal=").append(previousBillingCycleDayLocal);
         sb.append(", previousPlan=").append(previousPlan);
         sb.append(", previousPhase=").append(previousPhase);
         sb.append(", nextState=").append(nextState);
         sb.append(", nextPriceList=").append(nextPriceList);
+        sb.append(", nextBillingCycleDayLocal=").append(nextBillingCycleDayLocal);
         sb.append(", nextPlan=").append(nextPlan);
         sb.append(", nextPhase=").append(nextPhase);
         sb.append(", isFromDisk=").append(isFromDisk);
@@ -321,6 +343,9 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
             return false;
         }
+        if (nextBillingCycleDayLocal != null ? !nextBillingCycleDayLocal.equals(that.nextBillingCycleDayLocal) : that.nextBillingCycleDayLocal != null) {
+            return false;
+        }
         if (nextState != that.nextState) {
             return false;
         }
@@ -333,6 +358,9 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         if (previousPriceList != null ? !previousPriceList.equals(that.previousPriceList) : that.previousPriceList != null) {
             return false;
         }
+        if (previousBillingCycleDayLocal != null ? !previousBillingCycleDayLocal.equals(that.previousBillingCycleDayLocal) : that.previousBillingCycleDayLocal != null) {
+            return false;
+        }
         if (previousState != that.previousState) {
             return false;
         }
@@ -348,7 +376,6 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         if (userToken != null ? !userToken.equals(that.userToken) : that.userToken != null) {
             return false;
         }
-
         return true;
     }
 
@@ -363,10 +390,12 @@ public class SubscriptionBaseTransitionData implements SubscriptionBaseTransitio
         result = 31 * result + (effectiveTransitionTime != null ? effectiveTransitionTime.hashCode() : 0);
         result = 31 * result + (previousState != null ? previousState.hashCode() : 0);
         result = 31 * result + (previousPriceList != null ? previousPriceList.hashCode() : 0);
+        result = 31 * result + (previousBillingCycleDayLocal != null ? previousBillingCycleDayLocal.hashCode() : 0);
         result = 31 * result + (previousPlan != null ? previousPlan.hashCode() : 0);
         result = 31 * result + (previousPhase != null ? previousPhase.hashCode() : 0);
         result = 31 * result + (nextState != null ? nextState.hashCode() : 0);
         result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+        result = 31 * result + (nextBillingCycleDayLocal != null ? nextBillingCycleDayLocal.hashCode() : 0);
         result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
         result = 31 * result + (nextPhase != null ? nextPhase.hashCode() : 0);
         result = 31 * result + (isFromDisk != null ? isFromDisk.hashCode() : 0);
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
index 881e24f..e7acad6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBaseTransitionDataIterator.java
@@ -27,7 +27,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
 
     private final Clock clock;
     private final Iterator<SubscriptionBaseTransition> it;
-    private final Kind kind;
     private final TimeLimit timeLimit;
     private final Visibility visibility;
 
@@ -38,12 +37,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
         DESC_FROM_FUTURE
     }
 
-    public enum Kind {
-        SUBSCRIPTION,
-        BILLING,
-        ALL
-    }
-
     public enum TimeLimit {
         FUTURE_ONLY,
         PAST_OR_PRESENT_ONLY,
@@ -56,10 +49,9 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
     }
 
     public SubscriptionBaseTransitionDataIterator(final Clock clock, final LinkedList<SubscriptionBaseTransition> transitions,
-                                                  final Order order, final Kind kind, final Visibility visibility, final TimeLimit timeLimit) {
+                                                  final Order order, final Visibility visibility, final TimeLimit timeLimit) {
         this.it = (order == Order.DESC_FROM_FUTURE) ? transitions.descendingIterator() : transitions.iterator();
         this.clock = clock;
-        this.kind = kind;
         this.timeLimit = timeLimit;
         this.visibility = visibility;
         this.next = null;
@@ -81,10 +73,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
         if (visibility == Visibility.FROM_DISK_ONLY && ! ((SubscriptionBaseTransitionData) input).isFromDisk()) {
             return true;
         }
-        if ((kind == Kind.SUBSCRIPTION && shouldSkipForSubscriptionEvents((SubscriptionBaseTransitionData) input)) ||
-            (kind == Kind.BILLING && shouldSkipForBillingEvents((SubscriptionBaseTransitionData) input))) {
-            return true;
-        }
         if ((timeLimit == TimeLimit.FUTURE_ONLY && !input.getEffectiveTransitionTime().isAfter(clock.getUTCNow())) ||
                 ((timeLimit == TimeLimit.PAST_OR_PRESENT_ONLY && input.getEffectiveTransitionTime().isAfter(clock.getUTCNow())))) {
             return true;
@@ -92,17 +80,6 @@ public class SubscriptionBaseTransitionDataIterator implements Iterator<Subscrip
         return false;
     }
 
-    private boolean shouldSkipForSubscriptionEvents(final SubscriptionBaseTransitionData input) {
-        // SubscriptionBase system knows about all events except for MIGRATE_BILLING
-        return (input.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_BILLING);
-    }
-
-    private boolean shouldSkipForBillingEvents(final SubscriptionBaseTransitionData input) {
-        // Junction system knows about all events except for MIGRATE_ENTITLEMENT
-        return input.getTransitionType() == SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
-    }
-
-
     @Override
     public SubscriptionBaseTransition next() {
         return next;
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
index a23502f..7beabf6 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionBuilder.java
@@ -32,12 +32,11 @@ public class SubscriptionBuilder {
     private DateTime updatedDate;
     private DateTime alignStartDate;
     private DateTime bundleStartDate;
-    private Long activeVersion;
     private ProductCategory category;
     private DateTime chargedThroughDate;
+    private boolean migrated;
 
     public SubscriptionBuilder() {
-        this.activeVersion = SubscriptionEvents.INITIAL_VERSION;
     }
 
     public SubscriptionBuilder(final DefaultSubscriptionBase original) {
@@ -46,8 +45,8 @@ public class SubscriptionBuilder {
         this.alignStartDate = original.getAlignStartDate();
         this.bundleStartDate = original.getBundleStartDate();
         this.category = original.getCategory();
-        this.activeVersion = original.getActiveVersion();
         this.chargedThroughDate = original.getChargedThroughDate();
+        this.migrated = original.isMigrated();
     }
 
     public SubscriptionBuilder setId(final UUID id) {
@@ -79,14 +78,13 @@ public class SubscriptionBuilder {
         this.bundleStartDate = bundleStartDate;
         return this;
     }
-
-    public SubscriptionBuilder setActiveVersion(final long activeVersion) {
-        this.activeVersion = activeVersion;
+    public SubscriptionBuilder setChargedThroughDate(final DateTime chargedThroughDate) {
+        this.chargedThroughDate = chargedThroughDate;
         return this;
     }
 
-    public SubscriptionBuilder setChargedThroughDate(final DateTime chargedThroughDate) {
-        this.chargedThroughDate = chargedThroughDate;
+    public SubscriptionBuilder setMigrated(final boolean migrated) {
+        this.migrated = migrated;
         return this;
     }
 
@@ -118,11 +116,6 @@ public class SubscriptionBuilder {
     public DateTime getBundleStartDate() {
         return bundleStartDate;
     }
-
-    public Long getActiveVersion() {
-        return activeVersion;
-    }
-
     public ProductCategory getCategory() {
         return category;
     }
@@ -131,6 +124,10 @@ public class SubscriptionBuilder {
         return chargedThroughDate;
     }
 
+    public boolean isMigrated() {
+        return migrated;
+    }
+
     private void checkAllFieldsSet() {
         for (final Field cur : SubscriptionBuilder.class.getDeclaredFields()) {
             try {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
index 46bc931..7977dc5 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/SubscriptionEvents.java
@@ -23,49 +23,31 @@ import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 
 
 public class SubscriptionEvents {
-    public static final long INITIAL_VERSION = 1;
 
-    private final List<SubscriptionBaseEvent> events;
 
-    private long activeVersion;
+    private final List<SubscriptionBaseEvent> events;
 
     public SubscriptionEvents() {
         this.events = new LinkedList<SubscriptionBaseEvent>();
-        this.activeVersion = INITIAL_VERSION;
     }
 
     public void addEvent(final SubscriptionBaseEvent ev) {
         events.add(ev);
     }
 
-    public List<SubscriptionBaseEvent> getCurrentView() {
-        return getViewForVersion(activeVersion);
-    }
-
     public List<SubscriptionBaseEvent> getViewForVersion(final long version) {
         final LinkedList<SubscriptionBaseEvent> result = new LinkedList<SubscriptionBaseEvent>();
         for (final SubscriptionBaseEvent cur : events) {
-            if (cur.getActiveVersion() == version) {
-                result.add(cur);
-            }
+            result.add(cur);
         }
 
         return result;
     }
 
-    public long getActiveVersion() {
-        return activeVersion;
-    }
-
-    public void setActiveVersion(final long activeVersion) {
-        this.activeVersion = activeVersion;
-    }
-
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
         sb.append("SubscriptionEvents");
-        sb.append("{activeVersion=").append(activeVersion);
         sb.append(", events=").append(events);
         sb.append('}');
         return sb.toString();
@@ -82,9 +64,6 @@ public class SubscriptionEvents {
 
         final SubscriptionEvents that = (SubscriptionEvents) o;
 
-        if (activeVersion != that.activeVersion) {
-            return false;
-        }
         if (events != null ? !events.equals(that.events) : that.events != null) {
             return false;
         }
@@ -95,7 +74,6 @@ public class SubscriptionEvents {
     @Override
     public int hashCode() {
         int result = events != null ? events.hashCode() : 0;
-        result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
         return result;
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
index 6d5a679..8fd9da7 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/addon/AddonUtils.java
@@ -16,6 +16,9 @@
 
 package org.killbill.billing.subscription.engine.addon;
 
+import java.util.Collection;
+import java.util.List;
+
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -23,7 +26,9 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
@@ -46,7 +51,7 @@ public class AddonUtils {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
         }
 
-        final Product baseProduct = catalogService.getFullCatalog(context).findProduct(baseSubscription.getCurrentPlan().getProduct().getName(), requestedDate);
+        final Product baseProduct = catalogService.getFullCatalog(true, true, context).findProduct(baseSubscription.getCurrentPlan().getProduct().getName(), requestedDate);
         if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
             throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED,
                                                    targetAddOnPlan.getName(), baseSubscription.getCurrentPlan().getProduct().getName());
@@ -60,7 +65,7 @@ public class AddonUtils {
 
     public boolean isAddonAvailableFromProdName(final String baseProductName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Product product = catalogService.getFullCatalog(context).findProduct(baseProductName, requestedDate);
+            final Product product = catalogService.getFullCatalog(true, true, context).findProduct(baseProductName, requestedDate);
             return isAddonAvailable(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseError(e);
@@ -69,7 +74,7 @@ public class AddonUtils {
 
     public boolean isAddonAvailableFromPlanName(final String basePlanName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Plan plan = catalogService.getFullCatalog(context).findPlan(basePlanName, requestedDate);
+            final Plan plan = catalogService.getFullCatalog(true, true, context).findPlan(basePlanName, requestedDate);
             final Product product = plan.getProduct();
             return isAddonAvailable(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
@@ -80,7 +85,7 @@ public class AddonUtils {
 
     public boolean isAddonIncludedFromProdName(final String baseProductName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Product product = catalogService.getFullCatalog(context).findProduct(baseProductName, requestedDate);
+            final Product product = catalogService.getFullCatalog(true, true, context).findProduct(baseProductName, requestedDate);
             return isAddonIncluded(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
             throw new SubscriptionBaseError(e);
@@ -90,7 +95,7 @@ public class AddonUtils {
 
     public boolean isAddonIncludedFromPlanName(final String basePlanName, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) {
         try {
-            final Plan plan = catalogService.getFullCatalog(context).findPlan(basePlanName, requestedDate);
+            final Plan plan = catalogService.getFullCatalog(true, true, context).findPlan(basePlanName, requestedDate);
             final Product product = plan.getProduct();
             return isAddonIncluded(product, targetAddOnPlan);
         } catch (CatalogApiException e) {
@@ -101,7 +106,7 @@ public class AddonUtils {
 
     private boolean isAddonAvailable(final Product baseProduct, final Plan targetAddOnPlan) {
         final Product targetAddonProduct = targetAddOnPlan.getProduct();
-        final Product[] availableAddOns = baseProduct.getAvailable();
+        final Collection<Product> availableAddOns = baseProduct.getAvailable();
 
         for (final Product curAv : availableAddOns) {
             if (curAv.getName().equals(targetAddonProduct.getName())) {
@@ -113,7 +118,7 @@ public class AddonUtils {
 
     private boolean isAddonIncluded(final Product baseProduct, final Plan targetAddOnPlan) {
         final Product targetAddonProduct = targetAddOnPlan.getProduct();
-        final Product[] includedAddOns = baseProduct.getIncluded();
+        final Collection<Product> includedAddOns = baseProduct.getIncluded();
         for (final Product curAv : includedAddOns) {
             if (curAv.getName().equals(targetAddonProduct.getName())) {
                 return true;
@@ -121,4 +126,16 @@ public class AddonUtils {
         }
         return false;
     }
+
+    public int countExistingAddOnsWithSamePlanName(final List<SubscriptionBase> subscriptionsForBundle, final String planName) {
+        int countExistingAddOns = 0;
+        for (SubscriptionBase subscription : subscriptionsForBundle) {
+            if (subscription.getCurrentPlan().getName().equalsIgnoreCase(planName)
+                && subscription.getLastActiveProduct().getCategory() != null
+                && ProductCategory.ADD_ON.equals(subscription.getLastActiveProduct().getCategory())) {
+                countExistingAddOns++;
+            }
+        }
+        return countExistingAddOns;
+    }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
index e5e84ab..fd0e273 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/core/DefaultSubscriptionBaseService.java
@@ -152,8 +152,10 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
                 log.warn("Error retrieving subscriptionId='{}'", event.getSubscriptionId());
                 return;
             }
-            if (subscription.getActiveVersion() > event.getActiveVersion()) {
-                // Skip repaired events
+
+            final SubscriptionBaseTransitionData transition = subscription.getTransitionFromEvent(event, seqId);
+            if (transition == null) {
+                log.warn("Skipping event ='{}', no matching transition was built", event.getType());
                 return;
             }
 
@@ -163,11 +165,12 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
             } else if (event.getType() == EventType.API_USER && subscription.getCategory() == ProductCategory.BASE) {
                 final CallContext callContext = internalCallContextFactory.createCallContext(context);
                 eventSent = onBasePlanEvent(subscription, event, callContext);
+            } else if (event.getType() == EventType.BCD_UPDATE) {
+                eventSent = false;
             }
 
             if (!eventSent) {
                 // Methods above invoking the DAO will send this event directly from the transaction
-                final SubscriptionBaseTransitionData transition = subscription.getTransitionFromEvent(event, seqId);
                 final BusEvent busEvent = new DefaultEffectiveSubscriptionEvent(transition,
                                                                                 subscription.getAlignStartDate(),
                                                                                 context.getUserToken(),
@@ -184,10 +187,9 @@ public class DefaultSubscriptionBaseService implements EventListener, Subscripti
 
     private boolean onPhaseEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent readyPhaseEvent, final InternalCallContext context) {
         try {
-            final DateTime now = clock.getUTCNow();
-            final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, context);
+            final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, readyPhaseEvent.getEffectiveDate(), context);
             final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
-                                              PhaseEventData.createNextPhaseEvent(subscription.getId(), subscription.getActiveVersion(),
+                                              PhaseEventData.createNextPhaseEvent(subscription.getId(),
                                                                                   nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) :
                                               null;
             if (nextPhaseEvent != null) {
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 119bfe7..8899098 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
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -47,9 +46,9 @@ import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entity.EntityPersistenceException;
 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;
+import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
+import org.killbill.billing.subscription.api.transfer.SubscriptionTransferData;
 import org.killbill.billing.subscription.api.transfer.TransferCancelData;
 import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
 import org.killbill.billing.subscription.api.user.DefaultRequestedSubscriptionEvent;
@@ -67,16 +66,16 @@ import org.killbill.billing.subscription.engine.dao.model.SubscriptionModelDao;
 import org.killbill.billing.subscription.events.EventBaseBuilder;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
+import org.killbill.billing.subscription.events.bcd.BCDEvent;
+import org.killbill.billing.subscription.events.bcd.BCDEventBuilder;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEventBuilder;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventBuilder;
 import org.killbill.billing.subscription.events.user.ApiEventCancel;
 import org.killbill.billing.subscription.events.user.ApiEventChange;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
 import org.killbill.billing.subscription.events.user.ApiEventType;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
-import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
@@ -543,28 +542,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     @Override
-    public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
-        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
-            @Override
-            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
-
-                for (final SubscriptionBaseEvent cur : recreateEvents) {
-                    transactional.create(new SubscriptionEventModelDao(cur), context);
-
-                    final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
-                    recordBusOrFutureNotificationFromTransaction(subscription, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, context);
-                }
-
-                // Notify the Bus of the latest requested change
-                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, recreateEvents.get(recreateEvents.size() - 1), SubscriptionBaseTransitionType.RE_CREATE, context);
-
-                return null;
-            }
-        });
-    }
-
-    @Override
     public void cancelSubscriptionsOnBasePlanEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent event, final List<DefaultSubscriptionBase> subscriptions, final List<SubscriptionBaseEvent> cancelEvents, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
@@ -647,13 +624,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
                 final UUID subscriptionId = subscription.getId();
 
-                final List<SubscriptionBaseEvent> changeEventsTweakedWithMigrateBilling = reinsertFutureMigrateBillingEventOnChangeFromTransaction(subscriptionId,
-                                                                                                                                                   changeEvents,
-                                                                                                                                                   entitySqlDaoWrapperFactory,
-                                                                                                                                                   context);
-                cancelFutureEventsFromTransaction(subscriptionId, changeEvents.get(0).getEffectiveDate(), entitySqlDaoWrapperFactory, context);
+                cancelFutureEventsFromTransaction(subscriptionId, changeEvents.get(0).getEffectiveDate(), entitySqlDaoWrapperFactory, false, context);
 
-                for (final SubscriptionBaseEvent cur : changeEventsTweakedWithMigrateBilling) {
+                for (final SubscriptionBaseEvent cur : changeEvents) {
                     transactional.create(new SubscriptionEventModelDao(cur), context);
 
                     final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
@@ -661,7 +634,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 }
 
                 // Notify the Bus of the latest requested change
-                final SubscriptionBaseEvent finalEvent = changeEventsTweakedWithMigrateBilling.get(changeEvents.size() - 1);
+                final SubscriptionBaseEvent finalEvent = changeEvents.get(changeEvents.size() - 1);
                 notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, finalEvent, SubscriptionBaseTransitionType.CHANGE, context);
 
                 // Cancel associated add-ons
@@ -672,93 +645,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         });
     }
 
-    //
-    // This piece of code has been isolated in its own method in order to allow for migrated subscriptions to have their plan to changed prior
-    // to MIGRATE_BILLING; the effect will be to reflect the change from an subscription point of view while ignoring the change until we hit
-    // the begining of the billing, that is when we hit the MIGRATE_BILLING event. If we had a clear separation between subscription and
-    // billing that would not be needed.
-    //
-    // If there is a change of plan prior to a future MIGRATE_BILLING, we want to modify the existing MIGRATE_BILLING so it reflects
-    // the new plan, phase, pricelist; Invoice will only see the MIGRATE_BILLING as things prior to that will be ignored, so we need to make sure
-    // that event reflects the correct subscription information.
-    //
-    //
-    final List<SubscriptionBaseEvent> reinsertFutureMigrateBillingEventOnChangeFromTransaction(final UUID subscriptionId, final List<SubscriptionBaseEvent> changeEvents, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) {
-        final SubscriptionEventModelDao migrateBillingEvent = findFutureEventFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, EventType.API_USER, ApiEventType.MIGRATE_BILLING, context);
-        if (migrateBillingEvent == null) {
-            // No future migrate billing : returns same list
-            return changeEvents;
-        }
-
-        String prevPlan = null;
-        String prevPhase = null;
-        String prevPriceList = null;
-        String curPlan = null;
-        String curPhase = null;
-        String curPriceList = null;
-        for (SubscriptionBaseEvent cur : changeEvents) {
-            switch (cur.getType()) {
-                case API_USER:
-                    final ApiEvent apiEvent = (ApiEvent) cur;
-                    curPlan = apiEvent.getEventPlan();
-                    curPhase = apiEvent.getEventPlanPhase();
-                    curPriceList = apiEvent.getPriceList();
-                    break;
-
-                case PHASE:
-                    final PhaseEvent phaseEvent = (PhaseEvent) cur;
-                    curPhase = phaseEvent.getPhase();
-                    break;
-
-                default:
-                    throw new SubscriptionBaseError("Unknown event type " + cur.getType());
-            }
-
-            if (cur.getEffectiveDate().compareTo(migrateBillingEvent.getEffectiveDate()) > 0) {
-                if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getApiEventType() == ApiEventType.CHANGE) {
-                    // This is an EOT change that is occurring after the MigrateBilling : returns same list
-                    return changeEvents;
-                }
-                // We found the first event after the migrate billing
-                break;
-            }
-            prevPlan = curPlan;
-            prevPhase = curPhase;
-            prevPriceList = curPriceList;
-        }
-
-        if (prevPlan != null) {
-            // Create the new MIGRATE_BILLING with same effectiveDate but new plan information
-            final DateTime now = clock.getUTCNow();
-            final ApiEventBuilder builder = new ApiEventBuilder()
-                    .setActive(true)
-                    .setApiEventType(ApiEventType.MIGRATE_BILLING)
-                    .setFromDisk(true)
-                    .setTotalOrdering(migrateBillingEvent.getTotalOrdering())
-                    .setUuid(UUIDs.randomUUID())
-                    .setSubscriptionId(migrateBillingEvent.getSubscriptionId())
-                    .setCreatedDate(now)
-                    .setUpdatedDate(now)
-                    .setEffectiveDate(migrateBillingEvent.getEffectiveDate())
-                    .setActiveVersion(migrateBillingEvent.getCurrentVersion())
-                    .setEventPlan(prevPlan)
-                    .setEventPlanPhase(prevPhase)
-                    .setEventPriceList(prevPriceList);
-
-            final SubscriptionBaseEvent newMigrateBillingEvent = new ApiEventMigrateBilling(builder);
-            changeEvents.add(newMigrateBillingEvent);
-
-            Collections.sort(changeEvents, new Comparator<SubscriptionBaseEvent>() {
-                @Override
-                public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
-                    return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
-                }
-            });
-        }
-
-        return changeEvents;
-    }
-
     private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final List<SubscriptionEventModelDao> models) {
         final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
             @Override
@@ -787,7 +673,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     private void cancelSubscriptionFromTransaction(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent cancelEvent, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context, final int seqId)
             throws EntityPersistenceException {
         final UUID subscriptionId = subscription.getId();
-        cancelFutureEventsFromTransaction(subscriptionId, cancelEvent.getEffectiveDate(), entitySqlDaoWrapperFactory, context);
+        cancelFutureEventsFromTransaction(subscriptionId, cancelEvent.getEffectiveDate(), entitySqlDaoWrapperFactory, true, context);
         entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).create(new SubscriptionEventModelDao(cancelEvent), context);
 
         final boolean isBusEvent = cancelEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0;
@@ -801,10 +687,18 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         cancelFutureEventFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, EventType.PHASE, null, context);
     }
 
-    private void cancelFutureEventsFromTransaction(final UUID subscriptionId, final DateTime effectiveDate, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) {
-        final List<SubscriptionEventModelDao> eventModels = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), effectiveDate.toDate(), context);
+    private void cancelFutureEventsFromTransaction(final UUID subscriptionId, final DateTime effectiveDate, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final boolean includingBCDChange, final InternalCallContext context) {
+        final List<SubscriptionEventModelDao> eventModels = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getFutureOrPresentActiveEventForSubscription(subscriptionId.toString(), effectiveDate.toDate(), context);
         for (final SubscriptionEventModelDao cur : eventModels) {
-            unactivateEventFromTransaction(cur, entitySqlDaoWrapperFactory, context);
+
+            // Skip CREATE event (because of date equality in the query and we don't want to invalidate CREATE event that match a CANCEL event)
+            if (cur.getEventType() == EventType.API_USER && (cur.getUserType()== ApiEventType.CREATE || cur.getUserType()== ApiEventType.TRANSFER)) {
+                continue;
+            }
+
+            if (includingBCDChange || cur.getEventType() != EventType.BCD_UPDATE) {
+                unactivateEventFromTransaction(cur, entitySqlDaoWrapperFactory, context);
+            }
         }
     }
 
@@ -819,6 +713,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
         SubscriptionEventModelDao futureEvent = null;
         final Date now = clock.getUTCNow().toDate();
+
         final List<SubscriptionEventModelDao> eventModels = dao.become(SubscriptionEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
         for (final SubscriptionEventModelDao cur : eventModels) {
             if (cur.getEventType() == type &&
@@ -877,18 +772,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         }
 
         // Make sure BasePlan -- if exists-- is first
-        Collections.sort(input, new Comparator<SubscriptionBase>() {
-            @Override
-            public int compare(final SubscriptionBase o1, final SubscriptionBase o2) {
-                if (o1.getCategory() == ProductCategory.BASE) {
-                    return -1;
-                } else if (o2.getCategory() == ProductCategory.BASE) {
-                    return 1;
-                } else {
-                    return ((DefaultSubscriptionBase) o1).getAlignStartDate().compareTo(((DefaultSubscriptionBase) o2).getAlignStartDate());
-                }
-            }
-        });
+        Collections.sort(input, DefaultSubscriptionInternalApi.SUBSCRIPTIONS_COMPARATOR);
 
         final List<ApiEventChange> baseChangeEvents = new LinkedList<ApiEventChange>();
         ApiEventCancel baseCancellationEvent = null;
@@ -943,7 +827,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                         final DateTime now = clock.getUTCNow();
                         final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
                                                                                                   .setSubscriptionId(reloaded.getId())
-                                                                                                  .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
                                                                                                   .setEffectiveDate(baseTriggerEventForAddOnCancellation.getEffectiveDate())
                                                                                                   .setCreatedDate(baseTriggerEventForAddOnCancellation.getCreatedDate())
                                                                                                   // This event is only there to indicate the ADD_ON is future canceled, but it is not there
@@ -983,9 +866,20 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                 // Set total ordering value of the fake dryRun event to make sure billing events are correctly ordered
                 final SubscriptionBaseEvent curAdjustedDryRun;
                 if (!events.isEmpty()) {
-                    final EventBaseBuilder eventBuilder = (curDryRun.getType() == EventType.API_USER) ?
-                                                          new ApiEventBuilder((ApiEvent) curDryRun) :
-                                                          new PhaseEventBuilder((PhaseEvent) curDryRun);
+
+                    final EventBaseBuilder eventBuilder;
+                    switch (curDryRun.getType()) {
+                        case PHASE:
+                            eventBuilder = new PhaseEventBuilder((PhaseEvent) curDryRun);
+                            break;
+                        case BCD_UPDATE:
+                            eventBuilder = new BCDEventBuilder((BCDEvent) curDryRun);
+                            break;
+                        case API_USER:
+                        default:
+                            eventBuilder = new ApiEventBuilder((ApiEvent) curDryRun);
+                            break;
+                    }
                     eventBuilder.setTotalOrdering(events.get(events.size() - 1).getTotalOrdering() + 1);
 
                     curAdjustedDryRun = eventBuilder.build();
@@ -998,57 +892,61 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     @Override
-    public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
+    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleTransferData bundleTransferData,
+                         final List<TransferCancelData> transferCancelData, final InternalCallContext fromContext, final InternalCallContext toContext) {
+
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
 
-                for (final BundleMigrationData curBundle : accountData.getData()) {
-                    migrateBundleDataFromTransaction(curBundle, transactional, entitySqlDaoWrapperFactory, context);
+                // Cancel the subscriptions for the old bundle
+                for (final TransferCancelData cancel : transferCancelData) {
+                    cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), entitySqlDaoWrapperFactory, fromContext, 0);
                 }
+
+                transferBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, toContext);
                 return null;
             }
         });
     }
 
     @Override
-    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData bundleTransferData,
-                         final List<TransferCancelData> transferCancelData, final InternalCallContext fromContext, final InternalCallContext toContext) {
-
+    public void updateBundleExternalKey(final UUID bundleId, final String externalKey, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
 
-                // Cancel the subscriptions for the old bundle
-                for (final TransferCancelData cancel : transferCancelData) {
-                    cancelSubscriptionFromTransaction(cancel.getSubscription(), cancel.getCancelEvent(), entitySqlDaoWrapperFactory, fromContext, 0);
-                }
-
-                migrateBundleDataFromTransaction(bundleTransferData, transactional, entitySqlDaoWrapperFactory, toContext);
+                final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
+                bundleSqlDao.updateBundleExternalKey(bundleId.toString(), externalKey, contextWithUpdatedDate(context));
                 return null;
             }
         });
     }
 
     @Override
-    public void updateBundleExternalKey(final UUID bundleId, final String externalKey, final InternalCallContext context) {
+    public void createBCDChangeEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent bcdEvent, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final SubscriptionEventSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
+                transactional.create(new SubscriptionEventModelDao(bcdEvent), context);
+
+                // Notify the Bus
+                notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, bcdEvent, SubscriptionBaseTransitionType.BCD_CHANGE, context);
+                final boolean isBusEvent = bcdEvent.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0;
+                recordBusOrFutureNotificationFromTransaction(subscription, bcdEvent, entitySqlDaoWrapperFactory, isBusEvent, 0, context);
 
-                final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
-                bundleSqlDao.updateBundleExternalKey(bundleId.toString(), externalKey, contextWithUpdatedDate(context));
                 return null;
             }
         });
+
     }
 
     private DefaultSubscriptionBase createSubscriptionForInternalUse(final SubscriptionBase shellSubscription, final List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
         final DefaultSubscriptionBase result = new DefaultSubscriptionBase(new SubscriptionBuilder(((DefaultSubscriptionBase) shellSubscription)), null, clock);
         if (events.size() > 0) {
-            final Catalog fullCatalog = catalogService.getFullCatalog(context);
+            final Catalog fullCatalog = catalogService.getFullCatalog(true, true, context);
             result.rebuildTransitions(events, fullCatalog);
         }
         return result;
@@ -1094,13 +992,15 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
                                                      final SubscriptionBaseEvent immediateEvent, final int seqId, final InternalCallContext context) {
         try {
             final SubscriptionBaseTransitionData transition = subscription.getTransitionFromEvent(immediateEvent, seqId);
-            final BusEvent busEvent = new DefaultEffectiveSubscriptionEvent(transition,
-                                                                            subscription.getAlignStartDate(),
-                                                                            context.getUserToken(),
-                                                                            context.getAccountRecordId(),
-                                                                            context.getTenantRecordId());
-
-            eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
+            if (transition != null) {
+                final BusEvent busEvent = new DefaultEffectiveSubscriptionEvent(transition,
+                                                                                subscription.getAlignStartDate(),
+                                                                                context.getUserToken(),
+                                                                                context.getAccountRecordId(),
+                                                                                context.getTenantRecordId());
+
+                eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getHandle().getConnection());
+            }
         } catch (final EventBusException e) {
             log.warn("Failed to post effective event for subscriptionId='{}'", subscription.getId(), e);
         }
@@ -1128,8 +1028,8 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         }
     }
 
-    private void migrateBundleDataFromTransaction(final BundleMigrationData bundleTransferData, final SubscriptionEventSqlDao transactional,
-                                                  final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException {
+    private void transferBundleDataFromTransaction(final BundleTransferData bundleTransferData, final SubscriptionEventSqlDao transactional,
+                                                   final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException {
 
         final SubscriptionSqlDao transSubDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
         final BundleSqlDao transBundleDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
@@ -1142,7 +1042,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             return;
         }
 
-        for (final SubscriptionMigrationData curSubscription : bundleTransferData.getSubscriptions()) {
+        for (final SubscriptionTransferData curSubscription : bundleTransferData.getSubscriptions()) {
             final DefaultSubscriptionBase subData = curSubscription.getData();
             for (final SubscriptionBaseEvent curEvent : curSubscription.getInitialEvents()) {
                 transactional.create(new SubscriptionEventModelDao(curEvent), context);
@@ -1155,7 +1055,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, SubscriptionBaseTransitionType.MIGRATE_BILLING, context);
+            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subData, finalEvent, SubscriptionBaseTransitionType.TRANSFER, context);
         }
 
         transBundleDao.create(new SubscriptionBundleModelDao(bundleData), context);
@@ -1172,7 +1072,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             allEvents.addAll(subscriptionWithNewEvent.getEvents());
         }
         allEvents.add(newEvent);
-        subscriptionWithNewEvent.rebuildTransitions(allEvents, catalogService.getFullCatalog(context));
+        subscriptionWithNewEvent.rebuildTransitions(allEvents, catalogService.getFullCatalog(true, true, context));
         return subscriptionWithNewEvent;
     }
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
index 73d4492..9063bba 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionEventModelDao.java
@@ -19,24 +19,16 @@ package org.killbill.billing.subscription.engine.dao.model;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-
+import org.killbill.billing.subscription.events.EventBaseBuilder;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent.EventType;
-import org.killbill.billing.subscription.events.EventBaseBuilder;
+import org.killbill.billing.subscription.events.bcd.BCDEvent;
+import org.killbill.billing.subscription.events.bcd.BCDEventBuilder;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEventBuilder;
-import org.killbill.billing.subscription.events.phase.PhaseEventData;
 import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventBuilder;
-import org.killbill.billing.subscription.events.user.ApiEventCancel;
-import org.killbill.billing.subscription.events.user.ApiEventChange;
-import org.killbill.billing.subscription.events.user.ApiEventCreate;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateBilling;
-import org.killbill.billing.subscription.events.user.ApiEventMigrateSubscription;
-import org.killbill.billing.subscription.events.user.ApiEventReCreate;
-import org.killbill.billing.subscription.events.user.ApiEventTransfer;
 import org.killbill.billing.subscription.events.user.ApiEventType;
-import org.killbill.billing.subscription.events.user.ApiEventUncancel;
 import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -47,13 +39,12 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
     private long totalOrdering;
     private EventType eventType;
     private ApiEventType userType;
-    private DateTime requestedDate; // deprecated (similar to effectiveDate)
     private DateTime effectiveDate;
     private UUID subscriptionId;
     private String planName;
     private String phaseName;
     private String priceListName;
-    private long currentVersion;
+    private int billingCycleDayLocal;
     private boolean isActive;
 
     public SubscriptionEventModelDao() {
@@ -61,20 +52,19 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
     }
 
     public SubscriptionEventModelDao(final UUID id, final long totalOrdering, final EventType eventType, final ApiEventType userType,
-                                     final DateTime requestedDate, final DateTime effectiveDate, final UUID subscriptionId,
-                                     final String planName, final String phaseName, final String priceListName, final long currentVersion,
+                                     final DateTime effectiveDate, final UUID subscriptionId,
+                                     final String planName, final String phaseName, final String priceListName, final int billingCycleDayLocal,
                                      final boolean active, final DateTime createDate, final DateTime updateDate) {
         super(id, createDate, updateDate);
         this.totalOrdering = totalOrdering;
         this.eventType = eventType;
         this.userType = userType;
-        this.requestedDate = requestedDate;
         this.effectiveDate = effectiveDate;
         this.subscriptionId = subscriptionId;
         this.planName = planName;
         this.phaseName = phaseName;
         this.priceListName = priceListName;
-        this.currentVersion = currentVersion;
+        this.billingCycleDayLocal = billingCycleDayLocal;
         this.isActive = active;
     }
 
@@ -83,13 +73,18 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         this.totalOrdering = src.getTotalOrdering();
         this.eventType = src.getType();
         this.userType = eventType == EventType.API_USER ? ((ApiEvent) src).getApiEventType() : null;
-        this.requestedDate = src.getEffectiveDate();
         this.effectiveDate = src.getEffectiveDate();
         this.subscriptionId = src.getSubscriptionId();
         this.planName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlan() : null;
-        this.phaseName = eventType == EventType.API_USER ? ((ApiEvent) src).getEventPlanPhase() : ((PhaseEvent) src).getPhase();
+        if (eventType == EventType.API_USER) {
+            this.phaseName = ((ApiEvent) src).getEventPlanPhase();
+        } else if (eventType == EventType.PHASE) {
+            this.phaseName = ((PhaseEvent) src).getPhase();
+        } else {
+            this.phaseName = null;
+        }
         this.priceListName = eventType == EventType.API_USER ? ((ApiEvent) src).getPriceList() : null;
-        this.currentVersion = src.getActiveVersion();
+        this.billingCycleDayLocal = eventType == EventType.BCD_UPDATE ? ((BCDEvent) src).getBillCycleDayLocal() : 0;
         this.isActive = src.isActive();
     }
 
@@ -105,10 +100,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         return userType;
     }
 
-    public DateTime getRequestedDate() {
-        return requestedDate;
-    }
-
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
@@ -129,8 +120,8 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         return priceListName;
     }
 
-    public long getCurrentVersion() {
-        return currentVersion;
+    public int getBillingCycleDayLocal() {
+        return billingCycleDayLocal;
     }
 
     // TODO required for jdbi binder
@@ -154,10 +145,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         this.userType = userType;
     }
 
-    public void setRequestedDate(final DateTime requestedDate) {
-        this.requestedDate = requestedDate;
-    }
-
     public void setEffectiveDate(final DateTime effectiveDate) {
         this.effectiveDate = effectiveDate;
     }
@@ -178,8 +165,9 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         this.priceListName = priceListName;
     }
 
-    public void setCurrentVersion(final long currentVersion) {
-        this.currentVersion = currentVersion;
+
+    public void setBillingCycleDayLocal(final int billingCycleDayLocal) {
+        this.billingCycleDayLocal = billingCycleDayLocal;
     }
 
     public void setIsActive(final boolean isActive) {
@@ -192,17 +180,21 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
             return null;
         }
 
-        final EventBaseBuilder<?> base = ((src.getEventType() == EventType.PHASE) ?
-                                          new PhaseEventBuilder() :
-                                          new ApiEventBuilder())
-                .setTotalOrdering(src.getTotalOrdering())
-                .setUuid(src.getId())
-                .setSubscriptionId(src.getSubscriptionId())
-                .setCreatedDate(src.getCreatedDate())
-                .setUpdatedDate(src.getUpdatedDate())
-                .setEffectiveDate(src.getEffectiveDate())
-                .setActiveVersion(src.getCurrentVersion())
-                .setActive(src.isActive());
+        final EventBaseBuilder<?> base;
+        if (src.getEventType() == EventType.PHASE) {
+            base = new PhaseEventBuilder();
+        } else if (src.getEventType() == EventType.BCD_UPDATE) {
+            base = new BCDEventBuilder();
+        } else {
+            base = new ApiEventBuilder();
+        }
+        base.setTotalOrdering(src.getTotalOrdering())
+            .setUuid(src.getId())
+            .setSubscriptionId(src.getSubscriptionId())
+            .setCreatedDate(src.getCreatedDate())
+            .setUpdatedDate(src.getUpdatedDate())
+            .setEffectiveDate(src.getEffectiveDate())
+            .setActive(src.isActive());
 
         SubscriptionBaseEvent result;
         if (src.getEventType() == EventType.PHASE) {
@@ -216,6 +208,8 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
                     .setApiEventType(src.getUserType())
                     .setFromDisk(true);
             result = builder.build();
+        } else if (src.getEventType() == EventType.BCD_UPDATE) {
+            result = (new BCDEventBuilder(base).setBillCycleDayLocal(src.getBillingCycleDayLocal())).build();
         } else {
             throw new SubscriptionBaseError(String.format("Can't figure out event %s", src.getEventType()));
         }
@@ -229,13 +223,12 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         sb.append("{totalOrdering=").append(totalOrdering);
         sb.append(", eventType=").append(eventType);
         sb.append(", userType=").append(userType);
-        sb.append(", requestedDate=").append(requestedDate);
         sb.append(", effectiveDate=").append(effectiveDate);
         sb.append(", subscriptionId=").append(subscriptionId);
         sb.append(", planName='").append(planName).append('\'');
         sb.append(", phaseName='").append(phaseName).append('\'');
         sb.append(", priceListName='").append(priceListName).append('\'');
-        sb.append(", currentVersion=").append(currentVersion);
+        sb.append(", billingCycleDayLocal=").append(billingCycleDayLocal);
         sb.append(", isActive=").append(isActive);
         sb.append('}');
         return sb.toString();
@@ -255,9 +248,6 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
 
         final SubscriptionEventModelDao that = (SubscriptionEventModelDao) o;
 
-        if (currentVersion != that.currentVersion) {
-            return false;
-        }
         if (isActive != that.isActive) {
             return false;
         }
@@ -279,16 +269,15 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         if (priceListName != null ? !priceListName.equals(that.priceListName) : that.priceListName != null) {
             return false;
         }
-        if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
-            return false;
-        }
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
             return false;
         }
         if (userType != that.userType) {
             return false;
         }
-
+        if (billingCycleDayLocal != that.billingCycleDayLocal) {
+            return false;
+        }
         return true;
     }
 
@@ -298,13 +287,11 @@ public class SubscriptionEventModelDao extends EntityModelDaoBase implements Ent
         result = 31 * result + (int) (totalOrdering ^ (totalOrdering >>> 32));
         result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
         result = 31 * result + (userType != null ? userType.hashCode() : 0);
-        result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
         result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
         result = 31 * result + (subscriptionId != null ? subscriptionId.hashCode() : 0);
         result = 31 * result + (planName != null ? planName.hashCode() : 0);
         result = 31 * result + (phaseName != null ? phaseName.hashCode() : 0);
         result = 31 * result + (priceListName != null ? priceListName.hashCode() : 0);
-        result = 31 * result + (int) (currentVersion ^ (currentVersion >>> 32));
         result = 31 * result + (isActive ? 1 : 0);
         return result;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
index d9d778c..b53a9cd 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/model/SubscriptionModelDao.java
@@ -35,25 +35,25 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
     private ProductCategory category;
     private DateTime startDate;
     private DateTime bundleStartDate;
-    private long activeVersion;
     private DateTime chargedThroughDate;
+    private boolean migrated;
 
     public SubscriptionModelDao() { /* For the DAO mapper */ }
 
     public SubscriptionModelDao(final UUID id, final UUID bundleId, final ProductCategory category, final DateTime startDate, final DateTime bundleStartDate,
-                                final long activeVersion, final DateTime chargedThroughDate, final DateTime createdDate, final DateTime updateDate) {
+                                final DateTime chargedThroughDate, final boolean migrated, final DateTime createdDate, final DateTime updateDate) {
         super(id, createdDate, updateDate);
         this.bundleId = bundleId;
         this.category = category;
         this.startDate = startDate;
         this.bundleStartDate = bundleStartDate;
-        this.activeVersion = activeVersion;
         this.chargedThroughDate = chargedThroughDate;
+        this.migrated = migrated;
     }
 
     public SubscriptionModelDao(final DefaultSubscriptionBase src) {
-        this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(), src.getActiveVersion(),
-             src.getChargedThroughDate(), src.getCreatedDate(), src.getUpdatedDate());
+        this(src.getId(), src.getBundleId(), src.getCategory(), src.getAlignStartDate(), src.getBundleStartDate(),
+             src.getChargedThroughDate(), src.isMigrated(), src.getCreatedDate(), src.getUpdatedDate());
     }
 
     public UUID getBundleId() {
@@ -72,9 +72,6 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         return bundleStartDate;
     }
 
-    public long getActiveVersion() {
-        return activeVersion;
-    }
 
     public DateTime getChargedThroughDate() {
         return chargedThroughDate;
@@ -96,14 +93,19 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         this.bundleStartDate = bundleStartDate;
     }
 
-    public void setActiveVersion(final long activeVersion) {
-        this.activeVersion = activeVersion;
-    }
 
     public void setChargedThroughDate(final DateTime chargedThroughDate) {
         this.chargedThroughDate = chargedThroughDate;
     }
 
+    public boolean isMigrated() {
+        return migrated;
+    }
+
+    public void setMigrated(final boolean migrated) {
+        this.migrated = migrated;
+    }
+
     public static SubscriptionBase toSubscription(final SubscriptionModelDao src) {
         if (src == null) {
             return null;
@@ -116,8 +118,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
                                             .setUpdatedDate(src.getUpdatedDate())
                                             .setBundleStartDate(src.getBundleStartDate())
                                             .setAlignStartDate(src.getStartDate())
-                                            .setActiveVersion(src.getActiveVersion())
                                             .setChargedThroughDate(src.getChargedThroughDate())
+                                            .setMigrated(src.isMigrated())
                                             .setCreatedDate(src.getCreatedDate())
                                             .setUpdatedDate(src.getUpdatedDate()));
     }
@@ -130,8 +132,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         sb.append(", category=").append(category);
         sb.append(", startDate=").append(startDate);
         sb.append(", bundleStartDate=").append(bundleStartDate);
-        sb.append(", activeVersion=").append(activeVersion);
         sb.append(", chargedThroughDate=").append(chargedThroughDate);
+        sb.append(", migrated=").append(migrated);
         sb.append('}');
         return sb.toString();
     }
@@ -150,9 +152,6 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
 
         final SubscriptionModelDao that = (SubscriptionModelDao) o;
 
-        if (activeVersion != that.activeVersion) {
-            return false;
-        }
         if (bundleId != null ? !bundleId.equals(that.bundleId) : that.bundleId != null) {
             return false;
         }
@@ -165,6 +164,9 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         if (chargedThroughDate != null ? !chargedThroughDate.equals(that.chargedThroughDate) : that.chargedThroughDate != null) {
             return false;
         }
+        if (migrated != that.migrated) {
+            return false;
+        }
         if (startDate != null ? !startDate.equals(that.startDate) : that.startDate != null) {
             return false;
         }
@@ -179,8 +181,8 @@ public class SubscriptionModelDao extends EntityModelDaoBase implements EntityMo
         result = 31 * result + (category != null ? category.hashCode() : 0);
         result = 31 * result + (startDate != null ? startDate.hashCode() : 0);
         result = 31 * result + (bundleStartDate != null ? bundleStartDate.hashCode() : 0);
-        result = 31 * result + (int) (activeVersion ^ (activeVersion >>> 32));
         result = 31 * result + (chargedThroughDate != null ? chargedThroughDate.hashCode() : 0);
+        result = 31 * result + Boolean.valueOf(migrated).hashCode();
         return result;
     }
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
index 487fae3..846be18 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
@@ -25,8 +25,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData;
-import org.killbill.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
 import org.killbill.billing.subscription.api.transfer.TransferCancelData;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
@@ -84,8 +83,6 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
 
     public void createSubscriptionWithAddOns(List<DefaultSubscriptionBase> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, InternalCallContext context);
 
-    public void recreateSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> recreateEvents, InternalCallContext context);
-
     public void cancelSubscriptionsOnBasePlanEvent(DefaultSubscriptionBase subscription, SubscriptionBaseEvent event, List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
 
     public void cancelSubscriptions(List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
@@ -94,11 +91,10 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
 
     public void changePlan(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> changeEvents, List<DefaultSubscriptionBase> subscriptionsToBeCancelled, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
 
-    public void migrate(UUID accountId, AccountMigrationData data, InternalCallContext context);
-
-    public void transfer(UUID srcAccountId, UUID destAccountId, BundleMigrationData data, List<TransferCancelData> transferCancelData, InternalCallContext fromContext, InternalCallContext toContext);
+    public void transfer(UUID srcAccountId, UUID destAccountId, BundleTransferData data, List<TransferCancelData> transferCancelData, InternalCallContext fromContext, InternalCallContext toContext);
 
     public void updateBundleExternalKey(UUID bundleId, String externalKey, InternalCallContext context);
 
+    public void createBCDChangeEvent(DefaultSubscriptionBase subscription, SubscriptionBaseEvent bcdEvent, InternalCallContext context);
 }
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
index 177df8b..0e2a8ab 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.java
@@ -18,7 +18,10 @@ package org.killbill.billing.subscription.engine.dao;
 
 import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
+import org.joda.time.DateTime;
+import org.killbill.billing.entity.EntityPersistenceException;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
@@ -41,28 +44,20 @@ public interface SubscriptionEventSqlDao extends EntitySqlDao<SubscriptionEventM
     public void unactiveEvent(@Bind("id") String id,
                               @BindBean final InternalCallContext context);
 
-    @SqlUpdate
-    @Audited(ChangeType.UPDATE)
-    public void reactiveEvent(@Bind("id") String id,
-                              @BindBean final InternalCallContext context);
-
-    @SqlUpdate
-    @Audited(ChangeType.UPDATE)
-    public void updateVersion(@Bind("id") String id,
-                              @Bind("currentVersion") Long currentVersion,
-                              @BindBean final InternalCallContext context);
-
     @SqlQuery
     public List<SubscriptionEventModelDao> getFutureActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
-                                                                              @Bind("now") Date now,
-                                                                              @BindBean final InternalTenantContext context);
+                                                                               @Bind("now") Date now,
+                                                                               @BindBean final InternalTenantContext context);
+
+    @SqlQuery
+    public List<SubscriptionEventModelDao> getFutureOrPresentActiveEventForSubscription(@Bind("subscriptionId") String subscriptionId,
+                                                                               @Bind("now") Date now,
+                                                                               @BindBean final InternalTenantContext context);
 
     @SqlQuery
     public List<SubscriptionEventModelDao> getEventsForSubscription(@Bind("subscriptionId") String subscriptionId,
                                                                     @BindBean final InternalTenantContext context);
 
-
     @SqlQuery
     public List<SubscriptionEventModelDao> getFutureActiveEventsForAccount(@Bind("now") Date now, @BindBean final InternalTenantContext context);
-
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
index 76baf12..b47b5ba 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.java
@@ -45,15 +45,4 @@ public interface SubscriptionSqlDao extends EntitySqlDao<SubscriptionModelDao, S
     public void updateChargedThroughDate(@Bind("id") String id, @Bind("chargedThroughDate") Date chargedThroughDate,
                                          @BindBean final InternalCallContext context);
 
-    @SqlUpdate
-    @Audited(ChangeType.UPDATE)
-    void updateActiveVersion(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
-                             @BindBean final InternalCallContext context);
-
-    @SqlUpdate
-    @Audited(ChangeType.UPDATE)
-    public void updateForRepair(@Bind("id") String id, @Bind("activeVersion") long activeVersion,
-                                @Bind("startDate") Date startDate,
-                                @Bind("bundleStartDate") Date bundleStartDate,
-                                @BindBean final InternalCallContext context);
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventBuilder.java
new file mode 100644
index 0000000..7c0926d
--- /dev/null
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.subscription.events.bcd;
+
+import org.killbill.billing.subscription.events.EventBaseBuilder;
+import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
+
+public class BCDEventBuilder extends EventBaseBuilder<BCDEventBuilder> {
+
+    private Integer billCycleDayLocal;
+
+    public BCDEventBuilder() {
+        super();
+    }
+
+
+    public BCDEventBuilder(final BCDEvent event) {
+        super(event);
+        this.billCycleDayLocal = event.getBillCycleDayLocal();
+    }
+
+    public BCDEventBuilder(final EventBaseBuilder<?> base) {
+        super(base);
+    }
+
+
+    @Override
+    public SubscriptionBaseEvent build() {
+        return new BCDEventData(this);
+    }
+
+    public Integer getBillCycleDayLocal() {
+        return billCycleDayLocal;
+    }
+
+    public BCDEventBuilder setBillCycleDayLocal(final Integer billCycleDayLocal) {
+        this.billCycleDayLocal = billCycleDayLocal;
+        return this;
+    }
+}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventData.java b/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventData.java
new file mode 100644
index 0000000..c935d6c
--- /dev/null
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/bcd/BCDEventData.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.subscription.events.bcd;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
+import org.killbill.billing.subscription.events.EventBase;
+
+public class BCDEventData extends EventBase implements BCDEvent {
+
+    private final Integer billCycleDayLocal;
+
+    public BCDEventData(final BCDEventBuilder builder) {
+        super(builder);
+        this.billCycleDayLocal = builder.getBillCycleDayLocal();
+    }
+
+    @Override
+    public EventType getType() {
+        return EventType.BCD_UPDATE;
+    }
+
+    @Override
+    public String toString() {
+        return "BCDEventData {" +
+               "uuid=" + getId() +
+               ", subscriptionId=" + getSubscriptionId() +
+               ", createdDate=" + getCreatedDate() +
+               ", updatedDate=" + getUpdatedDate() +
+               ", effectiveDate=" + getEffectiveDate() +
+               ", totalOrdering=" + getTotalOrdering() +
+               ", isActive=" + isActive() +
+               '}';
+    }
+
+    // Hack until we introduce a proper field for that
+    @Override
+    public Integer getBillCycleDayLocal() {
+        return billCycleDayLocal;
+    }
+
+    public static BCDEvent createBCDEvent(final DefaultSubscriptionBase subscription, final DateTime effectiveDate, final int billCycleDayLocal) {
+        return new BCDEventData(new BCDEventBuilder()
+                                        .setSubscriptionId(subscription.getId())
+                                        .setEffectiveDate(effectiveDate)
+                                        .setActive(true)
+                                        .setBillCycleDayLocal(billCycleDayLocal));
+    }
+
+}
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
index ed379f8..334d524 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBase.java
@@ -31,7 +31,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
     private final DateTime effectiveDate;
 
     private final long totalOrdering;
-    private final long activeVersion;
     private final boolean isActive;
 
     public EventBase(final EventBaseBuilder<?> builder) {
@@ -41,7 +40,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
         this.createdDate = builder.getCreatedDate();
         this.updatedDate = builder.getUpdatedDate();
         this.effectiveDate = builder.getEffectiveDate();
-        this.activeVersion = builder.getActiveVersion();
         this.isActive = builder.isActive();
     }
 
@@ -76,11 +74,6 @@ public abstract class EventBase implements SubscriptionBaseEvent {
     }
 
     @Override
-    public long getActiveVersion() {
-        return activeVersion;
-    }
-
-    @Override
     public boolean isActive() {
         return isActive;
     }
@@ -89,7 +82,7 @@ public abstract class EventBase implements SubscriptionBaseEvent {
     // Really used for unit tests only as the sql implementation relies on date first and then event insertion
     //
     // Order first by:
-    // - effectiveDate, followed by processedDate, requestedDate
+    // - effectiveDate, followed by processedDate
     // - if all dates are equal-- unlikely, we first return PHASE EVENTS
     // - If both events are User events, return the first CREATE, CHANGE,... as specified by ApiEventType
     // - If all that is not enough return consistent by random ordering based on UUID
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
index 4dd7b96..41fd074 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/EventBaseBuilder.java
@@ -31,7 +31,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
     private DateTime updatedDate;
     private DateTime effectiveDate;
 
-    private long activeVersion;
     private boolean isActive;
 
     public EventBaseBuilder() {
@@ -46,7 +45,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
         this.effectiveDate = event.getEffectiveDate();
         this.createdDate = event.getCreatedDate();
         this.updatedDate = event.getUpdatedDate();
-        this.activeVersion = event.getActiveVersion();
         this.isActive = event.isActive();
         this.totalOrdering = event.getTotalOrdering();
     }
@@ -57,7 +55,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
         this.effectiveDate = copy.effectiveDate;
         this.createdDate = copy.getCreatedDate();
         this.updatedDate = copy.getUpdatedDate();
-        this.activeVersion = copy.activeVersion;
         this.isActive = copy.isActive;
         this.totalOrdering = copy.totalOrdering;
     }
@@ -92,11 +89,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
         return (T) this;
     }
 
-    public T setActiveVersion(final long activeVersion) {
-        this.activeVersion = activeVersion;
-        return (T) this;
-    }
-
     public T setActive(final boolean isActive) {
         this.isActive = isActive;
         return (T) this;
@@ -125,11 +117,6 @@ public abstract class EventBaseBuilder<T extends EventBaseBuilder<T>> {
     public DateTime getEffectiveDate() {
         return effectiveDate;
     }
-
-    public long getActiveVersion() {
-        return activeVersion;
-    }
-
     public boolean isActive() {
         return isActive;
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
index 07afcdf..acf9375 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/phase/PhaseEventData.java
@@ -21,7 +21,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
-import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.events.EventBase;
 
 
@@ -51,18 +50,16 @@ public class PhaseEventData extends EventBase implements PhaseEvent {
                 + ", getType()=" + getType()
                 + ", getPhase()=" + getPhase()
                 + ", getEffectiveDate()=" + getEffectiveDate()
-                + ", getActiveVersion()=" + getActiveVersion()
                 + ", getSubscriptionId()=" + getSubscriptionId()
                 + ", isActive()=" + isActive() + "]\n";
     }
 
-    public static PhaseEvent createNextPhaseEvent(final UUID subscriptionId, final long activeVersion, final String phaseName, final DateTime effectiveDate) {
+    public static PhaseEvent createNextPhaseEvent(final UUID subscriptionId, final String phaseName, final DateTime effectiveDate) {
         return (phaseName == null) ?
                 null :
                 new PhaseEventData(new PhaseEventBuilder()
                                            .setSubscriptionId(subscriptionId)
                                            .setEffectiveDate(effectiveDate)
-                                           .setActiveVersion(activeVersion)
                                            .setPhaseName(phaseName));
     }
 }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java b/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
index 1f81e63..36e7f24 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/SubscriptionBaseEvent.java
@@ -27,15 +27,14 @@ public interface SubscriptionBaseEvent extends Comparable<SubscriptionBaseEvent>
 
     public enum EventType {
         API_USER,
-        PHASE
+        PHASE,
+        BCD_UPDATE
     }
 
     public EventType getType();
 
     public long getTotalOrdering();
 
-    public long getActiveVersion();
-
     public boolean isActive();
 
     public DateTime getEffectiveDate();
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
index 98f6b9e..70c5d63 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBase.java
@@ -78,7 +78,6 @@ public class ApiEventBase extends EventBase implements ApiEvent {
                + ", getEventPlanPhase()=" + getEventPlanPhase()
                + ", getType()=" + getType()
                + ", getEffectiveDate()=" + getEffectiveDate()
-               + ", getActiveVersion()=" + getActiveVersion()
                + ", getSubscriptionId()=" + getSubscriptionId()
                + ", isActive()=" + isActive() + "]";
     }
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
index 18cedb6..7e3ce7a 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventBuilder.java
@@ -92,20 +92,12 @@ public class ApiEventBuilder extends EventBaseBuilder<ApiEventBuilder> {
         final ApiEventBase result;
         if (apiEventType == ApiEventType.CREATE) {
             result = new ApiEventCreate(this);
-        } else if (apiEventType == ApiEventType.RE_CREATE) {
-            result = new ApiEventReCreate(this);
-        } else if (apiEventType == ApiEventType.MIGRATE_ENTITLEMENT) {
-            result = new ApiEventMigrateSubscription(this);
-        } else if (apiEventType == ApiEventType.MIGRATE_BILLING) {
-            result = new ApiEventMigrateBilling(this);
         } else if (apiEventType == ApiEventType.TRANSFER) {
             result = new ApiEventTransfer(this);
         } else if (apiEventType == ApiEventType.CHANGE) {
             result = new ApiEventChange(this);
         } else if (apiEventType == ApiEventType.CANCEL) {
             result = new ApiEventCancel(this);
-        } else if (apiEventType == ApiEventType.RE_CREATE) {
-            result = new ApiEventReCreate(this);
         } else if (apiEventType == ApiEventType.UNCANCEL) {
             result = new ApiEventUncancel(this);
         } else {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
index 4006a7a..1646243 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/events/user/ApiEventType.java
@@ -20,24 +20,12 @@ import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 
 
 public enum ApiEventType {
-    MIGRATE_ENTITLEMENT {
-        @Override
-        public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-            return SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
-        }
-    },
     CREATE {
         @Override
         public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
             return SubscriptionBaseTransitionType.CREATE;
         }
     },
-    MIGRATE_BILLING {
-        @Override
-        public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-            return SubscriptionBaseTransitionType.MIGRATE_BILLING;
-        }
-    },
     TRANSFER {
         @Override
         public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
@@ -50,12 +38,6 @@ public enum ApiEventType {
             return SubscriptionBaseTransitionType.CHANGE;
         }
     },
-    RE_CREATE {
-        @Override
-        public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-            return SubscriptionBaseTransitionType.RE_CREATE;
-        }
-    },
     CANCEL {
         @Override
         public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java b/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
index e0ba5ab..16dde8b 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/glue/DefaultSubscriptionModule.java
@@ -20,13 +20,10 @@ package org.killbill.billing.subscription.glue;
 
 import org.killbill.billing.glue.SubscriptionModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.subscription.alignment.MigrationPlanAligner;
 import org.killbill.billing.subscription.alignment.PlanAligner;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.DefaultSubscriptionBaseMigrationApi;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
 import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
 import org.killbill.billing.subscription.api.timeline.DefaultSubscriptionBaseTimelineApi;
 import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
@@ -37,7 +34,7 @@ import org.killbill.billing.subscription.engine.addon.AddonUtils;
 import org.killbill.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import org.killbill.billing.subscription.engine.dao.DefaultSubscriptionDao;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
-import org.killbill.billing.util.config.SubscriptionConfig;
+import org.killbill.billing.util.config.definition.SubscriptionConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
@@ -61,11 +58,8 @@ public class DefaultSubscriptionModule extends KillBillModule implements Subscri
         bind(DefaultSubscriptionBaseService.class).asEagerSingleton();
         bind(PlanAligner.class).asEagerSingleton();
         bind(AddonUtils.class).asEagerSingleton();
-        bind(MigrationPlanAligner.class).asEagerSingleton();
-
         installSubscriptionService();
         installSubscriptionTimelineApi();
-        installSubscriptionMigrationApi();
         installSubscriptionInternalApi();
         installSubscriptionTransferApi();
     }
@@ -88,11 +82,6 @@ public class DefaultSubscriptionModule extends KillBillModule implements Subscri
     }
 
     @Override
-    public void installSubscriptionMigrationApi() {
-        bind(SubscriptionBaseMigrationApi.class).to(DefaultSubscriptionBaseMigrationApi.class).asEagerSingleton();
-    }
-
-    @Override
     public void installSubscriptionInternalApi() {
         bind(SubscriptionBaseInternalApi.class).to(DefaultSubscriptionInternalApi.class).asEagerSingleton();
     }
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
index 30137f4..80abc65 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
@@ -4,15 +4,14 @@ DROP TABLE IF EXISTS subscription_events;
 CREATE TABLE subscription_events (
     record_id serial unique,
     id varchar(36) NOT NULL,
-    event_type varchar(9) NOT NULL,
+    event_type varchar(15) NOT NULL,
     user_type varchar(25) DEFAULT NULL,
-    requested_date datetime NOT NULL,
     effective_date datetime NOT NULL,
     subscription_id varchar(36) NOT NULL,
-    plan_name varchar(64) DEFAULT NULL,
-    phase_name varchar(128) DEFAULT NULL,
+    plan_name varchar(255) DEFAULT NULL,
+    phase_name varchar(255) DEFAULT NULL,
     price_list_name varchar(64) DEFAULT NULL,
-    current_version int DEFAULT 1,
+    billing_cycle_day_local int DEFAULT NULL,
     is_active boolean default true,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
@@ -24,7 +23,7 @@ CREATE TABLE subscription_events (
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
 CREATE UNIQUE INDEX subscription_events_id ON subscription_events(id);
 CREATE INDEX idx_ent_1 ON subscription_events(subscription_id, is_active, effective_date);
-CREATE INDEX idx_ent_2 ON subscription_events(subscription_id, effective_date, created_date, requested_date,id);
+CREATE INDEX idx_ent_2 ON subscription_events(subscription_id, effective_date, created_date, id);
 CREATE INDEX subscription_events_tenant_account_record_id ON subscription_events(tenant_record_id, account_record_id);
 
 DROP TABLE IF EXISTS subscriptions;
@@ -35,8 +34,8 @@ CREATE TABLE subscriptions (
     category varchar(32) NOT NULL,
     start_date datetime NOT NULL,
     bundle_start_date datetime NOT NULL,
-    active_version int DEFAULT 1,
     charged_through_date datetime DEFAULT NULL,
+    migrated bool NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     updated_by varchar(50) NOT NULL,
@@ -53,7 +52,7 @@ DROP TABLE IF EXISTS bundles;
 CREATE TABLE bundles (
     record_id serial unique,
     id varchar(36) NOT NULL,
-    external_key varchar(64) NOT NULL,
+    external_key varchar(255) NOT NULL,
     account_id varchar(36) NOT NULL,
     last_sys_update_date datetime,
     original_created_date datetime NOT NULL,
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
index 1bc98e2..4b0aa5a 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionEventSqlDao.sql.stg
@@ -16,13 +16,12 @@ order by <prefix>effective_date ASC, <recordIdField(prefix)> ASC
 tableFields(prefix) ::= <<
   <prefix> event_type
 , <prefix> user_type
-, <prefix> requested_date
 , <prefix> effective_date
 , <prefix> subscription_id
 , <prefix> plan_name
 , <prefix> phase_name
 , <prefix> price_list_name
-, <prefix> current_version
+, <prefix> billing_cycle_day_local
 , <prefix> is_active
 , <prefix> created_by
 , <prefix> created_date
@@ -33,13 +32,12 @@ tableFields(prefix) ::= <<
 tableValues() ::= <<
   :eventType
 , :userType
-, :requestedDate
 , :effectiveDate
 , :subscriptionId
 , :planName
 , :phaseName
 , :priceListName
-, :currentVersion
+, :billingCycleDayLocal
 , :isActive
 , :createdBy
 , :createdDate
@@ -47,19 +45,6 @@ tableValues() ::= <<
 , :updatedDate
 >>
 
-
-updateVersion() ::= <<
-update <tableName()>
-set
-current_version = :currentVersion
-, updated_by = :createdBy
-, updated_date = :updatedDate
-where
-id = :id
-<AND_CHECK_TENANT()>
-;
->>
-
 unactiveEvent() ::= <<
 update <tableName()>
 set
@@ -72,32 +57,31 @@ id = :id
 ;
 >>
 
-reactiveEvent() ::= <<
-update <tableName()>
-set
-is_active = true
-, updated_by = :createdBy
-, updated_date = :updatedDate
+getFutureActiveEventForSubscription() ::= <<
+select <allTableFields()>
+, record_id as total_ordering
+from <tableName()>
 where
-event_id = :eventId
+subscription_id = :subscriptionId
+and is_active = true
+and effective_date > :now
 <AND_CHECK_TENANT()>
+<defaultOrderBy()>
 ;
 >>
 
-
-
-getFutureActiveEventForSubscription() ::= <<
+getFutureOrPresentActiveEventForSubscription() ::= <<
 select <allTableFields()>
 , record_id as total_ordering
 from <tableName()>
 where
 subscription_id = :subscriptionId
 and is_active = true
-and effective_date > :now
+and effective_date >= :now
 <AND_CHECK_TENANT()>
 <defaultOrderBy()>
 ;
->> 
+>>
 
 getEventsForSubscription() ::= <<
 select <allTableFields()>
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
index 6efe455..47ae310 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/engine/dao/SubscriptionSqlDao.sql.stg
@@ -7,8 +7,8 @@ tableFields(prefix) ::= <<
 , <prefix>category
 , <prefix>start_date
 , <prefix>bundle_start_date
-, <prefix>active_version
 , <prefix>charged_through_date
+, <prefix>migrated
 , <prefix>created_by
 , <prefix>created_date
 , <prefix>updated_by
@@ -20,8 +20,8 @@ tableValues() ::= <<
 , :category
 , :startDate
 , :bundleStartDate
-, :activeVersion
 , :chargedThroughDate
+, :migrated
 , :createdBy
 , :createdDate
 , :updatedBy
@@ -49,26 +49,3 @@ where id = :id
 <AND_CHECK_TENANT()>
 ;
 >>
-
-updateActiveVersion() ::= <<
-update <tableName()>
-set
-active_version = :activeVersion
-, updated_by = :createdBy
-, updated_date = :updatedDate
-where id = :id
-;
->>
-
-updateForRepair() ::= <<
-update <tableName()>
-set
-active_version = :activeVersion
-, start_date = :startDate
-, bundle_start_date = :bundleStartDate
-, updated_by = :createdBy
-, updated_date = :updatedDate
-where id = :id
-<AND_CHECK_TENANT()>
-;
->>
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180903__cleanup_499.sql b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180903__cleanup_499.sql
new file mode 100644
index 0000000..89dfedb
--- /dev/null
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180903__cleanup_499.sql
@@ -0,0 +1,4 @@
+alter table subscriptions drop column active_version;
+alter table subscriptions add column migrated bool NOT NULL default FALSE after charged_through_date;
+alter table subscription_events drop column requested_date;
+alter table subscription_events drop column current_version;
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180904__bcd_546.sql b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180904__bcd_546.sql
new file mode 100644
index 0000000..4280909
--- /dev/null
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20160915180904__bcd_546.sql
@@ -0,0 +1,2 @@
+alter table subscription_events change event_type event_type varchar(15) NOT NULL;
+alter table subscription_events add column billing_cycle_day_local int DEFAULT NULL after price_list_name;
\ No newline at end of file
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161005110304__bundles_external_key.sql b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161005110304__bundles_external_key.sql
new file mode 100644
index 0000000..d8aef66
--- /dev/null
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161005110304__bundles_external_key.sql
@@ -0,0 +1 @@
+alter table bundles modify external_key varchar(255);
\ No newline at end of file
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161703111745__plan_length_NA.sql b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161703111745__plan_length_NA.sql
new file mode 100644
index 0000000..a3550a2
--- /dev/null
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/migration/V20161703111745__plan_length_NA.sql
@@ -0,0 +1,2 @@
+alter table subscription_events modify plan_name varchar(255) COLLATE utf8_bin DEFAULT NULL;
+alter table subscription_events modify phase_name varchar(255) COLLATE utf8_bin DEFAULT NULL;
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
index 0ec542a..869e886 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/alignment/TestPlanAligner.java
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.subscription.alignment;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.joda.time.DateTime;
@@ -38,7 +39,6 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.google.common.collect.ImmutableList;
 
 public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
 
@@ -52,20 +52,6 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
     @BeforeClass(groups = "fast")
     public void beforeClass() throws Exception {
         super.beforeClass();
-
-        /*
-        final VersionedCatalogLoader versionedCatalogLoader = new VersionedCatalogLoader(clock);
-        final CatalogConfig config = new ConfigurationObjectFactory(new ConfigSource() {
-            final Map<String, String> properties = ImmutableMap.<String, String>of("org.killbill.catalog.uri", "file:src/test/resources/testInput.xml");
-
-            @Override
-            public String getString(final String propertyName) {
-                return properties.get(propertyName);
-            }
-        }).build(CatalogConfig.class);
-
-        catalogService = new DefaultCatalogService(config, versionedCatalogLoader);
-        */
         planAligner = new PlanAligner(catalogService);
 
     }
@@ -171,9 +157,11 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
         final SubscriptionBaseEvent event = createSubscriptionEvent(builder.getAlignStartDate(),
                                                                     productName,
                                                                     phaseType,
-                                                                    ApiEventType.CREATE,
-                                                                    defaultSubscriptionBase.getActiveVersion());
-        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(event), catalogService.getFullCatalog(internalCallContext));
+                                                                    ApiEventType.CREATE
+                                                                   );
+        final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
+        events.add(event);
+        defaultSubscriptionBase.rebuildTransitions(events, catalogService.getFullCatalog(true, true, internalCallContext));
 
         Assert.assertEquals(defaultSubscriptionBase.getAllTransitions().size(), 1);
         Assert.assertNull(defaultSubscriptionBase.getAllTransitions().get(0).getPreviousPhase());
@@ -190,15 +178,19 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
         final SubscriptionBaseEvent previousEvent = createSubscriptionEvent(defaultSubscriptionBase.getStartDate(),
                                                                             previousProductName,
                                                                             commonPhaseType,
-                                                                            ApiEventType.CREATE,
-                                                                            defaultSubscriptionBase.getActiveVersion());
+                                                                            ApiEventType.CREATE
+                                                                           );
         final SubscriptionBaseEvent event = createSubscriptionEvent(effectiveChangeDate,
                                                                     newProductName,
                                                                     commonPhaseType,
-                                                                    ApiEventType.CHANGE,
-                                                                    defaultSubscriptionBase.getActiveVersion());
+                                                                    ApiEventType.CHANGE
+                                                                   );
+
+        final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>();
+        events.add(previousEvent);
+        events.add(event);
 
-        defaultSubscriptionBase.rebuildTransitions(ImmutableList.<SubscriptionBaseEvent>of(previousEvent, event), catalogService.getFullCatalog(internalCallContext));
+        defaultSubscriptionBase.rebuildTransitions(events, catalogService.getFullCatalog(true, true, internalCallContext));
 
         final List<SubscriptionBaseTransition> newTransitions = defaultSubscriptionBase.getAllTransitions();
         Assert.assertEquals(newTransitions.size(), 2);
@@ -210,8 +202,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
     private SubscriptionBaseEvent createSubscriptionEvent(final DateTime effectiveDate,
                                                           final String productName,
                                                           final PhaseType phaseType,
-                                                          final ApiEventType apiEventType,
-                                                          final long activeVersion) {
+                                                          final ApiEventType apiEventType) {
         final ApiEventBuilder eventBuilder = new ApiEventBuilder();
         eventBuilder.setEffectiveDate(effectiveDate);
         eventBuilder.setEventPlan(productName);
@@ -220,7 +211,6 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
 
         // We don't really use the following but the code path requires it
         eventBuilder.setFromDisk(true);
-        eventBuilder.setActiveVersion(activeVersion);
 
         return new ApiEventBase(eventBuilder.setApiEventType(apiEventType));
     }
@@ -229,7 +219,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                  final String newProductName,
                                                  final DateTime effectiveChangeDate) throws CatalogApiException, SubscriptionBaseApiException {
         // The date is used for different catalog versions - we don't care here
-        final Plan newPlan = catalogService.getFullCatalog(internalCallContext).findPlan(newProductName, clock.getUTCNow());
+        final Plan newPlan = catalogService.getFullCatalog(true, true, internalCallContext).findPlan(newProductName, clock.getUTCNow());
 
         return planAligner.getNextTimedPhaseOnChange(defaultSubscriptionBase, newPlan, priceList, effectiveChangeDate, internalCallContext);
     }
@@ -239,7 +229,7 @@ public class TestPlanAligner extends SubscriptionTestSuiteNoDB {
                                                 final DefaultSubscriptionBase defaultSubscriptionBase,
                                                 final DateTime effectiveDate) throws CatalogApiException, SubscriptionBaseApiException {
         // The date is used for different catalog versions - we don't care here
-        final Plan plan = catalogService.getFullCatalog(internalCallContext).findPlan(productName, clock.getUTCNow());
+        final Plan plan = catalogService.getFullCatalog(true, true, internalCallContext).findPlan(productName, clock.getUTCNow());
 
         // Same here for the requested date
         final TimedPhase[] phases = planAligner.getCurrentAndNextTimedPhaseOnCreate(defaultSubscriptionBase.getAlignStartDate(), defaultSubscriptionBase.getBundleStartDate(),
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
index cee8006..772055c 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/TestEventJson.java
@@ -37,7 +37,7 @@ public class TestEventJson extends SubscriptionTestSuiteNoDB {
     public void testSubscriptionEvent() throws Exception {
 
         final EffectiveSubscriptionInternalEvent e = new DefaultEffectiveSubscriptionEvent(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), new DateTime(),
-                                                                                           EntitlementState.ACTIVE, "pro", "TRIAL", "DEFAULT", EntitlementState.CANCELLED, null, null, null, 3L,
+                                                                                           EntitlementState.ACTIVE, "pro", "TRIAL", "DEFAULT", null, EntitlementState.CANCELLED, null, null, null, null, 3L,
                                                                                            SubscriptionBaseTransitionType.CANCEL, 0, new DateTime(), 1L, 2L, null);
 
         final String json = mapper.writeValueAsString(e);
@@ -46,4 +46,17 @@ public class TestEventJson extends SubscriptionTestSuiteNoDB {
         final Object obj = mapper.readValue(json, claz);
         Assert.assertTrue(obj.equals(e));
     }
+
+    // Verify deserialization will work when we miss fields (previousBillCycleDayLocal, nextBillCycleDayLocal)
+    @Test(groups = "fast")
+    public void testSubscriptionEventWithNoBillCycleDayLocal() throws Exception {
+
+        final String json = "{\"eventId\":\"9e901bbc-bbcb-4f0a-8511-e58029bbea91\",\"subscriptionId\":\"c373056c-bb0c-4562-ab06-f595176aa4ae\",\"bundleId\":\"f61536b1-fc76-4337-b1e8-e38383894352\",\"effectiveTransitionTime\":\"2016-05-26T23:02:20.322Z\",\"previousState\":\"ACTIVE\",\"previousPlan\":\"pro\",\"previousPhase\":\"TRIAL\",\"previousPriceList\":\"DEFAULT\",\"nextState\":\"CANCELLED\",\"nextPlan\":null,\"nextPhase\":null,\"nextPriceList\":null,\"totalOrdering\":3,\"transitionType\":\"CANCEL\",\"remainingEventsForUserOperation\":0,\"startDate\":\"2016-05-26T23:02:20.322Z\",\"searchKey1\":1,\"searchKey2\":2,\"userToken\":null,\"requestedTransitionTime\":\"2016-05-26T23:02:20.322Z\"}";
+        final Class<?> claz = Class.forName(DefaultEffectiveSubscriptionEvent.class.getName());
+        final DefaultEffectiveSubscriptionEvent obj = (DefaultEffectiveSubscriptionEvent) mapper.readValue(json, claz);
+
+        Assert.assertEquals(obj.getId(), UUID.fromString("9e901bbc-bbcb-4f0a-8511-e58029bbea91"));
+        Assert.assertNull(obj.getPreviousBillCycleDayLocal());
+        Assert.assertNull(obj.getNextBillCycleDayLocal());
+    }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
index 721183d..9601f74 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestDefaultSubscriptionTransferApi.java
@@ -104,71 +104,6 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
         Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
     }
 
-    @Test(groups = "fast")
-    public void testEventsAfterTransferForMigratedBundle1() throws Exception {
-        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the past)
-        final DateTime transferDate = clock.getUTCNow();
-        final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
-        final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(1);
-        final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
-        Assert.assertEquals(events.size(), 1);
-        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
-        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
-        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
-    }
-
-    @Test(groups = "fast")
-    public void testEventsAfterTransferForMigratedBundle2() throws Exception {
-        // MIGRATE_ENTITLEMENT and MIGRATE_BILLING at the same time (both in the past)
-        final DateTime transferDate = clock.getUTCNow();
-        final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
-        final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate;
-        final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
-        Assert.assertEquals(events.size(), 1);
-        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
-        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
-        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
-    }
-
-    @Test(groups = "fast")
-    public void testEventsAfterTransferForMigratedBundle3() throws Exception {
-        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (the latter in the future)
-        final DateTime transferDate = clock.getUTCNow();
-        final DateTime migrateSubscriptionEventEffectiveDate = transferDate.minusDays(10);
-        final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(20);
-        final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
-        Assert.assertEquals(events.size(), 1);
-        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
-        Assert.assertEquals(events.get(0).getEffectiveDate(), transferDate);
-        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
-    }
-
-    @Test(groups = "fast")
-    public void testEventsAfterTransferForMigratedBundle4() throws Exception {
-        // MIGRATE_ENTITLEMENT then MIGRATE_BILLING (both in the future)
-        final DateTime transferDate = clock.getUTCNow();
-        final DateTime migrateSubscriptionEventEffectiveDate = transferDate.plusDays(10);
-        final DateTime migrateBillingEventEffectiveDate = migrateSubscriptionEventEffectiveDate.plusDays(20);
-        final List<SubscriptionBaseEvent> events = transferBundle(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate, transferDate);
-
-        Assert.assertEquals(events.size(), 1);
-        Assert.assertEquals(events.get(0).getType(), EventType.API_USER);
-        Assert.assertEquals(events.get(0).getEffectiveDate(), migrateSubscriptionEventEffectiveDate);
-        Assert.assertEquals(((ApiEventTransfer) events.get(0)).getApiEventType(), ApiEventType.TRANSFER);
-    }
-
-    private List<SubscriptionBaseEvent> transferBundle(final DateTime migrateSubscriptionEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate,
-                                                       final DateTime transferDate) throws SubscriptionBaseTransferApiException {
-        final ImmutableList<ExistingEvent> existingEvents = createMigrateEvents(migrateSubscriptionEventEffectiveDate, migrateBillingEventEffectiveDate);
-        final SubscriptionBuilder subscriptionBuilder = new SubscriptionBuilder();
-        final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(subscriptionBuilder);
-
-        return transferApi.toEvents(existingEvents, subscription, transferDate, internalCallContext);
-    }
-
     private ExistingEvent createEvent(final DateTime eventEffectiveDate, final SubscriptionBaseTransitionType subscriptionTransitionType) {
         return new ExistingEvent() {
             @Override
@@ -178,53 +113,17 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
 
             @Override
             public String getPlanName() {
-                return "BicycleTrialEvergreen1USD";
+                return "1-BicycleTrialEvergreen1USD";
             }
 
             @Override
             public String getPlanPhaseName() {
-                return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null : "BicycleTrialEvergreen1USD-trial";
+                return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null : "1-BicycleTrialEvergreen1USD-trial";
             }
 
             @Override
-            public UUID getEventId() {
-                return UUID.randomUUID();
-            }
-
-            @Override
-            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
-                return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null :
-                       new PlanPhaseSpecifier("BicycleTrialEvergreen1USD", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD,
-                                              PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.FIXEDTERM);
-            }
-
-            @Override
-            public DateTime getRequestedDate() {
-                return getEffectiveDate();
-            }
-
-            @Override
-            public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-                return subscriptionTransitionType;
-            }
-        };
-    }
-
-    private ImmutableList<ExistingEvent> createMigrateEvents(final DateTime migrateSubscriptionEventEffectiveDate, final DateTime migrateBillingEventEffectiveDate) {
-        final ExistingEvent migrateEntitlementEvent = new ExistingEvent() {
-            @Override
-            public DateTime getEffectiveDate() {
-                return migrateSubscriptionEventEffectiveDate;
-            }
-
-            @Override
-            public String getPlanName() {
-                return "BicycleTrialEvergreen1USD";
-            }
-
-            @Override
-            public String getPlanPhaseName() {
-                return "BicycleTrialEvergreen1USD-trial";
+            public Integer getBillCycleDayLocal() {
+                return null;
             }
 
             @Override
@@ -234,59 +133,20 @@ public class TestDefaultSubscriptionTransferApi extends SubscriptionTestSuiteNoD
 
             @Override
             public PlanPhaseSpecifier getPlanPhaseSpecifier() {
-                return new PlanPhaseSpecifier("BicycleTrialEvergreen1USD", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD,
+                return SubscriptionBaseTransitionType.CANCEL.equals(subscriptionTransitionType) ? null :
+                       new PlanPhaseSpecifier("1-BicycleTrialEvergreen1USD",  BillingPeriod.NO_BILLING_PERIOD,
                                               PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.FIXEDTERM);
             }
 
             @Override
-            public DateTime getRequestedDate() {
-                return getEffectiveDate();
-            }
-
-            @Override
             public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-                return SubscriptionBaseTransitionType.MIGRATE_ENTITLEMENT;
-            }
-        };
-
-        final ExistingEvent migrateBillingEvent = new ExistingEvent() {
-
-            @Override
-            public DateTime getEffectiveDate() {
-                return migrateBillingEventEffectiveDate;
-            }
-
-            @Override
-            public String getPlanName() {
-                return migrateEntitlementEvent.getPlanName();
-            }
-
-            @Override
-            public String getPlanPhaseName() {
-                return migrateEntitlementEvent.getPlanPhaseName();
-            }
-
-            @Override
-            public UUID getEventId() {
-                return UUID.randomUUID();
-            }
-
-            @Override
-            public PlanPhaseSpecifier getPlanPhaseSpecifier() {
-                return migrateEntitlementEvent.getPlanPhaseSpecifier();
-            }
-
-            @Override
-            public DateTime getRequestedDate() {
-                return migrateEntitlementEvent.getRequestedDate();
+                return subscriptionTransitionType;
             }
 
             @Override
-            public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
-                return SubscriptionBaseTransitionType.MIGRATE_BILLING;
+            public ProductCategory getProductCategory() {
+                return ProductCategory.BASE;
             }
         };
-
-        return ImmutableList.<ExistingEvent>of(migrateEntitlementEvent, migrateBillingEvent);
     }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
index cce6cb0..bbbd1e4 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/transfer/TestTransfer.java
@@ -28,13 +28,12 @@ import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
 import org.killbill.billing.subscription.api.SubscriptionBase;
-import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.slf4j.Logger;
@@ -71,55 +70,6 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         newAccountId = account.getId();
     }
 
-    @Test(groups = "slow")
-    public void testTransferMigratedSubscriptionWithCTDInFuture() throws Exception {
-        final DateTime startDate = clock.getUTCNow().minusMonths(2);
-        final DateTime beforeMigration = clock.getUTCNow();
-        final AccountMigration toBeMigrated = testUtil.createAccountForMigrationWithRegularBasePlan(startDate);
-        final DateTime afterMigration = clock.getUTCNow();
-
-        testListener.pushExpectedEvent(NextEvent.MIGRATE_ENTITLEMENT);
-        migrationApi.migrate(toBeMigrated, callContext);
-        assertListenerStatus();
-
-        final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(toBeMigrated.getAccountKey(), internalCallContext);
-        assertEquals(bundles.size(), 1);
-        final SubscriptionBaseBundle bundle = bundles.get(0);
-
-        final DateTime bundleCreatedDate = bundle.getCreatedDate();
-
-        final List<SubscriptionBase> subscriptions = subscriptionInternalApi.getSubscriptionsForBundle(bundle.getId(), null, internalCallContext);
-        assertEquals(subscriptions.size(), 1);
-        final SubscriptionBase subscription = subscriptions.get(0);
-        testUtil.assertDateWithin(subscription.getStartDate(), beforeMigration.minusMonths(2), afterMigration.minusMonths(2));
-        assertEquals(subscription.getEndDate(), null);
-        assertEquals(subscription.getCurrentPriceList().getName(), PriceListSet.DEFAULT_PRICELIST_NAME);
-        assertEquals(subscription.getCurrentPhase().getPhaseType(), PhaseType.EVERGREEN);
-        assertEquals(subscription.getState(), EntitlementState.ACTIVE);
-        assertEquals(subscription.getCurrentPlan().getName(), "shotgun-annual");
-        assertEquals(subscription.getChargedThroughDate(), startDate.plusYears(1));
-        // WE should see MIGRATE_ENTITLEMENT and then MIGRATE_BILLING in the future
-        assertEquals(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).size(), 1);
-        assertEquals(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.MIGRATE_BILLING);
-        assertTrue(subscriptionInternalApi.getBillingTransitions(subscription, internalCallContext).get(0).getEffectiveTransitionTime().compareTo(clock.getUTCNow()) > 0);
-        assertListenerStatus();
-
-        // MOVE A LITTLE, STILL IN TRIAL
-        clock.addDays(20);
-
-        final DateTime transferRequestedDate = clock.getUTCNow();
-
-        testListener.pushExpectedEvent(NextEvent.TRANSFER);
-        testListener.pushExpectedEvent(NextEvent.CANCEL);
-        transferApi.transferBundle(bundle.getAccountId(), newAccountId, bundle.getExternalKey(), transferRequestedDate, false, true, callContext);
-        assertListenerStatus();
-
-        final SubscriptionBase oldBaseSubscription = subscriptionInternalApi.getBaseSubscription(bundle.getId(), internalCallContext);
-        assertTrue(oldBaseSubscription.getState() == EntitlementState.CANCELLED);
-        // The MIGRATE_BILLING event should have been invalidated
-        assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).size(), 0);
-        //assertEquals(subscriptionInternalApi.getBillingTransitions(oldBaseSubscription, internalCallContext).get(0).getTransitionType(), SubscriptionBaseTransitionType.CANCEL);
-    }
 
     @Test(groups = "slow")
     public void testTransferBPInTrialWithNoCTD() throws Exception {
@@ -317,7 +267,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
         final String newBaseProduct1 = "Assault-Rifle";
         final BillingPeriod newBaseTerm1 = BillingPeriod.ANNUAL;
         testListener.pushExpectedEvent(NextEvent.CHANGE);
-        newBaseSubscription.changePlan(newBaseProduct1, newBaseTerm1, basePriceList, null, callContext);
+        newBaseSubscription.changePlan(new PlanSpecifier(newBaseProduct1, newBaseTerm1, basePriceList), null, callContext);
         assertListenerStatus();
 
         newPlan = newBaseSubscription.getCurrentPlan();
@@ -333,7 +283,7 @@ public class TestTransfer extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final String newBaseProduct2 = "Pistol";
         final BillingPeriod newBaseTerm2 = BillingPeriod.ANNUAL;
-        newBaseSubscriptionWithCtd.changePlan(newBaseProduct2, newBaseTerm2, basePriceList, null, callContext);
+        newBaseSubscriptionWithCtd.changePlan(new PlanSpecifier(newBaseProduct2, newBaseTerm2, basePriceList), null, callContext);
 
         newPlan = newBaseSubscriptionWithCtd.getCurrentPlan();
         assertEquals(newPlan.getProduct().getName(), newBaseProduct1);
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
index 487a3d9..52ba58e 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestSubscriptionHelper.java
@@ -19,7 +19,6 @@
 package org.killbill.billing.subscription.api.user;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
 
@@ -27,9 +26,8 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
 import org.joda.time.Period;
-import org.killbill.billing.account.api.Account;
-import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.api.TestApiListener;
 import org.killbill.billing.api.TestApiListener.NextEvent;
@@ -38,16 +36,9 @@ import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Duration;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
-import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.TimeUnit;
-import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-import org.killbill.billing.mock.MockAccountBuilder;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.AccountMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.BundleMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.SubscriptionMigration;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi.SubscriptionMigrationCase;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
@@ -100,8 +91,8 @@ public class TestSubscriptionHelper {
             throws SubscriptionBaseApiException {
         testListener.pushExpectedEvent(NextEvent.CREATE);
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionApi.createSubscription(bundleId,
-                                                                                                                  new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSet, null), null,
-                                                                                                                  requestedDate == null ? clock.getUTCNow() : requestedDate, internalCallContext);
+                                                                                                                  new PlanPhaseSpecifier(productName, term, planSet, null), null,
+                                                                                                                  requestedDate == null ? clock.getUTCNow() : requestedDate, false, internalCallContext);
         assertNotNull(subscription);
 
         testListener.assertListenerStatus();
@@ -154,9 +145,12 @@ public class TestSubscriptionHelper {
 
             @Override
             public DateTime addToDateTime(final DateTime dateTime) {
-                return null;  //To change body of implemented methods use File | Settings | File Templates.
+                return null;
+            }
+            @Override
+            public LocalDate addToLocalDate(final LocalDate localDate) {
+                return null;
             }
-
             @Override
             public Period toJodaPeriod() {
                 throw new UnsupportedOperationException();
@@ -168,7 +162,7 @@ public class TestSubscriptionHelper {
     public PlanPhaseSpecifier getProductSpecifier(final String productName, final String priceList,
                                                   final BillingPeriod term,
                                                   @Nullable final PhaseType phaseType) {
-        return new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, priceList, phaseType);
+        return new PlanPhaseSpecifier(productName, term, priceList, phaseType);
     }
 
     public void printEvents(final List<SubscriptionBaseEvent> events) {
@@ -177,169 +171,6 @@ public class TestSubscriptionHelper {
         }
     }
 
-    public void printSubscriptionTransitions(final List<EffectiveSubscriptionInternalEvent> transitions) {
-        for (final EffectiveSubscriptionInternalEvent cur : transitions) {
-            log.debug("Transition " + cur);
-        }
-    }
-
-    /**
-     * ***********************************************************
-     * Utilities for migration tests
-     * *************************************************************
-     */
-
-    public AccountMigration createAccountForMigrationTest(final List<List<SubscriptionMigrationCaseWithCTD>> cases) {
-        final Account account;
-        try {
-            final Account accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
-                                                                .email(UUID.randomUUID().toString().substring(1, 8))
-                                                                .build();
-            account = accountApi.createAccount(accountData, callContext);
-        } catch (final AccountApiException e) {
-            throw new AssertionError(e.getLocalizedMessage());
-        }
-
-        return new AccountMigration() {
-
-            @Override
-            public BundleMigration[] getBundles() {
-                final List<BundleMigration> bundles = new ArrayList<BundleMigration>();
-                final BundleMigration bundle0 = new BundleMigration() {
-                    @Override
-                    public SubscriptionMigration[] getSubscriptions() {
-                        final SubscriptionMigration[] result = new SubscriptionMigration[cases.size()];
-
-                        for (int i = 0; i < cases.size(); i++) {
-                            final List<SubscriptionMigrationCaseWithCTD> curCases = cases.get(i);
-                            final SubscriptionMigration subscription = new SubscriptionMigration() {
-                                @Override
-                                public SubscriptionMigrationCaseWithCTD[] getSubscriptionCases() {
-                                    return curCases.toArray(new SubscriptionMigrationCaseWithCTD[curCases.size()]);
-                                }
-
-                                @Override
-                                public ProductCategory getCategory() {
-                                    return curCases.get(0).getPlanPhaseSpecifier().getProductCategory();
-                                }
-
-                                @Override
-                                public DateTime getChargedThroughDate() {
-                                    for (final SubscriptionMigrationCaseWithCTD cur : curCases) {
-                                        if (cur.getChargedThroughDate() != null) {
-                                            return cur.getChargedThroughDate();
-                                        }
-                                    }
-                                    return null;
-                                }
-                            };
-                            result[i] = subscription;
-                        }
-                        return result;
-                    }
-
-                    @Override
-                    public String getBundleKey() {
-                        return "12345";
-                    }
-                };
-                bundles.add(bundle0);
-                return bundles.toArray(new BundleMigration[bundles.size()]);
-            }
-
-            @Override
-            public UUID getAccountKey() {
-                return account.getId();
-            }
-        };
-    }
-
-    public AccountMigration createAccountForMigrationWithRegularBasePlanAndAddons(final DateTime initialBPstart, final DateTime initalAddonStart) {
-
-        final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                initialBPstart,
-                null,
-                initialBPstart.plusYears(1)));
-
-        final List<SubscriptionMigrationCaseWithCTD> firstAddOnCases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        firstAddOnCases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.DISCOUNT),
-                initalAddonStart,
-                initalAddonStart.plusMonths(1),
-                initalAddonStart.plusMonths(1)));
-        firstAddOnCases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                initalAddonStart.plusMonths(1),
-                null,
-                null));
-
-        final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        input.add(firstAddOnCases);
-        return createAccountForMigrationTest(input);
-    }
-
-    public AccountMigration createAccountForMigrationWithRegularBasePlan(final DateTime startDate) {
-        final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                startDate,
-                null,
-                startDate.plusYears(1)));
-        final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountForMigrationTest(input);
-    }
-
-    public AccountMigration createAccountForMigrationWithRegularBasePlanFutreCancelled(final DateTime startDate) {
-        final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                startDate,
-                startDate.plusYears(1),
-                startDate.plusYears(1)));
-        final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountForMigrationTest(input);
-    }
-
-    public AccountMigration createAccountForMigrationFuturePendingPhase(final DateTime trialDate) {
-        final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL),
-                trialDate,
-                trialDate.plusDays(30),
-                trialDate.plusDays(30)));
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                trialDate.plusDays(30),
-                null,
-                null));
-        final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountForMigrationTest(input);
-    }
-
-    public AccountMigration createAccountForMigrationFuturePendingChange() {
-        final List<SubscriptionMigrationCaseWithCTD> cases = new LinkedList<SubscriptionMigrationCaseWithCTD>();
-        final DateTime effectiveDate = clock.getUTCNow().minusDays(10);
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                effectiveDate,
-                effectiveDate.plusMonths(1),
-                effectiveDate.plusMonths(1)));
-        cases.add(new SubscriptionMigrationCaseWithCTD(
-                new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.EVERGREEN),
-                effectiveDate.plusMonths(1).plusDays(1),
-                null,
-                null));
-        final List<List<SubscriptionMigrationCaseWithCTD>> input = new ArrayList<List<SubscriptionMigrationCaseWithCTD>>();
-        input.add(cases);
-        return createAccountForMigrationTest(input);
-    }
-
     public static DateTime addOrRemoveDuration(final DateTime input, final List<Duration> durations, final boolean add) {
         DateTime result = input;
         for (final Duration cur : durations) {
@@ -373,38 +204,4 @@ public class TestSubscriptionHelper {
         return addOrRemoveDuration(input, list, true);
     }
 
-    public static class SubscriptionMigrationCaseWithCTD implements SubscriptionMigrationCase {
-
-        private final PlanPhaseSpecifier pps;
-        private final DateTime effDt;
-        private final DateTime cancelDt;
-        private final DateTime ctd;
-
-        public SubscriptionMigrationCaseWithCTD(final PlanPhaseSpecifier pps, final DateTime effDt, final DateTime cancelDt, final DateTime ctd) {
-            this.pps = pps;
-            this.cancelDt = cancelDt;
-            this.effDt = effDt;
-            this.ctd = ctd;
-        }
-
-        @Override
-        public PlanPhaseSpecifier getPlanPhaseSpecifier() {
-            return pps;
-        }
-
-        @Override
-        public DateTime getEffectiveDate() {
-            return effDt;
-        }
-
-        @Override
-        public DateTime getCancelledDate() {
-            return cancelDt;
-        }
-
-        public DateTime getChargedThroughDate() {
-            return ctd;
-        }
-    }
-
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
index 8829276..cb9e43a 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiAddOn.java
@@ -311,7 +311,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
         testListener.pushExpectedEvent(NextEvent.CHANGE);
         testListener.pushExpectedEvent(NextEvent.CANCEL);
-        baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, null, callContext);
+        baseSubscription.changePlan(new PlanSpecifier(newBaseProduct, newBaseTerm, newBasePriceList), null, callContext);
         assertListenerStatus();
 
         // REFETCH AO SUBSCRIPTION AND CHECK THIS CANCELLED
@@ -367,7 +367,7 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(aoStatus.get(0).getPriceList(), aoSubscription.getCurrentPriceList().getName());
         assertEquals(aoStatus.get(0).getReason(), DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN);
 
-        baseSubscription.changePlan(newBaseProduct, newBaseTerm, newBasePriceList, null, callContext);
+        baseSubscription.changePlan(new PlanSpecifier(newBaseProduct, newBaseTerm, newBasePriceList), null, callContext);
 
         // REFETCH AO SUBSCRIPTION AND CHECK THIS IS ACTIVE
         aoSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(aoSubscription.getId(), internalCallContext);
@@ -396,7 +396,6 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // This is just to double check our test catalog gives us what we want before we start the test
         final PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
-                                                              ProductCategory.ADD_ON,
                                                               aoTerm,
                                                               aoPriceList);
         final PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
@@ -413,7 +412,6 @@ public class TestUserApiAddOn extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // This is just to double check our test catalog gives us what we want before we start the test
         final PlanSpecifier planSpecifier = new PlanSpecifier(aoProduct,
-                                                              ProductCategory.ADD_ON,
                                                               aoTerm,
                                                               aoPriceList);
         final PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
index a3acb50..056208c 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCancel.java
@@ -16,8 +16,16 @@
 
 package org.killbill.billing.subscription.api.user;
 
+import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.entity.EntityPersistenceException;
+import org.killbill.billing.subscription.engine.dao.SubscriptionEventSqlDao;
+import org.killbill.billing.subscription.engine.dao.model.SubscriptionEventModelDao;
+import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
+import org.skife.jdbi.v2.Handle;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -118,6 +126,10 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         subscription.cancel(callContext);
         assertListenerStatus();
 
+        // CANCEL a second time (first pending CANCEL should be made inactive)
+        subscription.cancel(callContext);
+        assertListenerStatus();
+
         assertEquals(subscription.getLastActiveProduct().getName(), prod);
         assertEquals(subscription.getLastActivePriceList().getName(), planSet);
         assertEquals(subscription.getLastActiveBillingPeriod(), term);
@@ -127,10 +139,9 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         Assert.assertNotNull(futureEndDate);
 
         // MOVE TO EOT + RECHECK
-        testListener.pushExpectedEvent(NextEvent.CANCEL);
+        testListener.pushExpectedEvents(NextEvent.CANCEL);
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
         clock.addDeltaFromReality(it.toDurationMillis());
-        final DateTime future = clock.getUTCNow();
         assertListenerStatus();
 
         assertTrue(futureEndDate.compareTo(subscription.getEndDate()) == 0);
@@ -258,4 +269,81 @@ public class TestUserApiCancel extends SubscriptionTestSuiteWithEmbeddedDB {
         // CANCEL in EVERGREEN period with an invalid Date (prior to the Creation Date)
         subscription.cancelWithDate(invalidDate, callContext);
     }
+
+
+
+    @Test(groups = "slow")
+    public void testWithMultipleCancellationEvent() throws SubscriptionBillingApiException, SubscriptionBaseApiException {
+        final String prod = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String planSet = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        // CREATE
+        DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, prod, term, planSet);
+        PlanPhase trialPhase = subscription.getCurrentPhase();
+        assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
+
+        // NEXT PHASE
+        final DateTime expectedPhaseTrialChange = TestSubscriptionHelper.addDuration(subscription.getStartDate(), trialPhase.getDuration());
+        testUtil.checkNextPhaseChange(subscription, 1, expectedPhaseTrialChange);
+
+        // MOVE TO NEXT PHASE
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+        clock.addDeltaFromReality(it.toDurationMillis());
+
+        assertListenerStatus();
+        trialPhase = subscription.getCurrentPhase();
+        assertEquals(trialPhase.getPhaseType(), PhaseType.EVERGREEN);
+
+        // SET CTD + RE READ SUBSCRIPTION + CHANGE PLAN
+        final Duration ctd = testUtil.getDurationMonth(1);
+        final DateTime newChargedThroughDate = TestSubscriptionHelper.addDuration(expectedPhaseTrialChange, ctd);
+        subscriptionInternalApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, internalCallContext);
+        subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+
+        assertEquals(subscription.getLastActiveProduct().getName(), prod);
+        assertEquals(subscription.getLastActivePriceList().getName(), planSet);
+        assertEquals(subscription.getLastActiveBillingPeriod(), term);
+        assertEquals(subscription.getLastActiveCategory(), ProductCategory.BASE);
+
+        // CANCEL
+        subscription.cancel(callContext);
+        assertListenerStatus();
+
+        subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+        Assert.assertEquals(subscription.getAllTransitions().size(), 3);
+
+
+        // Manually add a CANCEL event on the same EOT date as the previous one to verify the code is resilient enough to ignore it
+        final SubscriptionBaseEvent cancelEvent = subscription.getEvents().get(subscription.getEvents().size() - 1);
+        final SubscriptionEventModelDao newCancelEvent = new SubscriptionEventModelDao(cancelEvent);
+        newCancelEvent.setId(UUID.randomUUID());
+
+        final Handle handle = dbi.open();
+        final SubscriptionEventSqlDao sqlDao = handle.attach(SubscriptionEventSqlDao.class);
+        try {
+            sqlDao.create(newCancelEvent, internalCallContext);
+        } catch (EntityPersistenceException e) {
+            Assert.fail(e.getMessage());
+        }
+
+        subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+        // The extra cancel event is being ignored
+        Assert.assertEquals(subscription.getEvents().size(), 3);
+        Assert.assertEquals(subscription.getAllTransitions().size(), 3);
+
+
+        // We expect only one CANCEL event, this other one is skipped
+        testListener.pushExpectedEvents(NextEvent.CANCEL);
+        it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertListenerStatus();
+
+        // Our previous transition should be a CANCEL with a valid previous plan
+        final SubscriptionBaseTransition previousTransition = subscription.getPreviousTransition();
+        Assert.assertEquals(previousTransition.getPreviousState(), EntitlementState.ACTIVE);
+        Assert.assertNotNull(previousTransition.getPreviousPlan());
+
+    }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
index 01a93e7..58fe498 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiChangePlan.java
@@ -23,6 +23,7 @@ import org.joda.time.DateTime;
 import org.joda.time.Interval;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.catalog.api.PlanAlignmentCreate;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -84,7 +85,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
             // CHANGE PLAN
             testListener.pushExpectedEvent(NextEvent.CHANGE);
-            subscription.changePlan(toProd, toTerm, toPlanSet, null, callContext);
+            subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
             assertListenerStatus();
 
             // CHECK CHANGE PLAN
@@ -125,7 +126,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // RE READ SUBSCRIPTION + CHANGE PLAN
         subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
-        subscription.changePlan(toProd, toTerm, toPlanSet, null, callContext);
+        subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
         assertListenerStatus();
 
         // CHECK CHANGE PLAN
@@ -168,7 +169,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         clock.addDeltaFromReality(it.toDurationMillis());
 
         // CHANGE PLAN IMM
-        subscription.changePlan(toProd, toTerm, toPlanSet, null, callContext);
+        subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
         checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.TRIAL);
 
         assertListenerStatus();
@@ -222,7 +223,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // CHANGE PLAN
         currentTime = clock.getUTCNow();
-        subscription.changePlan(toProd, toTerm, toPlanSet, null, callContext);
+        subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
 
         checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.EVERGREEN);
 
@@ -288,12 +289,12 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
 
         // CHANGE EOT
-        subscription.changePlan("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount", null, callContext);
+        subscription.changePlan(new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, "gunclubDiscount"), null, callContext);
         assertListenerStatus();
 
         // CHANGE
         testListener.pushExpectedEvent(NextEvent.CHANGE);
-        subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", null, callContext);
+        subscription.changePlan(new PlanSpecifier("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount"), null, callContext);
         assertListenerStatus();
 
         final Plan currentPlan = subscription.getCurrentPlan();
@@ -330,11 +331,11 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
 
         // CHANGE EOT
-        subscription.changePlan("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount", null, callContext);
+        subscription.changePlan(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, "gunclubDiscount"), null, callContext);
         assertListenerStatus();
 
         // CHANGE EOT
-        subscription.changePlan("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount", null, callContext);
+        subscription.changePlan(new PlanSpecifier("Pistol", BillingPeriod.ANNUAL, "gunclubDiscount"), null, callContext);
         assertListenerStatus();
 
         // CHECK NO CHANGE OCCURED YET
@@ -349,7 +350,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
         // ACTIVATE CHANGE BY MOVING AFTER CTD
-        testListener.pushExpectedEvents(NextEvent.CHANGE, NextEvent.CHANGE);
+        testListener.pushExpectedEvents(NextEvent.CHANGE);
         it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
         clock.addDeltaFromReality(it.toDurationMillis());
         assertListenerStatus();
@@ -396,7 +397,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
 
         // CHANGE IMMEDIATE TO A 3 PHASES PLAN
         testListener.pushExpectedEvent(NextEvent.CHANGE);
-        subscription.changePlan("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount", null, callContext);
+        subscription.changePlan(new PlanSpecifier("Assault-Rifle", BillingPeriod.ANNUAL, "gunclubDiscount"), null, callContext);
         assertListenerStatus();
 
         // CHECK EVERYTHING LOOKS CORRECT
@@ -450,7 +451,7 @@ public class TestUserApiChangePlan extends SubscriptionTestSuiteWithEmbeddedDB {
         DefaultSubscriptionBase aoSubscription = testUtil.createSubscription(bundle, aoProduct, aoTerm, aoPriceList);
 
         try {
-            aoSubscription.changePlanWithDate(baseProduct, baseTerm, basePriceList, null, clock.getUTCNow(), callContext);
+            aoSubscription.changePlanWithDate(new PlanSpecifier(baseProduct, baseTerm, basePriceList), null, clock.getUTCNow(), callContext);
             Assert.fail("Should not allow plan change across product type");
         } catch (final SubscriptionBaseApiException e) {
             Assert.assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_INVALID.getCode());
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
index d85336b..4776379 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiCreate.java
@@ -27,6 +27,7 @@ import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.subscription.DefaultSubscriptionTestInitializer;
 import org.killbill.billing.subscription.SubscriptionTestSuiteWithEmbeddedDB;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
@@ -57,7 +58,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
         assertListenerStatus();
         assertNotNull(subscription);
 
@@ -71,7 +72,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
 
         testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.PHASE);
         final DefaultSubscriptionBase newSubscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(newBundle.getId(),
-                                                                                                                             testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+                                                                                                                             testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
 
         subscriptionInternalApi.updateExternalKey(newBundle.getId(), "myNewSuperKey", internalCallContext);
 
@@ -95,7 +96,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         testListener.pushExpectedEvent(NextEvent.CREATE);
 
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, internalCallContext);
+                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, requestedDate, false, internalCallContext);
         assertNotNull(subscription);
 
         //
@@ -108,8 +109,6 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         final SubscriptionBaseEvent trialEvent = events.get(0);
         final SubscriptionBaseEvent phaseEvent = events.get(1);
 
-        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
-        //assertEquals(subscription.getAccount(), account.getId());
         assertEquals(subscription.getBundleId(), bundle.getId());
         assertEquals(subscription.getStartDate(), requestedDate);
 
@@ -135,11 +134,9 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         testListener.pushExpectedEvent(NextEvent.CREATE);
 
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
-                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), null, clock.getUTCNow(), internalCallContext);
+                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, PhaseType.EVERGREEN), null, clock.getUTCNow(), false, internalCallContext);
         assertNotNull(subscription);
 
-        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
-        //assertEquals(subscription.getAccount(), account.getId());
         assertEquals(subscription.getBundleId(), bundle.getId());
         testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
         testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
@@ -169,11 +166,9 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
                                                                                                                           testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                          null, clock.getUTCNow(), internalCallContext);
+                                                                                                                          null, clock.getUTCNow(), false, internalCallContext);
         assertNotNull(subscription);
 
-        assertEquals(subscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION);
-        //assertEquals(subscription.getAccount(), account.getId());
         assertEquals(subscription.getBundleId(), bundle.getId());
         testUtil.assertDateWithin(subscription.getStartDate(), init, clock.getUTCNow());
         testUtil.assertDateWithin(subscription.getBundleStartDate(), init, clock.getUTCNow());
@@ -219,7 +214,7 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
         // CREATE SUBSCRIPTION
         DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
                                                                                                                     testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                    null, clock.getUTCNow(), internalCallContext);
+                                                                                                                    null, clock.getUTCNow(), false, internalCallContext);
         assertNotNull(subscription);
 
         PlanPhase currentPhase = subscription.getCurrentPhase();
@@ -260,9 +255,35 @@ public class TestUserApiCreate extends SubscriptionTestSuiteWithEmbeddedDB {
 
         final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
                                                                                                                           testUtil.getProductSpecifier(productName, planSetName, term, null),
-                                                                                                                          null, clock.getUTCNow(), internalCallContext);
+                                                                                                                          null, clock.getUTCNow(), false, internalCallContext);
         assertNotNull(subscription);
 
         assertListenerStatus();
     }
+
+
+    @Test(groups = "slow")
+    public void testCreateSubscriptionInTheFuture() throws SubscriptionBaseApiException {
+        final DateTime init = clock.getUTCNow();
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+
+        final DateTime futureCreationDate = init.plusDays(10);
+
+        DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) subscriptionInternalApi.createSubscription(bundle.getId(),
+                                                                                                                          testUtil.getProductSpecifier(productName, planSetName, term, null), null, futureCreationDate, false, internalCallContext);
+        assertListenerStatus();
+        assertNotNull(subscription);
+        assertEquals(subscription.getState(), EntitlementState.PENDING);
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        clock.addDays(10);
+        assertListenerStatus();
+
+        subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
+        assertEquals(subscription.getState(), EntitlementState.ACTIVE);
+    }
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
index 2166d4c..0c2e6c5 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/api/user/TestUserApiError.java
@@ -30,6 +30,7 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Duration;
 import org.killbill.billing.catalog.api.PlanPhase;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.killbill.billing.catalog.api.PriceListSet;
 import org.killbill.billing.subscription.SubscriptionTestSuiteNoDB;
 import org.killbill.billing.subscription.api.SubscriptionBase;
@@ -52,7 +53,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         // TODO: MARTIN TO FIX WITH CORRECT ERROR CODE. RIGHT NOW NPE
 
         // WRONG BILLING PERIOD
-        tCreateSubscriptionInternal(bundle.getId(), "Shotgun", null, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_PLAN_NOT_FOUND);
+        tCreateSubscriptionInternal(bundle.getId(), "Shotgun", null, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NULL_BILLING_PERIOD);
         // WRONG PLAN SET
         tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, "Whatever", ErrorCode.CAT_PRICE_LIST_NOT_FOUND);
     }
@@ -92,7 +93,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         try {
             subscriptionInternalApi.createSubscription(bundleId,
                                                        testUtil.getProductSpecifier(productName, planSet, term, null),
-                                                       null, clock.getUTCNow(), internalCallContext);
+                                                       null, clock.getUTCNow(), false, internalCallContext);
             Assert.fail("Exception expected, error code: " + expected);
         } catch (final SubscriptionBaseApiException e) {
             assertEquals(e.getCode(), expected.getCode());
@@ -106,7 +107,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         testListener.pushExpectedEvent(NextEvent.CANCEL);
         subscription.cancelWithDate(clock.getUTCNow(), callContext);
         try {
-            subscription.changePlanWithDate("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, clock.getUTCNow(), callContext);
+            subscription.changePlanWithDate(new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, clock.getUTCNow(), callContext);
         } catch (final SubscriptionBaseApiException e) {
             assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode());
         }
@@ -117,7 +118,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         final SubscriptionBase subscription = testUtil.createSubscription(bundle, "Shotgun", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME);
 
         try {
-            subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, BillingActionPolicy.ILLEGAL, callContext);
+            subscription.changePlanWithPolicy(new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, BillingActionPolicy.ILLEGAL, callContext);
             Assert.fail();
         } catch (final SubscriptionBaseError error) {
             assertTrue(true);
@@ -125,7 +126,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
         }
 
         // Assume the call takes less than a second
-        assertEquals(DefaultClock.truncateMs(subscription.changePlanWithPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, BillingActionPolicy.IMMEDIATE, callContext)),
+        assertEquals(DefaultClock.truncateMs(subscription.changePlanWithPolicy(new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, BillingActionPolicy.IMMEDIATE, callContext)),
                      DefaultClock.truncateMs(clock.getUTCNow()));
         assertEquals(subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext).getCurrentPlan().getRecurringBillingPeriod(), BillingPeriod.MONTHLY);
     }
@@ -152,7 +153,7 @@ public class TestUserApiError extends SubscriptionTestSuiteNoDB {
 
         subscription.cancelWithPolicy(BillingActionPolicy.END_OF_TERM, callContext);
         try {
-            subscription.changePlanWithDate("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, clock.getUTCNow(), callContext);
+            subscription.changePlanWithDate(new PlanSpecifier("Pistol", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, clock.getUTCNow(), callContext);
         } catch (final SubscriptionBaseApiException e) {
             assertEquals(e.getCode(), ErrorCode.SUB_CHANGE_FUTURE_CANCELLED.getCode());
         }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
index d723dad..0964ae8 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/DefaultSubscriptionTestInitializer.java
@@ -55,7 +55,7 @@ public class DefaultSubscriptionTestInitializer implements SubscriptionTestIniti
     public Catalog initCatalog(final CatalogService catalogService, final InternalTenantContext context) throws Exception {
 
         ((DefaultCatalogService) catalogService).loadCatalog();
-        final Catalog catalog = catalogService.getFullCatalog(context);
+        final Catalog catalog = catalogService.getFullCatalog(true, true, context);
         assertNotNull(catalog);
         return catalog;
     }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index f7947f5..b3bde6d 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -38,9 +38,7 @@ import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.billing.dao.MockNonEntityDao;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
-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;
+import org.killbill.billing.subscription.api.transfer.BundleTransferData;
 import org.killbill.billing.subscription.api.transfer.TransferCancelData;
 import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -241,16 +239,6 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
     }
 
     @Override
-    public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
-        synchronized (events) {
-            events.addAll(recreateEvents);
-            for (final SubscriptionBaseEvent cur : recreateEvents) {
-                recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new SubscriptionNotificationKey(cur.getId()), context);
-            }
-        }
-    }
-
-    @Override
     public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) {
         final List<SubscriptionBase> results = new ArrayList<SubscriptionBase>();
         for (final SubscriptionBase cur : subscriptions) {
@@ -323,7 +311,7 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
         final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(new SubscriptionBuilder(in), null, clock);
         if (events.size() > 0) {
             try {
-                subscription.rebuildTransitions(getEventsForSubscription(in.getId(), context), catalogService.getFullCatalog(context));
+                subscription.rebuildTransitions(getEventsForSubscription(in.getId(), context), catalogService.getFullCatalog(true, true, context));
             } catch (final CatalogApiException e) {
                 log.warn("Failed to rebuild subscription", e);
             }
@@ -460,30 +448,6 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
     }
 
     @Override
-    public void migrate(final UUID accountId, final AccountMigrationData accountData, final InternalCallContext context) {
-        synchronized (events) {
-
-            for (final BundleMigrationData curBundle : accountData.getData()) {
-                final DefaultSubscriptionBaseBundle bundleData = curBundle.getData();
-                for (final SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
-                    final DefaultSubscriptionBase subData = curSubscription.getData();
-                    for (final SubscriptionBaseEvent curEvent : curSubscription.getInitialEvents()) {
-                        events.add(curEvent);
-                        mockNonEntityDao.addTenantRecordIdMapping(curEvent.getId(), context);
-                        recordFutureNotificationFromTransaction(null, curEvent.getEffectiveDate(),
-                                                                new SubscriptionNotificationKey(curEvent.getId()), context);
-
-                    }
-                    subscriptions.add(subData);
-                    mockNonEntityDao.addTenantRecordIdMapping(subData.getId(), context);
-                }
-                bundles.add(bundleData);
-                mockNonEntityDao.addTenantRecordIdMapping(bundleData.getId(), context);
-            }
-        }
-    }
-
-    @Override
     public SubscriptionBaseEvent getEventById(final UUID eventId, final InternalTenantContext context) {
         synchronized (events) {
             for (final SubscriptionBaseEvent cur : events) {
@@ -529,7 +493,7 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
     }
 
     @Override
-    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleMigrationData data,
+    public void transfer(final UUID srcAccountId, final UUID destAccountId, final BundleTransferData data,
                          final List<TransferCancelData> transferCancelData, final InternalCallContext fromContext,
                          final InternalCallContext toContext) {
     }
@@ -537,4 +501,10 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
     @Override
     public void updateBundleExternalKey(final UUID bundleId, final String externalKey, final InternalCallContext context) {
     }
+
+    @Override
+    public void createBCDChangeEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent bcdEvent, final InternalCallContext context) {
+
+    }
+
 }
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModule.java b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModule.java
index 25984ce..fe9ba7c 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModule.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/glue/TestDefaultSubscriptionModule.java
@@ -27,6 +27,7 @@ import org.killbill.billing.subscription.SubscriptionTestInitializer;
 import org.killbill.billing.subscription.api.user.TestSubscriptionHelper;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 
 public class TestDefaultSubscriptionModule extends DefaultSubscriptionModule {
 
@@ -40,6 +41,7 @@ public class TestDefaultSubscriptionModule extends DefaultSubscriptionModule {
         install(new CatalogModule(configSource));
         install(new CallContextModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new MockTenantModule(configSource));
 
         bind(TestSubscriptionHelper.class).asEagerSingleton();
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
index c57adde..0ace9f9 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteNoDB.java
@@ -32,7 +32,6 @@ import org.killbill.billing.lifecycle.api.BusService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
 import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
 import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
@@ -42,7 +41,7 @@ import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.glue.TestDefaultSubscriptionModuleNoDB;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.SubscriptionConfig;
+import org.killbill.billing.util.config.definition.SubscriptionConfig;
 import org.killbill.clock.ClockMock;
 import org.mockito.Mockito;
 import org.skife.jdbi.v2.IDBI;
@@ -70,8 +69,6 @@ public class SubscriptionTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     protected SubscriptionBaseTransferApi transferApi;
 
     @Inject
-    protected SubscriptionBaseMigrationApi migrationApi;
-    @Inject
     protected SubscriptionBaseTimelineApi repairApi;
 
     @Inject
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
index 0dc88d7..e97f3e0 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/SubscriptionTestSuiteWithEmbeddedDB.java
@@ -21,7 +21,6 @@ package org.killbill.billing.subscription;
 import javax.inject.Inject;
 
 import org.killbill.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
-import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
@@ -33,15 +32,13 @@ import org.killbill.billing.lifecycle.api.BusService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
 import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
 import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.subscription.api.user.TestSubscriptionHelper;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.glue.TestDefaultSubscriptionModuleWithEmbeddedDB;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.killbill.billing.util.config.SubscriptionConfig;
+import org.killbill.billing.util.config.definition.SubscriptionConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.clock.ClockMock;
 import org.slf4j.Logger;
@@ -70,8 +67,6 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
     protected SubscriptionBaseTransferApi transferApi;
 
     @Inject
-    protected SubscriptionBaseMigrationApi migrationApi;
-    @Inject
     protected SubscriptionBaseTimelineApi repairApi;
 
     @Inject
@@ -136,9 +131,7 @@ public class SubscriptionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteW
     protected Account createAccount(final AccountData accountData) throws AccountApiException {
         final Account account = accountUserApi.createAccount(accountData, callContext);
 
-        final Long accountRecordId = nonEntityDao.retrieveRecordIdFromObject(account.getId(), ObjectType.ACCOUNT, controlCacheDispatcher.getCacheController(CacheType.RECORD_ID));
-        internalCallContext.setAccountRecordId(accountRecordId);
-        internalCallContext.setReferenceDateTimeZone(account.getTimeZone());
+        refreshCallContext(account.getId());
 
         return account;
     }

tenant/pom.xml 53(+52 -1)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index 19568a5..69fe1bf 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>
@@ -27,6 +27,10 @@
     <name>killbill-tenant</name>
     <dependencies>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>jsr305</artifactId>
             <scope>provided</scope>
@@ -41,10 +45,34 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+        </dependency>
+        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
@@ -101,20 +129,43 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-concurrent</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-h2</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-queue</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-all</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.skife.config</groupId>
+            <artifactId>config-magic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
             <scope>test</scope>
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
index 95c793e..fcf5f05 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
@@ -72,14 +72,20 @@ public class DefaultTenantInternalApi implements TenantInternalApi {
     }
 
     @Override
+    public String getTenantConfig(final InternalTenantContext tenantContext) {
+        final List<String> values = tenantDao.getTenantValueForKey(TenantKey.PER_TENANT_CONFIG.toString(), tenantContext);
+        return getUniqueValue(values, "per tenant config", tenantContext);
+    }
+
+    @Override
     public String getInvoiceTemplate(final Locale locale, final InternalTenantContext tenantContext) {
-        final List<String> values = tenantDao.getTenantValueForKey(LocaleUtils.localeString(locale, TenantKey.INVOICE_TEMPLATE.toString()), tenantContext);
+        final List<String> values = tenantDao.getTenantValueForKey(TenantKey.INVOICE_TEMPLATE.toString(), tenantContext);
         return getUniqueValue(values, "invoice template", tenantContext);
     }
 
     @Override
     public String getManualPayInvoiceTemplate(final Locale locale, final InternalTenantContext tenantContext) {
-        final List<String> values = tenantDao.getTenantValueForKey(LocaleUtils.localeString(locale, TenantKey.INVOICE_MP_TEMPLATE.toString()), tenantContext);
+        final List<String> values = tenantDao.getTenantValueForKey(TenantKey.INVOICE_MP_TEMPLATE.toString(), tenantContext);
         return getUniqueValue(values, "manual pay invoice template", tenantContext);
     }
 
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index 4b0d8b5..cc48190 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -37,7 +37,7 @@ import org.killbill.billing.tenant.dao.TenantBroadcastModelDao;
 import org.killbill.billing.tenant.dao.TenantDao;
 import org.killbill.billing.tenant.dao.TenantKVModelDao;
 import org.killbill.billing.tenant.glue.DefaultTenantModule;
-import org.killbill.billing.util.config.TenantConfig;
+import org.killbill.billing.util.config.definition.TenantConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.commons.concurrent.Executors;
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
index 81d3df5..b9a22bc 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -18,7 +18,10 @@
 
 package org.killbill.billing.tenant.api.user;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.killbill.billing.ErrorCode;
@@ -32,6 +35,7 @@ import org.killbill.billing.tenant.api.TenantData;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.tenant.dao.TenantDao;
+import org.killbill.billing.tenant.dao.TenantKVModelDao;
 import org.killbill.billing.tenant.dao.TenantModelDao;
 import org.killbill.billing.util.cache.Cachable.CacheType;
 import org.killbill.billing.util.cache.CacheController;
@@ -83,8 +87,12 @@ public class DefaultTenantUserApi implements TenantUserApi {
     public Tenant createTenant(final TenantData data, final CallContext context) throws TenantApiException {
         final Tenant tenant = new DefaultTenant(data);
 
+        if (null != tenant.getExternalKey() && tenant.getExternalKey().length() > 255) {
+            throw new TenantApiException(ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED);
+        }
+
         try {
-            tenantDao.create(new TenantModelDao(tenant), internalCallContextFactory.createInternalCallContext(context));
+            tenantDao.create(new TenantModelDao(tenant), internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
         } catch (final TenantApiException e) {
             throw new TenantApiException(e, ErrorCode.TENANT_CREATION_FAILED);
         }
@@ -112,7 +120,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
 
     @Override
     public List<String> getTenantValuesForKey(final String key, final TenantContext context) throws TenantApiException {
-        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
         if (!isCachedInTenantKVCache(key)) {
             return tenantDao.getTenantValueForKey(key, internalContext);
         } else {
@@ -123,21 +131,45 @@ public class DefaultTenantUserApi implements TenantUserApi {
     @Override
     public void addTenantKeyValue(final String key, final String value, final CallContext context) throws TenantApiException {
         // Invalidate tenantKVCache after we store (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
-        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
         final String tenantKey = getCacheKeyName(key, internalContext);
         tenantDao.addTenantKeyValue(key, value, isSingleValueKey(key), internalContext);
         tenantKVCache.remove(tenantKey);
     }
 
     @Override
+    public void updateTenantKeyValue(final String key, final String value, final CallContext context) throws TenantApiException {
+        // Invalidate tenantKVCache after we store (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
+        final String tenantKey = getCacheKeyName(key, internalContext);
+        tenantDao.updateTenantLastKeyValue(key, value, internalContext);
+        tenantKVCache.remove(tenantKey);
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final CallContext context) throws TenantApiException {
         // Invalidate tenantKVCache after we delete (to avoid race conditions). Multi-node invalidation will follow the TenantBroadcast pattern
-        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(context);
+        final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context);
         final String tenantKey = getCacheKeyName(key, internalContext);
         tenantDao.deleteTenantKey(key, internalContext);
         tenantKVCache.remove(tenantKey);
     }
 
+    @Override
+    public Map<String, List<String>> searchTenantKeyValues(String searchKey, TenantContext context) throws TenantApiException {
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
+        final List<TenantKVModelDao> daoResult = tenantDao.searchTenantKeyValues(searchKey, internalContext);
+        final Map<String, List<String>> result = new HashMap<String, List<String>>();
+        for (final TenantKVModelDao cur : daoResult) {
+            if (!result.containsKey(cur.getTenantKey())) {
+                result.put(cur.getTenantKey(), new ArrayList<String>());
+            }
+            result.get(cur.getTenantKey()).add(cur.getTenantValue());
+        }
+        return result;
+    }
+
+
     private List<String> getCachedTenantValuesForKey(final String key, final InternalTenantContext internalContext) {
         final String tenantKey = getCacheKeyName(key, internalContext);
         final Object cachedTenantValues = tenantKVCache.get(tenantKey, new CacheLoaderArgument(ObjectType.TENANT_KVS));
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
index 1e12715..81477ce 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/DefaultTenantDao.java
@@ -38,7 +38,7 @@ import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.util.UUIDs;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.dao.EntityDaoBase;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
@@ -154,6 +154,32 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
     }
 
     @Override
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                final TenantKVModelDao tenantKVModelDao = new TenantKVModelDao(UUIDs.randomUUID(), context.getCreatedDate(), context.getUpdatedDate(), key, value);
+                final TenantKVSqlDao tenantKVSqlDao = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class);
+
+                // Retrieve all values for key ordered with recordId (last at the end)
+                final List<TenantKVModelDao> tenantKV = tenantKVSqlDao.getTenantValueForKey(key, context);
+                final String id;
+                if (!tenantKV.isEmpty()) {
+                    id = tenantKV.get(tenantKV.size() - 1).getId().toString();
+                    tenantKVSqlDao.updateTenantValueKey(id, value, context);
+                } else {
+                    id = tenantKVModelDao.getId().toString();
+                    tenantKVSqlDao.create(tenantKVModelDao, context);
+                }
+                final TenantKVModelDao rehydrated = tenantKVSqlDao.getById(id, context);
+                broadcastConfigurationChangeFromTransaction(rehydrated.getRecordId(), key, entitySqlDaoWrapperFactory, context);
+                return null;
+            }
+        });
+
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
@@ -175,6 +201,16 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
         });
     }
 
+    @Override
+    public List<TenantKVModelDao> searchTenantKeyValues(final String searchKeyPrefix, final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<TenantKVModelDao>>() {
+            @Override
+            public List<TenantKVModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+                return entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).searchTenantKeyValues(String.format("%s%%", searchKeyPrefix), context);
+            }
+        });
+    }
+
     private Void deleteFromTransaction(final String key, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) {
         final List<TenantKVModelDao> tenantKVs = entitySqlDaoWrapperFactory.become(TenantKVSqlDao.class).getTenantValueForKey(key, context);
         for (TenantKVModelDao cur : tenantKVs) {
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
index 3913ae2..8f0b841 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/NoCachingTenantDao.java
@@ -95,6 +95,11 @@ public class NoCachingTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Te
     }
 
     @Override
+    public List<TenantKVModelDao> searchTenantKeyValues(final String searchKey, final InternalTenantContext context) {
+        throw new IllegalStateException("Not implemented by NoCachingTenantDao");
+    }
+
+    @Override
     public TenantModelDao getByRecordId(final Long recordId, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<TenantModelDao>() {
             @Override
@@ -110,6 +115,11 @@ public class NoCachingTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Te
     }
 
     @Override
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context) {
+        throw new IllegalStateException("Not implemented by NoCachingTenantDao");
+    }
+
+    @Override
     public void deleteTenantKey(final String key, final InternalCallContext context) {
         throw new IllegalStateException("Not implemented by NoCachingTenantDao");
     }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
index 9cbf4ef..9a2f554 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantBroadcastModelDao.java
@@ -81,6 +81,20 @@ public class TenantBroadcastModelDao extends EntityModelDaoBase implements Entit
     }
 
     @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("TenantBroadcastModelDao{");
+        sb.append("recordId=").append(getRecordId());
+        sb.append(", accountRecordId=").append(getAccountRecordId());
+        sb.append(", tenantRecordId=").append(getTenantRecordId());
+        sb.append(", type='").append(type).append('\'');
+        sb.append(", targetRecordId=").append(targetRecordId);
+        sb.append(", targetTableName=").append(targetTableName);
+        sb.append(", userToken=").append(userToken);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
     public boolean equals(final Object o) {
         if (this == o) {
             return true;
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
index 8f48adc..538edbe 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantDao.java
@@ -22,6 +22,7 @@ import org.killbill.billing.tenant.api.Tenant;
 import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.dao.EntityDao;
 
 public interface TenantDao extends EntityDao<TenantModelDao, Tenant, TenantApiException> {
@@ -32,8 +33,12 @@ public interface TenantDao extends EntityDao<TenantModelDao, Tenant, TenantApiEx
 
     public void addTenantKeyValue(final String key, final String value, final boolean uniqueKey, final InternalCallContext context);
 
+    public void updateTenantLastKeyValue(final String key, final String value, final InternalCallContext context);
+
     public void deleteTenantKey(final String key, final InternalCallContext context);
 
     public TenantKVModelDao getKeyByRecordId(Long recordId, InternalTenantContext context);
 
+    public List<TenantKVModelDao> searchTenantKeyValues(String searchKey, InternalTenantContext context);
+
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
index bdfed51..fd523cb 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/dao/TenantKVSqlDao.java
@@ -18,18 +18,17 @@ package org.killbill.billing.tenant.dao;
 
 import java.util.List;
 
-import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
-import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
-
-import org.killbill.billing.tenant.api.TenantKV;
-import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantKV;
+import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.entity.dao.Audited;
 import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @EntitySqlDaoStringTemplate
 public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV> {
@@ -38,8 +37,21 @@ public interface TenantKVSqlDao extends EntitySqlDao<TenantKVModelDao, TenantKV>
     public List<TenantKVModelDao> getTenantValueForKey(@Bind("tenantKey") final String key,
                                                        @BindBean final InternalTenantContext context);
 
+
+    @SqlQuery
+    public List<TenantKVModelDao> searchTenantKeyValues(@Bind("tenantKeyPrefix") final String tenantKeyPrefix,
+                                                       @BindBean final InternalTenantContext context);
+
     @SqlUpdate
     @Audited(ChangeType.DELETE)
     public void markTenantKeyAsDeleted(@Bind("id")final String id,
                                        @BindBean final InternalCallContext context);
+
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    public void updateTenantValueKey(@Bind("id") final String id,
+                                     @Bind("tenantValue") final String tenantValue,
+                                     @BindBean final InternalCallContext context);
+
+
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
index c85a96f..b95e741 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
@@ -35,7 +35,7 @@ import org.killbill.billing.tenant.dao.NoCachingTenantDao;
 import org.killbill.billing.tenant.dao.TenantBroadcastDao;
 import org.killbill.billing.tenant.dao.TenantDao;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.TenantConfig;
+import org.killbill.billing.util.config.definition.TenantConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.killbill.billing.util.glue.NoCachingInternalCallContextFactoryProvider;
 import org.skife.config.ConfigurationObjectFactory;
diff --git a/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg b/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
index 2895b30..9495969 100644
--- a/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
+++ b/tenant/src/main/resources/org/killbill/billing/tenant/dao/TenantKVSqlDao.sql.stg
@@ -36,6 +36,18 @@ from <tableName()> t
 where t.tenant_key = :tenantKey
 and  t.is_active
 <AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
+;
+>>
+
+searchTenantKeyValues() ::= <<
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where t.tenant_key like :tenantKeyPrefix
+and  t.is_active
+<AND_CHECK_TENANT("t.")>
+<defaultOrderBy("t.")>
 ;
 >>
 
@@ -46,3 +58,12 @@ where id = :id
 <AND_CHECK_TENANT("")>
 ;
 >>
+
+
+updateTenantValueKey() ::= <<
+update <tableName()>
+set tenant_value = :tenantValue
+where id = :id
+<AND_CHECK_TENANT("")>
+;
+>>
\ No newline at end of file
diff --git a/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql b/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
index 6e34285..988714f 100644
--- a/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
+++ b/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
@@ -4,7 +4,7 @@ DROP TABLE IF EXISTS tenants;
 CREATE TABLE tenants (
     record_id serial unique,
     id varchar(36) NOT NULL,
-    external_key varchar(128) NULL,
+    external_key varchar(255) NULL,
     api_key varchar(128) NULL,
     api_secret varchar(128) NULL,
     api_salt varchar(128) NULL,
diff --git a/tenant/src/main/resources/org/killbill/billing/tenant/migration/V20161005110145__tenant_external_key.sql b/tenant/src/main/resources/org/killbill/billing/tenant/migration/V20161005110145__tenant_external_key.sql
new file mode 100644
index 0000000..fc6765f
--- /dev/null
+++ b/tenant/src/main/resources/org/killbill/billing/tenant/migration/V20161005110145__tenant_external_key.sql
@@ -0,0 +1 @@
+alter table tenants modify external_key varchar(255);
\ No newline at end of file
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/api/user/TestDefaultTenantUserApi.java b/tenant/src/test/java/org/killbill/billing/tenant/api/user/TestDefaultTenantUserApi.java
index 2d4faa4..8f4c091 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/api/user/TestDefaultTenantUserApi.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/api/user/TestDefaultTenantUserApi.java
@@ -20,9 +20,11 @@ package org.killbill.billing.tenant.api.user;
 import java.util.List;
 import java.util.UUID;
 
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.tenant.TenantTestSuiteWithEmbeddedDb;
 import org.killbill.billing.tenant.api.DefaultTenant;
 import org.killbill.billing.tenant.api.Tenant;
+import org.killbill.billing.tenant.api.TenantApiException;
 import org.killbill.billing.tenant.api.TenantData;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.testng.Assert;
@@ -141,4 +143,19 @@ public class TestDefaultTenantUserApi extends TenantTestSuiteWithEmbeddedDb {
         value = tenantUserApi.getTenantValuesForKey(tenantKey, callContext);
         Assert.assertEquals(value.size(), 0);
     }
+
+    @Test(groups = "slow", description = "Test Tenant creation with External Key over limit")
+    public void testCreateTenantWithExternalKeyOverLimit() throws Exception {
+        final TenantData tenantdata = new DefaultTenant(UUID.randomUUID(),
+                                                        clock.getUTCNow(),
+                                                        clock.getUTCNow(),
+                                                        "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis,.",
+                                                        "TTR445ee2", "dskjhfs^^54R");
+        try {
+            tenantUserApi.createTenant(tenantdata, callContext);
+            Assert.fail();
+        } catch (final TenantApiException e) {
+            Assert.assertEquals(e.getCode(), ErrorCode.EXTERNAL_KEY_LIMIT_EXCEEDED.getCode());
+        }
+    }
 }
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
index 112e3ba..ed59e32 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/dao/TestDefaultTenantDao.java
@@ -17,6 +17,7 @@
 package org.killbill.billing.tenant.dao;
 
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.apache.shiro.authc.AuthenticationInfo;
@@ -75,4 +76,55 @@ public class TestDefaultTenantDao extends TenantTestSuiteWithEmbeddedDb {
         value = tenantDao.getTenantValueForKey("THE_KEY", internalCallContext);
         Assert.assertEquals(value.size(), 0);
     }
+
+
+
+
+    @Test(groups = "slow")
+    public void testTenantKeyValueUpdate() throws Exception {
+        final DefaultTenant tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
+                                                       UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
+
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue1", false, internalCallContext);
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue2", false, internalCallContext);
+        tenantDao .addTenantKeyValue("MY_KEY", "TheValue3", false, internalCallContext);
+
+        final List<String> value = tenantDao.getTenantValueForKey("MY_KEY", internalCallContext);
+        Assert.assertEquals(value.size(), 3);
+
+
+        tenantDao.updateTenantLastKeyValue("MY_KEY", "NewValue3", internalCallContext);
+
+        final List<String> newValues = tenantDao.getTenantValueForKey("MY_KEY", internalCallContext);
+        Assert.assertEquals(newValues.size(), 3);
+
+        Assert.assertEquals(newValues.get(0), "TheValue1");
+        Assert.assertEquals(newValues.get(1), "TheValue2");
+        Assert.assertEquals(newValues.get(2), "NewValue3");
+    }
+
+
+    @Test(groups = "slow")
+    public void testTenantSearch() throws Exception {
+        final DefaultTenant tenant = new DefaultTenant(UUID.randomUUID(), null, null, UUID.randomUUID().toString(),
+                                                       UUID.randomUUID().toString(), UUID.randomUUID().toString());
+        tenantDao.create(new TenantModelDao(tenant), internalCallContext);
+
+        tenantDao .addTenantKeyValue("foobar", "foobar1", false, internalCallContext);
+        tenantDao .addTenantKeyValue("foobar", "foobar2", false, internalCallContext);
+        tenantDao .addTenantKeyValue("foobar", "foobar3", false, internalCallContext);
+
+
+        tenantDao.updateTenantLastKeyValue("foo", "foo1", internalCallContext);
+
+        tenantDao.updateTenantLastKeyValue("fooXX", "fooXX1", internalCallContext);
+
+        tenantDao.updateTenantLastKeyValue("bar", "bar", internalCallContext);
+
+        final List<TenantKVModelDao> result = tenantDao.searchTenantKeyValues("foo", internalCallContext);
+        Assert.assertEquals(result.size(), 5);
+
+    }
+
 }
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModule.java b/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModule.java
index 3fba86d..89a6621 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModule.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/glue/TestTenantModule.java
@@ -21,6 +21,7 @@ package org.killbill.billing.tenant.glue;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
+import org.killbill.billing.util.glue.ConfigModule;
 
 public class TestTenantModule extends DefaultTenantModule {
 
@@ -33,6 +34,7 @@ public class TestTenantModule extends DefaultTenantModule {
         super.configure();
 
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new CallContextModule(configSource));
     }
 }
diff --git a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
index 2c4f78f..060a087 100644
--- a/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
+++ b/tenant/src/test/java/org/killbill/billing/tenant/TenantTestSuiteWithEmbeddedDb.java
@@ -26,7 +26,7 @@ import org.killbill.billing.tenant.dao.DefaultTenantDao;
 import org.killbill.billing.tenant.dao.TenantBroadcastDao;
 import org.killbill.billing.tenant.glue.DefaultTenantModule;
 import org.killbill.billing.tenant.glue.TestTenantModuleWithEmbeddedDB;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;

usage/pom.xml 38(+37 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index 541efcf..0b98838 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>
@@ -27,6 +27,10 @@
     <name>killbill-usage</name>
     <dependencies>
         <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>annotations</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
@@ -36,6 +40,21 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
             <scope>provided</scope>
@@ -45,6 +64,11 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.antlr</groupId>
             <artifactId>stringtemplate</artifactId>
             <scope>runtime</scope>
@@ -55,6 +79,11 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-api</artifactId>
         </dependency>
         <dependency>
@@ -104,6 +133,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java b/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
index 7ce8718..f7a50f0 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/svcs/DefaultRawUsage.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -55,4 +55,15 @@ public class DefaultRawUsage implements RawUsage {
     public Long getAmount() {
         return amount;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultRawUsage{");
+        sb.append("subscriptionId=").append(subscriptionId);
+        sb.append(", recordDate=").append(recordDate);
+        sb.append(", unitType='").append(unitType).append('\'');
+        sb.append(", amount=").append(amount);
+        sb.append('}');
+        return sb.toString();
+    }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
index 45b0146..74baa77 100644
--- a/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
+++ b/usage/src/main/java/org/killbill/billing/usage/api/user/DefaultUsageUserApi.java
@@ -25,6 +25,7 @@ import java.util.UUID;
 import javax.inject.Inject;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -32,6 +33,7 @@ import org.killbill.billing.usage.api.RolledUpUnit;
 import org.killbill.billing.usage.api.RolledUpUsage;
 import org.killbill.billing.usage.api.SubscriptionUsageRecord;
 import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageApiException;
 import org.killbill.billing.usage.api.UsageRecord;
 import org.killbill.billing.usage.api.UsageUserApi;
 import org.killbill.billing.usage.dao.RolledUpUsageDao;
@@ -40,6 +42,8 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 
+import com.google.common.base.Strings;
+
 public class DefaultUsageUserApi implements UsageUserApi {
 
     private final RolledUpUsageDao rolledUpUsageDao;
@@ -53,13 +57,21 @@ public class DefaultUsageUserApi implements UsageUserApi {
     }
 
     @Override
-    public void recordRolledUpUsage(final SubscriptionUsageRecord record, final CallContext callContext) {
+    public void recordRolledUpUsage(final SubscriptionUsageRecord record, final CallContext callContext) throws UsageApiException {
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(record.getSubscriptionId(), ObjectType.SUBSCRIPTION, callContext);
-        for (UnitUsageRecord unitUsageRecord : record.getUnitUsageRecord()) {
-            for (UsageRecord usageRecord : unitUsageRecord.getDailyAmount()) {
-                rolledUpUsageDao.record(record.getSubscriptionId(), unitUsageRecord.getUnitType(), usageRecord.getDate(), usageRecord.getAmount(), internalCallContext);
+
+        // check if we have (at least) one row with the supplied tracking id
+        if(!Strings.isNullOrEmpty(record.getTrackingId()) && recordsWithTrackingIdExist(record, internalCallContext)){
+            throw new UsageApiException(ErrorCode.USAGE_RECORD_TRACKING_ID_ALREADY_EXISTS, record.getTrackingId());
+        }
+
+        final List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        for (final UnitUsageRecord unitUsageRecord : record.getUnitUsageRecord()) {
+            for (final UsageRecord usageRecord : unitUsageRecord.getDailyAmount()) {
+                usages.add(new RolledUpUsageModelDao(record.getSubscriptionId(), unitUsageRecord.getUnitType(), usageRecord.getDate(), usageRecord.getAmount(), record.getTrackingId()));
             }
         }
+        rolledUpUsageDao.record(usages, internalCallContext);
     }
 
     @Override
@@ -98,4 +110,8 @@ public class DefaultUsageUserApi implements UsageUserApi {
         }
         return result;
     }
+
+    private boolean recordsWithTrackingIdExist(SubscriptionUsageRecord record, InternalCallContext context){
+        return rolledUpUsageDao.recordsWithTrackingIdExist(record.getSubscriptionId(), record.getTrackingId(), context);
+    }
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
index 3ab2973..4d3b4c5 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/DefaultRolledUpUsageDao.java
@@ -16,7 +16,6 @@
 
 package org.killbill.billing.usage.dao;
 
-import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
@@ -37,9 +36,13 @@ public class DefaultRolledUpUsageDao implements RolledUpUsageDao {
     }
 
     @Override
-    public void record(final UUID subscriptionId, final String unitType, final LocalDate date, final Long amount, final InternalCallContext context) {
-        final RolledUpUsageModelDao rolledUpUsageModelDao = new RolledUpUsageModelDao(subscriptionId, unitType, date, amount);
-        rolledUpUsageSqlDao.create(rolledUpUsageModelDao, context);
+    public void record(final Iterable<RolledUpUsageModelDao> usages, final InternalCallContext context){
+        rolledUpUsageSqlDao.create(usages, context);
+    }
+
+    @Override
+    public Boolean recordsWithTrackingIdExist(final UUID subscriptionId, final String trackingId, final InternalTenantContext context){
+        return rolledUpUsageSqlDao.recordsWithTrackingIdExist(subscriptionId, trackingId, context) != null ;
     }
 
     @Override
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
index e458635..9f6be8e 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageDao.java
@@ -25,13 +25,13 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 
 public interface RolledUpUsageDao {
 
-    void record(UUID subscriptionId, String unitType, LocalDate date,
-                Long amount, InternalCallContext context);
+    void record(Iterable<RolledUpUsageModelDao> usages, InternalCallContext context);
+
+    Boolean recordsWithTrackingIdExist(UUID subscriptionId, String trackingId, InternalTenantContext context);
 
     List<RolledUpUsageModelDao> getUsageForSubscription(UUID subscriptionId, LocalDate startDate, LocalDate endDate, String unitType, InternalTenantContext context);
 
     List<RolledUpUsageModelDao> getAllUsageForSubscription(UUID subscriptionId, LocalDate startDate, LocalDate endDate, InternalTenantContext context);
 
-
     List<RolledUpUsageModelDao> getRawUsageForAccount(LocalDate startDate, LocalDate endDate, InternalTenantContext context);
 }
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
index 411765a..5cd4eb1 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageModelDao.java
@@ -26,25 +26,35 @@ import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.dao.EntityModelDao;
 import org.killbill.billing.util.entity.dao.EntityModelDaoBase;
 
+import com.google.common.base.Strings;
+
 public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityModelDao<Entity> {
 
     private UUID subscriptionId;
     private String unitType;
     private LocalDate recordDate;
     private Long amount;
+    private String trackingId;
 
     public RolledUpUsageModelDao() { /* For the DAO mapper */ }
 
-    public RolledUpUsageModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount) {
+    public RolledUpUsageModelDao(final UUID id, final DateTime createdDate, final DateTime updatedDate, final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount, final String trackingId) {
         super(id, createdDate, updatedDate);
         this.subscriptionId = subscriptionId;
         this.unitType = unitType;
         this.recordDate = recordDate;
         this.amount = amount;
+
+        if(Strings.isNullOrEmpty(trackingId)){
+            this.trackingId = UUIDs.randomUUID().toString();
+        }
+        else {
+            this.trackingId = trackingId;
+        }
     }
 
-    public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount) {
-        this(UUIDs.randomUUID(), null, null, subscriptionId, unitType, recordDate, amount);
+    public RolledUpUsageModelDao(final UUID subscriptionId, final String unitType, final LocalDate recordDate, final Long amount, final String trackingId) {
+        this(UUIDs.randomUUID(), null, null, subscriptionId, unitType, recordDate, amount, trackingId);
     }
 
     public UUID getSubscriptionId() {
@@ -79,6 +89,14 @@ public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityM
         this.amount = amount;
     }
 
+    public String getTrackingId() {
+        return trackingId;
+    }
+
+    public void setTrackingId(final String trackingId) {
+        this.trackingId = trackingId;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
@@ -88,6 +106,7 @@ public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityM
         sb.append(", unitType='").append(unitType).append('\'');
         sb.append(", recordDate=").append(recordDate);
         sb.append(", amount=").append(amount);
+        sb.append(", trackingId=").append(trackingId);
         sb.append('}');
         return sb.toString();
     }
@@ -118,7 +137,9 @@ public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityM
         if (subscriptionId != null ? !subscriptionId.equals(that.subscriptionId) : that.subscriptionId != null) {
             return false;
         }
-
+        if (trackingId != null ? !trackingId.equals(that.trackingId) : that.trackingId != null) {
+            return false;
+        }
         return true;
     }
 
@@ -129,6 +150,7 @@ public class RolledUpUsageModelDao extends EntityModelDaoBase implements EntityM
         result = 31 * result + (unitType != null ? unitType.hashCode() : 0);
         result = 31 * result + (recordDate != null ? recordDate.hashCode() : 0);
         result = 31 * result + (amount != null ? amount.hashCode() : 0);
+        result = 31 * result + (trackingId != null ? trackingId.hashCode() : 0);
         return result;
     }
 
diff --git a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
index a10a2c0..89fc3a2 100644
--- a/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
+++ b/usage/src/main/java/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.java
@@ -30,31 +30,36 @@ import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @EntitySqlDaoStringTemplate
 public interface RolledUpUsageSqlDao extends EntitySqlDao<RolledUpUsageModelDao, Entity> {
 
-    @SqlUpdate
-    public void create(@BindBean RolledUpUsageModelDao rolledUpUsage,
-                       @InternalTenantContextBinder final InternalCallContext context);
+    @SqlBatch
+    void create(@BindBean Iterable<RolledUpUsageModelDao> usages,
+                @InternalTenantContextBinder final InternalCallContext context);
 
     @SqlQuery
-    public List<RolledUpUsageModelDao> getUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
-                                                               @Bind("startDate") final Date startDate,
-                                                               @Bind("endDate") final Date endDate,
-                                                               @Bind("unitType") final String unitType,
-                                                               @InternalTenantContextBinder final InternalTenantContext context);
+    Long recordsWithTrackingIdExist(@Bind("subscriptionId") final UUID subscriptionId,
+                                    @Bind("trackingId") final String trackingId,
+                                    @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlQuery
-    public List<RolledUpUsageModelDao> getAllUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
-                                                                  @Bind("startDate") final Date startDate,
-                                                                  @Bind("endDate") final Date endDate,
-                                                                  @InternalTenantContextBinder final InternalTenantContext context);
+    List<RolledUpUsageModelDao> getUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
+                                                        @Bind("startDate") final Date startDate,
+                                                        @Bind("endDate") final Date endDate,
+                                                        @Bind("unitType") final String unitType,
+                                                        @InternalTenantContextBinder final InternalTenantContext context);
 
     @SqlQuery
-    public List<RolledUpUsageModelDao> getRawUsageForAccount(@Bind("startDate") final Date startDate,
-                                                             @Bind("endDate") final Date endDate,
-                                                             @InternalTenantContextBinder final InternalTenantContext context);
+    List<RolledUpUsageModelDao> getAllUsageForSubscription(@Bind("subscriptionId") final UUID subscriptionId,
+                                                           @Bind("startDate") final Date startDate,
+                                                           @Bind("endDate") final Date endDate,
+                                                           @InternalTenantContextBinder final InternalTenantContext context);
+
+    @SqlQuery
+    List<RolledUpUsageModelDao> getRawUsageForAccount(@Bind("startDate") final Date startDate,
+                                                      @Bind("endDate") final Date endDate,
+                                                      @InternalTenantContextBinder final InternalTenantContext context);
 }
diff --git a/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg b/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
index 94fcb03..b1d739c 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
+++ b/usage/src/main/resources/org/killbill/billing/usage/dao/RolledUpUsageSqlDao.sql.stg
@@ -8,6 +8,7 @@ tableFields(prefix) ::= <<
 , <prefix>unit_type
 , <prefix>record_date
 , <prefix>amount
+, <prefix>tracking_id
 , <prefix>created_by
 , <prefix>created_date
 >>
@@ -17,10 +18,21 @@ tableValues() ::= <<
 , :unitType
 , :recordDate
 , :amount
+, :trackingId
 , :userName
 , :createdDate
 >>
 
+recordsWithTrackingIdExist() ::= <<
+select
+  1
+from <tableName()>
+where subscription_id = :subscriptionId
+and tracking_id = :trackingId
+<AND_CHECK_TENANT()>
+limit 1
+;
+>>
 
 getUsageForSubscription() ::= <<
 select
diff --git a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
index 9415162..12940eb 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
+++ b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
@@ -4,10 +4,11 @@ DROP TABLE IF EXISTS rolled_up_usage;
 CREATE TABLE rolled_up_usage (
     record_id serial unique,
     id varchar(36) NOT NULL,
-    subscription_id varchar(36),
-    unit_type varchar(50),
+    subscription_id varchar(36) NOT NULL,
+    unit_type varchar(255) NOT NULL,
     record_date date NOT NULL,
     amount bigint NOT NULL,
+    tracking_id varchar(128) NOT NULL,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
     account_record_id bigint /*! unsigned */ not null,
@@ -18,3 +19,4 @@ CREATE UNIQUE INDEX rolled_up_usage_id ON rolled_up_usage(id);
 CREATE INDEX rolled_up_usage_subscription_id ON rolled_up_usage(subscription_id ASC);
 CREATE INDEX rolled_up_usage_tenant_account_record_id ON rolled_up_usage(tenant_record_id, account_record_id);
 CREATE INDEX rolled_up_usage_account_record_id ON rolled_up_usage(account_record_id);
+CREATE INDEX rolled_up_usage_tracking_id_subscription_id_tenant_record_id ON rolled_up_usage(tracking_id, subscription_id, tenant_record_id);
diff --git a/usage/src/main/resources/org/killbill/billing/usage/migration/V20160915180905__tracking_id_502.sql b/usage/src/main/resources/org/killbill/billing/usage/migration/V20160915180905__tracking_id_502.sql
new file mode 100644
index 0000000..591631d
--- /dev/null
+++ b/usage/src/main/resources/org/killbill/billing/usage/migration/V20160915180905__tracking_id_502.sql
@@ -0,0 +1,3 @@
+alter table rolled_up_usage add column tracking_id varchar(128) NOT NULL after amount;
+alter table rolled_up_usage change subscription_id subscription_id varchar(36) not null;
+alter table rolled_up_usage change unit_type unit_type varchar(255) not null;
diff --git a/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java b/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
index 5be6e0b..69a6cde 100644
--- a/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
+++ b/usage/src/test/java/org/killbill/billing/usage/dao/TestDefaultRolledUpUsageDao.java
@@ -17,14 +17,18 @@
 
 package org.killbill.billing.usage.dao;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
 import org.killbill.billing.usage.UsageTestSuiteWithEmbeddedDB;
+import org.killbill.billing.util.UUIDs;
+import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
 
 public class TestDefaultRolledUpUsageDao extends UsageTestSuiteWithEmbeddedDB {
 
@@ -37,8 +41,12 @@ public class TestDefaultRolledUpUsageDao extends UsageTestSuiteWithEmbeddedDB {
         final Long amount1 = 10L;
         final Long amount2 = 5L;
 
-        rolledUpUsageDao.record(subscriptionId, unitType, startDate, amount1, internalCallContext);
-        rolledUpUsageDao.record(subscriptionId, unitType, endDate.minusDays(1), amount2, internalCallContext);
+        RolledUpUsageModelDao usage1 = new RolledUpUsageModelDao(subscriptionId, unitType, startDate, amount1, UUID.randomUUID().toString());
+        RolledUpUsageModelDao usage2 = new RolledUpUsageModelDao(subscriptionId, unitType, endDate.minusDays(1), amount2, UUID.randomUUID().toString());
+        List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        usages.add(usage1);
+        usages.add(usage2);
+        rolledUpUsageDao.record(usages, internalCallContext);
 
         final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
         assertEquals(result.size(), 2);
@@ -63,10 +71,14 @@ public class TestDefaultRolledUpUsageDao extends UsageTestSuiteWithEmbeddedDB {
         final Long amount2 = 5L;
         final Long amount3 = 13L;
 
-        rolledUpUsageDao.record(subscriptionId, unitType1, startDate, amount1, internalCallContext);
-        rolledUpUsageDao.record(subscriptionId, unitType1, startDate.plusDays(1), amount2, internalCallContext);
-
-        rolledUpUsageDao.record(subscriptionId, unitType2, endDate.minusDays(1), amount3, internalCallContext);
+        RolledUpUsageModelDao usage1 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate, amount1, UUID.randomUUID().toString());
+        RolledUpUsageModelDao usage2 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate.plusDays(1), amount2, UUID.randomUUID().toString());
+        RolledUpUsageModelDao usage3 = new RolledUpUsageModelDao(subscriptionId, unitType2, endDate.minusDays(1), amount3, UUID.randomUUID().toString());
+        List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        usages.add(usage1);
+        usages.add(usage2);
+        usages.add(usage3);
+        rolledUpUsageDao.record(usages, internalCallContext);
 
         final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getAllUsageForSubscription(subscriptionId, startDate, endDate, internalCallContext);
         assertEquals(result.size(), 3);
@@ -91,9 +103,71 @@ public class TestDefaultRolledUpUsageDao extends UsageTestSuiteWithEmbeddedDB {
         final LocalDate startDate = new LocalDate(2013, 1, 1);
         final LocalDate endDate = new LocalDate(2013, 2, 1);
 
-        rolledUpUsageDao.record(subscriptionId, unitType, endDate, 9L, internalCallContext);
+        RolledUpUsageModelDao usage1 = new RolledUpUsageModelDao(subscriptionId, unitType, endDate, 9L, UUID.randomUUID().toString());
+        List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        usages.add(usage1);
+        rolledUpUsageDao.record(usages, internalCallContext);
 
         final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getUsageForSubscription(subscriptionId, startDate, endDate, unitType, internalCallContext);
         assertEquals(result.size(), 0);
     }
-}
+
+    @Test(groups = "slow")
+    public void testDuplicateRecords() {
+        final UUID subscriptionId = UUID.randomUUID();
+        final String unitType1 = "foo";
+        final String unitType2 = "bar";
+        final LocalDate startDate = new LocalDate(2013, 1, 1);
+        final LocalDate endDate = new LocalDate(2013, 2, 1);
+        final Long amount1 = 10L;
+        final Long amount2 = 5L;
+        final Long amount3 = 13L;
+
+        RolledUpUsageModelDao usage1 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate, amount1, UUID.randomUUID().toString());
+        RolledUpUsageModelDao usage2 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate.plusDays(1), amount2, UUID.randomUUID().toString());
+        RolledUpUsageModelDao usage3 = new RolledUpUsageModelDao(subscriptionId, unitType2, endDate.minusDays(1), amount3, UUID.randomUUID().toString());
+
+        List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        usages.add(usage1);
+        usages.add(usage2);
+        usages.add(usage3);
+        rolledUpUsageDao.record(usages, internalCallContext);
+
+        final List<RolledUpUsageModelDao> result = rolledUpUsageDao.getAllUsageForSubscription(subscriptionId, startDate, endDate, internalCallContext);
+        assertEquals(result.size(), 3);
+
+        try {
+            rolledUpUsageDao.record(usages, internalCallContext);
+            fail("duplicate records accepted");
+        } catch (UnableToExecuteStatementException e) {
+            assertEquals(result.size(), 3);
+        }
+    }
+
+    @Test(groups = "slow")
+    public void testRecordsWithTrackingIdExist() {
+        final UUID subscriptionId = UUIDs.randomUUID();
+        final String unitType1 = "foo";
+        final String unitType2 = "bar";
+        final LocalDate startDate = new LocalDate(2013, 1, 1);
+        final LocalDate endDate = new LocalDate(2013, 2, 1);
+        final Long amount1 = 10L;
+        final Long amount2 = 5L;
+        final Long amount3 = 13L;
+
+        String trackingId = UUIDs.randomUUID().toString();
+
+        RolledUpUsageModelDao usage1 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate, amount1, trackingId);
+        RolledUpUsageModelDao usage2 = new RolledUpUsageModelDao(subscriptionId, unitType1, startDate.plusDays(1), amount2, trackingId);
+        RolledUpUsageModelDao usage3 = new RolledUpUsageModelDao(subscriptionId, unitType2, endDate.minusDays(1), amount3, UUID.randomUUID().toString());
+
+        List<RolledUpUsageModelDao> usages = new ArrayList<RolledUpUsageModelDao>();
+        usages.add(usage1);
+        usages.add(usage2);
+        usages.add(usage3);
+        rolledUpUsageDao.record(usages, internalCallContext);
+
+        assertEquals(rolledUpUsageDao.recordsWithTrackingIdExist(subscriptionId, trackingId, internalCallContext),
+                     Boolean.TRUE);
+    }
+}
\ No newline at end of file
diff --git a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
index 1e71a18..e34500f 100644
--- a/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
+++ b/usage/src/test/java/org/killbill/billing/usage/glue/TestUsageModuleWithEmbeddedDB.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -19,8 +19,10 @@
 package org.killbill.billing.usage.glue;
 
 import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import org.killbill.billing.account.glue.DefaultAccountModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.CacheModule;
+import org.killbill.billing.util.glue.ConfigModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
 
 public class TestUsageModuleWithEmbeddedDB extends TestUsageModule {
@@ -35,6 +37,8 @@ public class TestUsageModuleWithEmbeddedDB extends TestUsageModule {
 
         install(new GuicyKillbillTestWithEmbeddedDBModule(configSource));
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new NonEntityDaoModule(configSource));
+        install(new DefaultAccountModule(configSource));
     }
 }

util/pom.xml 57(+44 -13)

diff --git a/util/pom.xml b/util/pom.xml
index 244aded..80b2a42 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.16.10-SNAPSHOT</version>
+        <version>0.17.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
@@ -29,11 +29,20 @@
     <name>killbill-util</name>
     <dependencies>
         <dependency>
+            <groupId>aopalliance</groupId>
+            <artifactId>aopalliance</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-annotations</artifactId>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
         <dependency>
@@ -75,6 +84,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>command</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>testing-postgresql-server</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.airlift</groupId>
+            <artifactId>units</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-core</artifactId>
         </dependency>
@@ -92,6 +116,16 @@
             <artifactId>joda-time</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-mxj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>net.sf.ehcache</groupId>
             <artifactId>ehcache</artifactId>
         </dependency>
@@ -144,10 +178,6 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
-            <artifactId>killbill-platform-base</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-osgi</artifactId>
         </dependency>
         <dependency>
@@ -162,17 +192,9 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing.plugin</groupId>
-            <artifactId>killbill-plugin-api-invoice</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.kill-bill.billing.plugin</groupId>
             <artifactId>killbill-plugin-api-notification</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.kill-bill.billing.plugin</groupId>
-            <artifactId>killbill-plugin-api-payment</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-clock</artifactId>
         </dependency>
@@ -199,11 +221,13 @@
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-mysql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-embeddeddb-postgresql</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>
@@ -229,6 +253,11 @@
             <artifactId>killbill-xmlloader</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-all</artifactId>
             <scope>test</scope>
@@ -241,6 +270,7 @@
             <!-- For Shiro -->
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -259,6 +289,7 @@
         <dependency>
             <groupId>org.weakref</groupId>
             <artifactId>jmxutils</artifactId>
+            <scope>runtime</scope>
         </dependency>
     </dependencies>
     <build>
diff --git a/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java b/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java
new file mode 100644
index 0000000..6ab4322
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/account/AccountDateTimeUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Groupon, Inc
+ * Copyright 2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.account;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.killbill.billing.account.api.Account;
+
+public abstract class AccountDateTimeUtils {
+
+    public static DateTimeZone getFixedOffsetTimeZone(final Account account) {
+        final DateTimeZone referenceDateTimeZone = account.getTimeZone();
+        final DateTime referenceDateTime = getReferenceDateTime(account);
+
+        // Check if DST was in effect at the reference date time
+        final boolean shouldUseDST = !referenceDateTimeZone.isStandardOffset(referenceDateTime.getMillis());
+        if (shouldUseDST) {
+            return DateTimeZone.forOffsetMillis(referenceDateTimeZone.getOffset(referenceDateTime.getMillis()));
+        } else {
+            return DateTimeZone.forOffsetMillis(referenceDateTimeZone.getStandardOffset(referenceDateTime.getMillis()));
+        }
+    }
+
+    public static DateTime getReferenceDateTime(final Account account) {
+        return account.getCreatedDate();
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java b/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
index 9c035a3..4692059 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/api/DefaultAuditUserApi.java
@@ -84,7 +84,7 @@ public class DefaultAuditUserApi implements AuditUserApi {
             return ImmutableList.<AuditLog>of();
         }
 
-        return auditDao.getAuditLogsForId(tableName, objectId, auditLevel, internalCallContextFactory.createInternalTenantContext(context));
+        return auditDao.getAuditLogsForId(tableName, objectId, auditLevel, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
     }
 
     private TableName getTableNameFromObjectType(final ObjectType objectType) {
diff --git a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
index 1871609..87c8433 100644
--- a/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
+++ b/util/src/main/java/org/killbill/billing/util/audit/DefaultAccountAuditLogs.java
@@ -96,6 +96,11 @@ public class DefaultAccountAuditLogs implements AccountAuditLogs {
     }
 
     @Override
+    public List<AuditLog> getAuditLogsForPaymentAttempt(final UUID paymentAttemptId) {
+        return getAuditLogs(ObjectType.PAYMENT_ATTEMPT).getAuditLogs(paymentAttemptId);
+    }
+
+    @Override
     public List<AuditLog> getAuditLogsForPaymentMethod(final UUID paymentMethodId) {
         return getAuditLogs(ObjectType.PAYMENT_METHOD).getAuditLogs(paymentMethodId);
     }
diff --git a/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
new file mode 100644
index 0000000..3ab047c
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/bcd/BillCycleDayCalculator.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.bcd;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.killbill.billing.catalog.api.BillingAlignment;
+import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.clock.ClockUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class BillCycleDayCalculator {
+
+    private static final Logger log = LoggerFactory.getLogger(BillCycleDayCalculator.class);
+
+    public static int calculateBcdForAlignment(final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final BillingAlignment alignment, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal) {
+        int result = 0;
+        switch (alignment) {
+            case ACCOUNT:
+                result = accountBillCycleDayLocal != 0 ? accountBillCycleDayLocal : calculateBcdFromSubscription(subscription, accountTimeZone);
+                break;
+            case BUNDLE:
+                result = calculateBcdFromSubscription(baseSubscription, accountTimeZone);
+                break;
+            case SUBSCRIPTION:
+                result = calculateBcdFromSubscription(subscription, accountTimeZone);
+                break;
+        }
+        return result;
+    }
+
+    private static int calculateBcdFromSubscription(final SubscriptionBase subscription, final DateTimeZone accountTimeZone) {
+        final DateTime date = subscription.getDateOfFirstRecurringNonZeroCharge();
+        final int bcdLocal = ClockUtil.toDateTime(date, accountTimeZone).getDayOfMonth();
+        log.debug("Calculated BCD: subscriptionId='{}', subscriptionStartDate='{}', accountTimeZone='{}', bcd='{}'",
+                  subscription.getId(), date.toDateTimeISO(), accountTimeZone, bcdLocal);
+        return bcdLocal;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
index a569fff..d3c48a0 100644
--- a/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
+++ b/util/src/main/java/org/killbill/billing/util/broadcast/DefaultBroadcastService.java
@@ -29,15 +29,13 @@ import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
 import org.killbill.billing.util.broadcast.dao.BroadcastDao;
 import org.killbill.billing.util.broadcast.dao.BroadcastModelDao;
-import org.killbill.billing.util.config.BroadcastConfig;
+import org.killbill.billing.util.config.definition.BroadcastConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.commons.concurrent.Executors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.eventbus.EventBus;
-
 public class DefaultBroadcastService implements BroadcastService {
 
     private final static int TERMINATION_TIMEOUT_SEC = 5;
diff --git a/util/src/main/java/org/killbill/billing/util/cache/Cachable.java b/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
index 5c5a854..a70556c 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/Cachable.java
@@ -34,6 +34,7 @@ public @interface Cachable {
     String TENANT_CATALOG_CACHE_NAME = "tenant-catalog";
     String TENANT_PAYMENT_STATE_MACHINE_CONFIG_CACHE_NAME = "tenant-payment-state-machine-config";
     String TENANT_OVERDUE_CONFIG_CACHE_NAME = "tenant-overdue-config";
+    String TENANT_CONFIG_CACHE_NAME = "tenant-config";
     String TENANT_KV_CACHE_NAME = "tenant-kv";
     String TENANT_CACHE_NAME = "tenant";
     String OVERRIDDEN_PLAN_CACHE_NAME = "overridden-plan";
@@ -71,6 +72,9 @@ public @interface Cachable {
         /* Tenant overdue config cache */
         TENANT_OVERDUE_CONFIG(TENANT_OVERDUE_CONFIG_CACHE_NAME, false),
 
+        /* Tenant overdue config cache */
+        TENANT_CONFIG(TENANT_CONFIG_CACHE_NAME, false),
+
         /* Tenant config cache */
         TENANT_KV(TENANT_KV_CACHE_NAME, false),
 
diff --git a/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java b/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
index 99429cf..305e6f2 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/EhCacheCacheManagerProvider.java
@@ -27,7 +27,7 @@ import java.util.LinkedList;
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import org.killbill.billing.util.config.CacheConfig;
+import org.killbill.billing.util.config.definition.EhCacheConfig;
 import org.killbill.xmlloader.UriAccessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,12 +45,12 @@ public class EhCacheCacheManagerProvider implements Provider<CacheManager> {
     private static final Logger logger = LoggerFactory.getLogger(EhCacheCacheManagerProvider.class);
 
     private final MetricRegistry metricRegistry;
-    private final CacheConfig cacheConfig;
+    private final EhCacheConfig cacheConfig;
     private final Collection<BaseCacheLoader> cacheLoaders = new LinkedList<BaseCacheLoader>();
 
     @Inject
     public EhCacheCacheManagerProvider(final MetricRegistry metricRegistry,
-                                       final CacheConfig cacheConfig,
+                                       final EhCacheConfig cacheConfig,
                                        final ImmutableAccountCacheLoader accountCacheLoader,
                                        final AccountBCDCacheLoader accountBCDCacheLoader,
                                        final RecordIdCacheLoader recordIdCacheLoader,
@@ -60,6 +60,7 @@ public class EhCacheCacheManagerProvider implements Provider<CacheManager> {
                                        final AuditLogCacheLoader auditLogCacheLoader,
                                        final AuditLogViaHistoryCacheLoader auditLogViaHistoryCacheLoader,
                                        final TenantCatalogCacheLoader tenantCatalogCacheLoader,
+                                       final TenantConfigCacheLoader tenantConfigCacheLoader,
                                        final TenantOverdueConfigCacheLoader tenantOverdueConfigCacheLoader,
                                        final TenantKVCacheLoader tenantKVCacheLoader,
                                        final TenantCacheLoader tenantCacheLoader,
@@ -76,6 +77,7 @@ public class EhCacheCacheManagerProvider implements Provider<CacheManager> {
         cacheLoaders.add(auditLogCacheLoader);
         cacheLoaders.add(auditLogViaHistoryCacheLoader);
         cacheLoaders.add(tenantCatalogCacheLoader);
+        cacheLoaders.add(tenantConfigCacheLoader);
         cacheLoaders.add(tenantOverdueConfigCacheLoader);
         cacheLoaders.add(tenantKVCacheLoader);
         cacheLoaders.add(tenantCacheLoader);
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantConfigCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantConfigCacheLoader.java
new file mode 100644
index 0000000..caced95
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantConfigCacheLoader.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.cache;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class TenantConfigCacheLoader extends BaseCacheLoader {
+
+    private final Logger log = LoggerFactory.getLogger(TenantConfigCacheLoader.class);
+
+    private final TenantInternalApi tenantApi;
+
+    @Inject
+    public TenantConfigCacheLoader(final TenantInternalApi tenantApi) {
+        super();
+        this.tenantApi = tenantApi;
+    }
+
+    @Override
+    public CacheType getCacheType() {
+        return CacheType.TENANT_CONFIG;
+    }
+
+    @Override
+    public Object load(final Object key, final Object argument) {
+        checkCacheLoaderStatus();
+
+        if (!(key instanceof Long)) {
+            throw new IllegalArgumentException("Unexpected key type of " + key.getClass().getName());
+        }
+        if (!(argument instanceof CacheLoaderArgument)) {
+            throw new IllegalArgumentException("Unexpected argument type of " + argument.getClass().getName());
+        }
+
+        final Long tenantRecordId = (Long) key;
+        final InternalTenantContext internalTenantContext = new InternalTenantContext(tenantRecordId);
+        final CacheLoaderArgument cacheLoaderArgument = (CacheLoaderArgument) argument;
+
+        if (cacheLoaderArgument.getArgs() == null || !(cacheLoaderArgument.getArgs()[0] instanceof LoaderCallback)) {
+            throw new IllegalArgumentException("Missing LoaderCallback from the arguments ");
+        }
+
+        final LoaderCallback loader = (LoaderCallback) cacheLoaderArgument.getArgs()[0];
+
+        final String jsonValue = tenantApi.getTenantConfig(internalTenantContext);
+
+        try {
+            return loader.loadConfig(jsonValue);
+        } catch (final IOException e) {
+            throw new IllegalArgumentException("Failed to deserialize per tenant config for tenant recordId = " + tenantRecordId, e);
+        }
+    }
+
+    public interface LoaderCallback {
+        public Object loadConfig(final String inputJson) throws IOException;
+    }
+
+}
diff --git a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
index c16cd2e..f85796b 100644
--- a/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
+++ b/util/src/main/java/org/killbill/billing/util/callcontext/InternalCallContextFactory.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2012 Ning, Inc.
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -27,7 +27,6 @@ import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountApiException;
-import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -91,7 +90,7 @@ public class InternalCallContextFactory {
      * @param context tenant callcontext (tenantId can be null only if multi-tenancy is disabled)
      * @return internal tenant callcontext
      */
-    public InternalTenantContext createInternalTenantContext(final TenantContext context) {
+    public InternalTenantContext createInternalTenantContextWithoutAccountRecordId(final TenantContext context) {
         // If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
         final Long tenantRecordId = getTenantRecordIdSafe(context);
         return createInternalTenantContext(tenantRecordId, null);
@@ -139,7 +138,7 @@ public class InternalCallContextFactory {
         if (accountRecordId == null) {
             return new InternalTenantContext(tenantRecordId);
         } else {
-            return new InternalTenantContext(tenantRecordId, accountRecordId, getAccountTimeZone(tenantRecordId, accountRecordId));
+            return new InternalTenantContext(tenantRecordId, accountRecordId, getFixedOffsetTimeZone(accountRecordId, tenantRecordId), getReferenceTime(accountRecordId, tenantRecordId));
         }
     }
 
@@ -177,15 +176,14 @@ public class InternalCallContextFactory {
 
         return createInternalCallContext(objectId, objectType, context.getUserName(), context.getCallOrigin(),
                                          context.getUserType(), context.getUserToken(), context.getReasonCode(), context.getComments(),
-                                         context.getCreatedDate(), context.getUpdatedDate(), context);
+                                         context);
     }
 
     // Used by the payment retry service
     public InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final String userName,
                                                          final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken, final Long tenantRecordId) {
         final Long accountRecordId = getAccountRecordIdSafe(objectId, objectType, tenantRecordId);
-        return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken,
-                                         null, null, clock.getUTCNow(), clock.getUTCNow());
+        return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken, null, null);
     }
 
     /**
@@ -203,7 +201,7 @@ public class InternalCallContextFactory {
      */
     public InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, @Nullable final Long accountRecordId, final String userName,
                                                          final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken) {
-        return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken, null, null, clock.getUTCNow(), clock.getUTCNow());
+        return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken, null, null);
     }
 
     /**
@@ -215,72 +213,92 @@ public class InternalCallContextFactory {
      * @param context original call callcontext
      * @return internal call callcontext
      */
-    public InternalCallContext createInternalCallContext(final CallContext context) {
+    public InternalCallContext createInternalCallContextWithoutAccountRecordId(final CallContext context) {
         // If tenant id is null, this will default to the default tenant record id (multi-tenancy disabled)
         final Long tenantRecordId = getTenantRecordIdSafe(context);
-
         populateMDCContext(null, tenantRecordId);
-
-        return new InternalCallContext(tenantRecordId, context);
+        return new InternalCallContext(tenantRecordId, context, clock.getUTCNow());
     }
 
     // Used when we need to re-hydrate the callcontext with the account_record_id (when creating the account)
     public InternalCallContext createInternalCallContext(final Long accountRecordId, final InternalCallContext context) {
-        final DateTimeZone accountTimeZone = getAccountTimeZone(context.getTenantRecordId(), accountRecordId);
-
+        final DateTimeZone fixedOffsetTimeZone = getFixedOffsetTimeZone(accountRecordId, context.getTenantRecordId());
+        final DateTime referenceTime = getReferenceTime(accountRecordId, context.getTenantRecordId());
         populateMDCContext(accountRecordId, context.getTenantRecordId());
-
-        return new InternalCallContext(context, accountRecordId, accountTimeZone);
+        return new InternalCallContext(context, accountRecordId, fixedOffsetTimeZone, referenceTime, clock.getUTCNow());
     }
 
     private InternalCallContext createInternalCallContext(final UUID objectId, final ObjectType objectType, final String userName,
                                                           final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken,
-                                                          @Nullable final String reasonCode, @Nullable final String comment, final DateTime createdDate,
-                                                          final DateTime updatedDate, final TenantContext tenantContext) {
+                                                          @Nullable final String reasonCode, @Nullable final String comment,
+                                                          final TenantContext tenantContext) {
         final Long tenantRecordId = getTenantRecordIdSafe(tenantContext);
         final Long accountRecordId = getAccountRecordIdSafe(objectId, objectType, tenantContext);
         return createInternalCallContext(tenantRecordId, accountRecordId, userName, callOrigin, userType, userToken,
-                                         reasonCode, comment, createdDate, updatedDate);
+                                         reasonCode, comment);
     }
 
-    private InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, @Nullable  final Long accountRecordId, final String userName,
+    private InternalCallContext createInternalCallContext(@Nullable final Long tenantRecordId, @Nullable final Long accountRecordId, final String userName,
                                                           final CallOrigin callOrigin, final UserType userType, @Nullable final UUID userToken,
-                                                          @Nullable final String reasonCode, @Nullable final String comment, final DateTime createdDate,
-                                                          final DateTime updatedDate) {
+                                                          @Nullable final String reasonCode, @Nullable final String comment) {
         final Long nonNulTenantRecordId = MoreObjects.firstNonNull(tenantRecordId, INTERNAL_TENANT_RECORD_ID);
-        final DateTimeZone accountTimeZone = getAccountTimeZone(tenantRecordId, accountRecordId);
+        final DateTimeZone fixedOffsetTimeZone = getFixedOffsetTimeZone(accountRecordId, tenantRecordId);
+        final DateTime referenceTime = getReferenceTime(accountRecordId, tenantRecordId);
 
         populateMDCContext(accountRecordId, nonNulTenantRecordId);
 
-        return new InternalCallContext(nonNulTenantRecordId, accountRecordId, accountTimeZone, userToken, userName, callOrigin, userType, reasonCode, comment,
-                                       createdDate, updatedDate);
+        return new InternalCallContext(nonNulTenantRecordId,
+                                       accountRecordId,
+                                       fixedOffsetTimeZone,
+                                       referenceTime,
+                                       userToken,
+                                       userName,
+                                       callOrigin,
+                                       userType,
+                                       reasonCode,
+                                       comment,
+                                       clock.getUTCNow(),
+                                       clock.getUTCNow());
     }
 
-    private void populateMDCContext(@Nullable final Long accountRecordId, final Long tenantRecordId) {
-        if (accountRecordId != null) {
-            MDC.put(MDC_KB_ACCOUNT_RECORD_ID, String.valueOf(accountRecordId));
+    private DateTimeZone getFixedOffsetTimeZone(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+        if (accountRecordId == null || accountInternalApi == null) {
+            return null;
         }
-        MDC.put(MDC_KB_TENANT_RECORD_ID, String.valueOf(tenantRecordId));
+
+        populateMDCContext(accountRecordId, tenantRecordId);
+
+        final ImmutableAccountData immutableAccountData = getImmutableAccountData(accountRecordId, tenantRecordId);
+        // Will be null while creating the account
+        return immutableAccountData == null ? null : immutableAccountData.getFixedOffsetTimeZone();
     }
 
-    private DateTimeZone getAccountTimeZone(final Long tenantRecordId, @Nullable final Long accountRecordId) {
+    private DateTime getReferenceTime(@Nullable final Long accountRecordId, final Long tenantRecordId) {
         if (accountRecordId == null || accountInternalApi == null) {
             return null;
         }
 
-        populateMDCContext(accountRecordId, tenantRecordId);
-
-        final InternalTenantContext tmp = new InternalTenantContext(tenantRecordId, accountRecordId, null);
+        final ImmutableAccountData immutableAccountData = getImmutableAccountData(accountRecordId, tenantRecordId);
+        // Will be null while creating the account
+        return immutableAccountData == null ? null : immutableAccountData.getReferenceTime();
+    }
 
+    private ImmutableAccountData getImmutableAccountData(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+        final InternalTenantContext tmp = new InternalTenantContext(tenantRecordId, accountRecordId, null, null);
         try {
-            final ImmutableAccountData immutableAccountData = accountInternalApi.getImmutableAccountDataByRecordId(accountRecordId, tmp);
-            // Will be null while creating the account
-            return immutableAccountData == null ? null : immutableAccountData.getTimeZone();
+            return accountInternalApi.getImmutableAccountDataByRecordId(accountRecordId, tmp);
         } catch (final AccountApiException e) {
             return null;
         }
     }
 
+    private void populateMDCContext(@Nullable final Long accountRecordId, final Long tenantRecordId) {
+        if (accountRecordId != null) {
+            MDC.put(MDC_KB_ACCOUNT_RECORD_ID, String.valueOf(accountRecordId));
+        }
+        MDC.put(MDC_KB_TENANT_RECORD_ID, String.valueOf(tenantRecordId));
+    }
+
     //
     // Safe NonEntityDao public wrappers
     //
diff --git a/util/src/main/java/org/killbill/billing/util/config/DefaultConfigKillbillService.java b/util/src/main/java/org/killbill/billing/util/config/DefaultConfigKillbillService.java
new file mode 100644
index 0000000..d0ce0c0
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/DefaultConfigKillbillService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.killbill.billing.platform.api.LifecycleHandlerType;
+import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
+import org.killbill.billing.util.glue.CacheModule;
+import org.killbill.billing.util.glue.ConfigModule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultConfigKillbillService implements ConfigKillbillService {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultConfigKillbillService.class);
+
+    public static final String CONFIG_SERVICE_NAME = "config-service";
+
+    private final TenantInternalApi tenantInternalApi;
+    private final CacheInvalidationCallback cacheInvalidationCallback;
+
+    @Inject
+    public DefaultConfigKillbillService(final TenantInternalApi tenantInternalApi, @Named(ConfigModule.CONFIG_INVALIDATION_CALLBACK) final CacheInvalidationCallback cacheInvalidationCallback) {
+        this.tenantInternalApi = tenantInternalApi;
+        this.cacheInvalidationCallback = cacheInvalidationCallback;
+    }
+
+    @Override
+    public String getName() {
+        return CONFIG_SERVICE_NAME;
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    public synchronized void initialize() throws ServiceException {
+        tenantInternalApi.initializeCacheInvalidationCallback(TenantKey.PER_TENANT_CONFIG, cacheInvalidationCallback);
+    }
+
+}
diff --git a/util/src/main/java/org/killbill/billing/util/config/definition/NotificationConfig.java b/util/src/main/java/org/killbill/billing/util/config/definition/NotificationConfig.java
new file mode 100644
index 0000000..b85670e
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/definition/NotificationConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config.definition;
+
+import java.util.List;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.skife.config.Config;
+import org.skife.config.Default;
+import org.skife.config.Description;
+import org.skife.config.Param;
+import org.skife.config.TimeSpan;
+
+public interface NotificationConfig extends KillbillConfig {
+
+    @Config("org.killbill.billing.server.notifications.retries")
+    @Default("15m,30m,2h,12h,1d")
+    @Description("Delay before which unresolved push notifications should be retried")
+    List<TimeSpan> getPushNotificationsRetries();
+
+    @Config("org.killbill.billing.server.notifications.retries")
+    @Default("15m,30m,2h,12h,1d")
+    @Description("Delay before which unresolved push notifications should be retried")
+    List<TimeSpan> getPushNotificationsRetries(@Param("dummy") final InternalTenantContext tenantContext);
+
+}
\ No newline at end of file
diff --git a/util/src/main/java/org/killbill/billing/util/config/tenant/CacheConfig.java b/util/src/main/java/org/killbill/billing/util/config/tenant/CacheConfig.java
new file mode 100644
index 0000000..1338b65
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/tenant/CacheConfig.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config.tenant;
+
+import java.io.IOException;
+
+import javax.annotation.Nullable;
+
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheController;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.util.cache.CacheLoaderArgument;
+import org.killbill.billing.util.cache.TenantConfigCacheLoader.LoaderCallback;
+import org.killbill.billing.util.jackson.ObjectMapper;
+
+import com.google.inject.Inject;
+
+public class CacheConfig {
+
+    private final CacheController cacheController;
+    private final CacheLoaderArgument cacheLoaderArgument;
+
+    private final ObjectMapper objectMapper;
+
+    @Inject
+    public CacheConfig(final CacheControllerDispatcher cacheControllerDispatcher) {
+        this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CONFIG);
+        this.objectMapper = new ObjectMapper();
+        this.cacheLoaderArgument = initializeCacheLoaderArgument();
+
+    }
+
+    public PerTenantConfig getPerTenantConfig(final InternalTenantContext tenantContext) {
+        final PerTenantConfig perTenantConfig = (PerTenantConfig) cacheController.get(tenantContext.getTenantRecordId(), cacheLoaderArgument);
+        return perTenantConfig;
+    }
+
+    public void clearPerTenantConfig(final InternalTenantContext tenantContext) {
+        cacheController.remove(tenantContext.getTenantRecordId());
+    }
+
+    private CacheLoaderArgument initializeCacheLoaderArgument() {
+        final LoaderCallback loaderCallback = new LoaderCallback() {
+            @Override
+            public Object loadConfig(@Nullable final String inputJson) throws IOException {
+                return inputJson != null ? objectMapper.readValue(inputJson, PerTenantConfig.class) : new PerTenantConfig();
+            }
+        };
+        final Object[] args = new Object[1];
+        args[0] = loaderCallback;
+        final ObjectType irrelevant = null;
+        final InternalTenantContext notUsed = null;
+        return new CacheLoaderArgument(irrelevant, args, notUsed);
+    }
+
+}
diff --git a/util/src/main/java/org/killbill/billing/util/config/tenant/MultiTenantConfigBase.java b/util/src/main/java/org/killbill/billing/util/config/tenant/MultiTenantConfigBase.java
new file mode 100644
index 0000000..39562ee
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/tenant/MultiTenantConfigBase.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config.tenant;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.config.definition.KillbillConfig;
+import org.skife.config.Config;
+import org.skife.config.Separator;
+import org.skife.config.TimeSpan;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public abstract class MultiTenantConfigBase {
+
+    private final Map<String, Method> methodsCache = new HashMap<String, Method>();
+    protected final CacheConfig cacheConfig;
+
+    private final static Function<String, Integer> INT_CONVERTER = new Function<String, Integer>() {
+        @Override
+        public Integer apply(final String input) {
+            return Integer.valueOf(input);
+        }
+    };
+
+    private final static Function<String, TimeSpan> TIME_SPAN_CONVERTER = new Function<String, TimeSpan>() {
+        @Override
+        public TimeSpan apply(final String input) {
+            return new TimeSpan(input);
+        }
+    };
+
+    public MultiTenantConfigBase(final CacheConfig cacheConfig) {
+        this.cacheConfig = cacheConfig;
+    }
+
+    //
+    // The conversion methds are rather limited (but this is all we need).
+    // Ideally we could reuse the bully/Coercer from skife package, but those are kept private.
+    //
+
+    protected List<String> convertToListString(final String value, final String methodName) {
+        final Method method = getConfigStaticMethodWithChecking(methodName);
+        final Iterable<String> tokens = getTokens(method, value);
+        return ImmutableList.copyOf(tokens);
+    }
+
+    protected List<TimeSpan> convertToListTimeSpan(final String value, final String methodName) {
+        final Method method = getConfigStaticMethodWithChecking(methodName);
+        final Iterable<String> tokens = getTokens(method, value);
+        return ImmutableList.copyOf(Iterables.transform(tokens, TIME_SPAN_CONVERTER));
+    }
+
+    protected List<Integer> convertToListInteger(final String value, final String methodName) {
+        final Method method = getConfigStaticMethodWithChecking(methodName);
+        final Iterable<String> tokens = getTokens(method, value);
+        return ImmutableList.copyOf(Iterables.transform(tokens, INT_CONVERTER));
+    }
+
+    protected String getStringTenantConfig(final String methodName, final InternalTenantContext tenantContext) {
+        // That means we want to default to static config value
+        if (tenantContext == null) {
+            return null;
+        }
+        final Method method = getConfigStaticMethodWithChecking(methodName);
+        return getCachedValue(method.getAnnotation(Config.class), tenantContext);
+    }
+
+    private String getCachedValue(final Config annotation, final InternalTenantContext tenantContext) {
+        final PerTenantConfig perTenantConfig = cacheConfig.getPerTenantConfig(tenantContext);
+        for (final String propertyName : annotation.value()) {
+            final String result = perTenantConfig.get(propertyName);
+            if (result != null) {
+                return result;
+            }
+        }
+        return null;
+    }
+
+    private Method getConfigStaticMethodWithChecking(final String methodName) {
+        final Method method = getConfigStaticMethod(methodName);
+        if (!method.isAnnotationPresent(Config.class)) {
+            throw new RuntimeException("Missing @Config annotation to skife config method " + method.getName());
+        }
+        return method;
+    }
+
+    private List<String> getTokens(final Method method, final String value) {
+        final Separator separator = method.getAnnotation(Separator.class);
+        return ImmutableList.copyOf(value.split(separator == null ? Separator.DEFAULT : separator.value()));
+    }
+
+    protected Method getConfigStaticMethod(final String methodName) {
+        Method method = methodsCache.get(methodName);
+        if (method == null) {
+            synchronized (methodsCache) {
+                method = methodsCache.get(methodName);
+                if (method == null) {
+                    try {
+                        method = getConfigClass().getMethod(methodName, InternalTenantContext.class);
+                        methodsCache.put(methodName, method);
+                    } catch (final NoSuchMethodException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        }
+        return method;
+    }
+
+    protected abstract Class<? extends KillbillConfig> getConfigClass();
+}
diff --git a/util/src/main/java/org/killbill/billing/util/config/tenant/PerTenantConfigInvalidationCallback.java b/util/src/main/java/org/killbill/billing/util/config/tenant/PerTenantConfigInvalidationCallback.java
new file mode 100644
index 0000000..bbc9a97
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/tenant/PerTenantConfigInvalidationCallback.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config.tenant;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PerTenantConfigInvalidationCallback implements CacheInvalidationCallback {
+
+
+    private final Logger log = LoggerFactory.getLogger(PerTenantConfigInvalidationCallback.class);
+
+    private final CacheConfig cacheConfig;
+
+    @Inject
+    public PerTenantConfigInvalidationCallback(final CacheConfig cacheConfig) {
+        this.cacheConfig = cacheConfig;
+    }
+
+    @Override
+    public void invalidateCache(final TenantKey key, final Object cookie, final InternalTenantContext tenantContext) {
+        log.info("Invalidate config cache for tenant {} ", tenantContext.getTenantRecordId());
+        cacheConfig.clearPerTenantConfig(tenantContext);
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
index 5abac28..23acc3e 100644
--- a/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/customfield/api/DefaultCustomFieldUserApi.java
@@ -69,7 +69,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
                                               new SourcePaginationBuilder<CustomFieldModelDao, CustomFieldApiException>() {
                                                   @Override
                                                   public Pagination<CustomFieldModelDao> build() {
-                                                      return customFieldDao.searchCustomFields(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return customFieldDao.searchCustomFields(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               CUSTOM_FIELD_MODEL_DAO_CUSTOM_FIELD_FUNCTION);
@@ -81,7 +81,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
                                               new SourcePaginationBuilder<CustomFieldModelDao, CustomFieldApiException>() {
                                                   @Override
                                                   public Pagination<CustomFieldModelDao> build() {
-                                                      return customFieldDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return customFieldDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               CUSTOM_FIELD_MODEL_DAO_CUSTOM_FIELD_FUNCTION);
@@ -133,7 +133,7 @@ public class DefaultCustomFieldUserApi implements CustomFieldUserApi {
 
     @Override
     public List<CustomField> getCustomFieldsForObject(final UUID objectId, final ObjectType objectType, final TenantContext context) {
-        return withCustomFieldsTransform(customFieldDao.getCustomFieldsForObject(objectId, objectType, internalCallContextFactory.createInternalTenantContext(context)));
+        return withCustomFieldsTransform(customFieldDao.getCustomFieldsForObject(objectId, objectType, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
     }
 
     @Override
diff --git a/util/src/main/java/org/killbill/billing/util/dao/TableName.java b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
index 974238e..041a38a 100644
--- a/util/src/main/java/org/killbill/billing/util/dao/TableName.java
+++ b/util/src/main/java/org/killbill/billing/util/dao/TableName.java
@@ -35,6 +35,7 @@ public enum TableName {
     INVOICE_ITEMS("invoice_items", ObjectType.INVOICE_ITEM),
     INVOICE_PAYMENTS("invoice_payments", ObjectType.INVOICE_PAYMENT),
     INVOICES("invoices", ObjectType.INVOICE),
+    INVOICE_PARENT_CHILDREN("invoice_parent_children"),
     NODE_INFOS("node_infos"),
     PAYMENT_ATTEMPT_HISTORY("payment_attempt_history"),
     PAYMENT_ATTEMPTS("payment_attempts", ObjectType.PAYMENT_ATTEMPT, PAYMENT_ATTEMPT_HISTORY),
diff --git a/util/src/main/java/org/killbill/billing/util/email/EmailConfig.java b/util/src/main/java/org/killbill/billing/util/email/EmailConfig.java
index edcfdea..fe3700a 100644
--- a/util/src/main/java/org/killbill/billing/util/email/EmailConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/email/EmailConfig.java
@@ -21,7 +21,7 @@ import org.skife.config.Default;
 import org.skife.config.DefaultNull;
 import org.skife.config.Description;
 
-import org.killbill.billing.util.config.KillbillConfig;
+import org.killbill.billing.util.config.definition.KillbillConfig;
 
 public interface EmailConfig extends KillbillConfig {
 
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java
index 824935f..69d6647 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntityModelDaoBase.java
@@ -66,4 +66,14 @@ public class EntityModelDaoBase extends EntityBase {
     public void setTenantRecordId(final Long tenantRecordId) {
         this.tenantRecordId = tenantRecordId;
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("EntityModelDaoBase{");
+        sb.append("recordId=").append(recordId);
+        sb.append(", accountRecordId=").append(accountRecordId);
+        sb.append(", tenantRecordId=").append(tenantRecordId);
+        sb.append('}');
+        return sb.toString();
+    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/export/dao/CSVExportOutputStream.java b/util/src/main/java/org/killbill/billing/util/export/dao/CSVExportOutputStream.java
index 2b0d14c..154415e 100644
--- a/util/src/main/java/org/killbill/billing/util/export/dao/CSVExportOutputStream.java
+++ b/util/src/main/java/org/killbill/billing/util/export/dao/CSVExportOutputStream.java
@@ -63,6 +63,9 @@ public class CSVExportOutputStream extends OutputStream implements DatabaseExpor
         currentTableName = tableName;
 
         final CsvSchema.Builder builder = CsvSchema.builder();
+        // Remove quoting of character which applies (somewhat arbitrarily, Tatu???) for string whose length is greater than MAX_QUOTE_CHECK = 24 -- See CVSWriter#_mayNeedQuotes
+        builder.disableQuoteChar();
+
         for (final ColumnInfo columnInfo : columnsForTable) {
             builder.addColumn(columnInfo.getColumnName(), getColumnTypeFromSqlType(columnInfo.getDataType()));
         }
diff --git a/util/src/main/java/org/killbill/billing/util/export/dao/DatabaseExportDao.java b/util/src/main/java/org/killbill/billing/util/export/dao/DatabaseExportDao.java
index 7343dbf..1f37627 100644
--- a/util/src/main/java/org/killbill/billing/util/export/dao/DatabaseExportDao.java
+++ b/util/src/main/java/org/killbill/billing/util/export/dao/DatabaseExportDao.java
@@ -23,17 +23,16 @@ import java.util.Map;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import org.skife.jdbi.v2.Handle;
-import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.ResultIterator;
-import org.skife.jdbi.v2.tweak.HandleCallback;
-
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.api.ColumnInfo;
 import org.killbill.billing.util.api.DatabaseExportOutputStream;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.util.dao.TableName;
 import org.killbill.billing.util.validation.DefaultColumnInfo;
 import org.killbill.billing.util.validation.dao.DatabaseSchemaDao;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.ResultIterator;
+import org.skife.jdbi.v2.tweak.HandleCallback;
 
 @Singleton
 public class DatabaseExportDao {
@@ -48,6 +47,35 @@ public class DatabaseExportDao {
         this.dbi = dbi;
     }
 
+    private enum TableType {
+        /* TableName.ACCOUNT */
+        KB_ACCOUNT("record_id", "tenant_record_id"),
+        /* TableName.ACCOUNT_HISTORY */
+        KB_ACCOUNT_HISTORY("target_record_id", "tenant_record_id"),
+        /* Any per-account data table */
+        KB_PER_ACCOUNT("account_record_id", "tenant_record_id"),
+        /* bus_events, notifications table */
+        NOTIFICATION("search_key1", "search_key2"),
+        /* To be discarded */
+        OTHER(null, null);
+
+        private final String accountRecordIdColumnName;
+        private final String tenantRecordIdColumnName;
+
+        TableType(final String accountRecordIdColumnName, final String tenantRecordIdColumnName) {
+            this.accountRecordIdColumnName = accountRecordIdColumnName;
+            this.tenantRecordIdColumnName = tenantRecordIdColumnName;
+        }
+
+        public String getAccountRecordIdColumnName() {
+            return accountRecordIdColumnName;
+        }
+
+        public String getTenantRecordIdColumnName() {
+            return tenantRecordIdColumnName;
+        }
+    }
+
     public void exportDataForAccount(final DatabaseExportOutputStream out, final InternalTenantContext context) {
         if (context.getAccountRecordId() == null || context.getTenantRecordId() == null) {
             return;
@@ -72,8 +100,19 @@ public class DatabaseExportDao {
         exportDataForAccountAndTable(out, columnsForTable, context);
     }
 
+
     private void exportDataForAccountAndTable(final DatabaseExportOutputStream out, final List<ColumnInfo> columnsForTable, final InternalTenantContext context) {
-        boolean hasAccountRecordIdColumn = false;
+
+
+        TableType tableType = TableType.OTHER;
+        final String tableName = columnsForTable.get(0).getTableName();
+
+        if (TableName.ACCOUNT.getTableName().equals(tableName)) {
+            tableType = TableType.KB_ACCOUNT;
+        } else if (TableName.ACCOUNT_HISTORY.getTableName().equals(tableName)) {
+            tableType = TableType.KB_ACCOUNT_HISTORY;
+        }
+
         boolean firstColumn = true;
         final StringBuilder queryBuilder = new StringBuilder("select ");
         for (final ColumnInfo column : columnsForTable) {
@@ -84,27 +123,29 @@ public class DatabaseExportDao {
             }
 
             queryBuilder.append(column.getColumnName());
-            if (column.getColumnName().equals("account_record_id")) {
-                hasAccountRecordIdColumn = true;
+
+            if (tableType == TableType.OTHER) {
+                if (column.getColumnName().equals(TableType.KB_PER_ACCOUNT.getAccountRecordIdColumnName())) {
+                    tableType = TableType.KB_PER_ACCOUNT;
+                } else if (column.getColumnName().equals(TableType.NOTIFICATION.getAccountRecordIdColumnName())) {
+                    tableType = TableType.NOTIFICATION;
+                }
             }
         }
 
-        final String tableName = columnsForTable.get(0).getTableName();
-        final boolean isAccountTable = TableName.ACCOUNT.getTableName().equals(tableName);
-
         // Don't export non-account specific tables
-        if (!isAccountTable && !hasAccountRecordIdColumn) {
+        if (tableType == TableType.OTHER) {
             return;
         }
 
         // Build the query - make sure to filter by account and tenant!
         queryBuilder.append(" from ")
-                    .append(tableName);
-        if (isAccountTable) {
-            queryBuilder.append(" where record_id = :accountRecordId and tenant_record_id = :tenantRecordId");
-        } else {
-            queryBuilder.append(" where account_record_id = :accountRecordId and tenant_record_id = :tenantRecordId");
-        }
+                    .append(tableName)
+                    .append(" where ")
+                    .append(tableType.getAccountRecordIdColumnName())
+                    .append(" = :accountRecordId and ")
+                    .append(tableType.getTenantRecordIdColumnName())
+                    .append("  = :tenantRecordId");
 
         // Notify the stream that we're about to write data for a different table
         out.newTable(tableName, columnsForTable);
@@ -124,7 +165,6 @@ public class DatabaseExportDao {
                 } finally {
                     iterator.close();
                 }
-
                 return null;
             }
         });
diff --git a/util/src/main/java/org/killbill/billing/util/glue/BroadcastModule.java b/util/src/main/java/org/killbill/billing/util/glue/BroadcastModule.java
index 72fb5be..df81cec 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/BroadcastModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/BroadcastModule.java
@@ -24,7 +24,7 @@ import org.killbill.billing.util.broadcast.DefaultBroadcastApi;
 import org.killbill.billing.util.broadcast.DefaultBroadcastService;
 import org.killbill.billing.util.broadcast.dao.BroadcastDao;
 import org.killbill.billing.util.broadcast.dao.DefaultBroadcastDao;
-import org.killbill.billing.util.config.BroadcastConfig;
+import org.killbill.billing.util.config.definition.BroadcastConfig;
 import org.skife.config.ConfigurationObjectFactory;
 
 public class BroadcastModule extends KillBillModule {
diff --git a/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java b/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
index f01e061..5a3b660 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
@@ -22,7 +22,7 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.cache.CacheControllerDispatcherProvider;
 import org.killbill.billing.util.cache.EhCacheCacheManagerProvider;
-import org.killbill.billing.util.config.CacheConfig;
+import org.killbill.billing.util.config.definition.EhCacheConfig;
 import org.skife.config.ConfigurationObjectFactory;
 
 import net.sf.ehcache.CacheManager;
@@ -35,8 +35,8 @@ public class CacheModule extends KillBillModule {
 
     @Override
     protected void configure() {
-        final CacheConfig config = new ConfigurationObjectFactory(skifeConfigSource).build(CacheConfig.class);
-        bind(CacheConfig.class).toInstance(config);
+        final EhCacheConfig config = new ConfigurationObjectFactory(skifeConfigSource).build(EhCacheConfig.class);
+        bind(EhCacheConfig.class).toInstance(config);
 
         // EhCache specifics
         bind(CacheManager.class).toProvider(EhCacheCacheManagerProvider.class).asEagerSingleton();
diff --git a/util/src/main/java/org/killbill/billing/util/glue/ConfigModule.java b/util/src/main/java/org/killbill/billing/util/glue/ConfigModule.java
new file mode 100644
index 0000000..0e73a81
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/ConfigModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.glue;
+
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.util.config.ConfigKillbillService;
+import org.killbill.billing.util.config.DefaultConfigKillbillService;
+import org.killbill.billing.util.config.tenant.CacheConfig;
+import org.killbill.billing.util.config.tenant.PerTenantConfigInvalidationCallback;
+
+import com.google.inject.name.Names;
+
+public class ConfigModule extends KillBillModule {
+
+    public static final String CONFIG_INVALIDATION_CALLBACK = "ConfigInvalidationCallback";
+
+    public ConfigModule(final KillbillConfigSource configSource) {
+        super(configSource);
+    }
+
+    @Override
+    protected void configure() {
+        bind(CacheConfig.class).asEagerSingleton();
+        bind(CacheInvalidationCallback.class).annotatedWith(Names.named(CONFIG_INVALIDATION_CALLBACK)).to(PerTenantConfigInvalidationCallback.class).asEagerSingleton();
+        bind(ConfigKillbillService.class).to(DefaultConfigKillbillService.class).asEagerSingleton();;
+    }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/IniRealmProvider.java b/util/src/main/java/org/killbill/billing/util/glue/IniRealmProvider.java
index 8e74e76..a31bd06 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/IniRealmProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/IniRealmProvider.java
@@ -28,7 +28,7 @@ import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.realm.Realm;
 import org.apache.shiro.realm.text.IniRealm;
 import org.apache.shiro.util.Factory;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/util/src/main/java/org/killbill/billing/util/glue/JDBCSessionDaoProvider.java b/util/src/main/java/org/killbill/billing/util/glue/JDBCSessionDaoProvider.java
index f9c6290..106bc83 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/JDBCSessionDaoProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/JDBCSessionDaoProvider.java
@@ -23,7 +23,7 @@ import org.apache.shiro.session.mgt.DefaultSessionManager;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.skife.jdbi.v2.IDBI;
 
-import org.killbill.billing.util.config.RbacConfig;
+import org.killbill.billing.util.config.definition.RbacConfig;
 import org.killbill.billing.util.security.shiro.dao.JDBCSessionDao;
 
 public class JDBCSessionDaoProvider implements Provider<JDBCSessionDao> {
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
index 00551eb..4e48913 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
@@ -24,7 +24,7 @@ import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.session.mgt.DefaultSessionManager;
 import org.apache.shiro.session.mgt.SessionManager;
 import org.killbill.billing.platform.api.KillbillConfigSource;
-import org.killbill.billing.util.config.RbacConfig;
+import org.killbill.billing.util.config.definition.RbacConfig;
 import org.killbill.billing.util.security.shiro.dao.JDBCSessionDao;
 import org.killbill.billing.util.security.shiro.realm.KillBillJdbcRealm;
 import org.killbill.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
diff --git a/util/src/main/java/org/killbill/billing/util/glue/SecurityModule.java b/util/src/main/java/org/killbill/billing/util/glue/SecurityModule.java
index 1f0920c..6b82c92 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/SecurityModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/SecurityModule.java
@@ -20,7 +20,7 @@ package org.killbill.billing.util.glue;
 
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.security.api.SecurityApi;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.security.api.DefaultSecurityApi;
 import org.killbill.billing.util.security.api.DefaultSecurityService;
 import org.killbill.billing.util.security.api.SecurityService;
diff --git a/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesApi.java b/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesApi.java
index 13bcb98..8503a8c 100644
--- a/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesApi.java
+++ b/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesApi.java
@@ -20,6 +20,8 @@ package org.killbill.billing.util.nodes;
 import java.io.IOException;
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 import org.killbill.CreatorName;
 import org.killbill.billing.broadcast.BroadcastApi;
 import org.killbill.billing.osgi.api.PluginInfo;
@@ -34,6 +36,7 @@ import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.google.common.base.Function;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.inject.Inject;
@@ -46,38 +49,43 @@ public class DefaultKillbillNodesApi implements KillbillNodesApi {
     private final BroadcastApi broadcastApi;
     private final NodeInfoMapper mapper;
     private final Clock clock;
-    private final PluginsInfoApi pluginInfoApi;
+    private final Function<NodeInfoModelDao, NodeInfo> nodeTransfomer;
 
     @Inject
-    public DefaultKillbillNodesApi(final NodeInfoDao nodeInfoDao, final BroadcastApi broadcastApi, final NodeInfoMapper mapper, final Clock clock, final PluginsInfoApi pluginInfoApi) {
+    public DefaultKillbillNodesApi(final NodeInfoDao nodeInfoDao, final BroadcastApi broadcastApi, final NodeInfoMapper mapper, final Clock clock) {
         this.nodeInfoDao = nodeInfoDao;
         this.broadcastApi = broadcastApi;
-        this.pluginInfoApi = pluginInfoApi;
         this.clock = clock;
         this.mapper = mapper;
-    }
-
-    @Override
-    public Iterable<NodeInfo> getNodesInfo() {
-        final List<NodeInfoModelDao> allNodes = nodeInfoDao.getAll();
-
-        final Iterable<NodeInfoModelJson> allModelNodes = Iterables.transform(allNodes, new Function<NodeInfoModelDao, NodeInfoModelJson>() {
+        this.nodeTransfomer = new Function<NodeInfoModelDao, NodeInfo>() {
             @Override
-            public NodeInfoModelJson apply(final NodeInfoModelDao input) {
+            public NodeInfo apply(final NodeInfoModelDao input) {
                 try {
-                    return mapper.deserializeNodeInfo(input.getNodeInfo());
-                } catch (IOException e) {
+                    final NodeInfoModelJson nodeInfoModelJson = mapper.deserializeNodeInfo(input.getNodeInfo());
+                    return new DefaultNodeInfo(nodeInfoModelJson);
+                } catch (final IOException e) {
                     throw new RuntimeException(e);
                 }
             }
-        });
+        };
+    }
+
+    @Override
+    public Iterable<NodeInfo> getNodesInfo() {
+        final List<NodeInfoModelDao> allNodes = nodeInfoDao.getAll();
+        return Iterables.transform(allNodes, nodeTransfomer);
+    }
 
-        return Iterables.transform(allModelNodes, new Function<NodeInfoModelJson, NodeInfo>() {
+    @Override
+    public NodeInfo getCurrentNodeInfo() {
+        final List<NodeInfoModelDao> allNodes = nodeInfoDao.getAll();
+        final NodeInfoModelDao current = Iterables.find(allNodes, new Predicate<NodeInfoModelDao>() {
             @Override
-            public NodeInfo apply(final NodeInfoModelJson input) {
-                return new DefaultNodeInfo(input);
+            public boolean apply(final NodeInfoModelDao input) {
+                return input.getNodeName().equals(CreatorName.get());
             }
         });
+        return nodeTransfomer.apply(current);
     }
 
     @Override
@@ -94,10 +102,10 @@ public class DefaultKillbillNodesApi implements KillbillNodesApi {
     }
 
     @Override
-    public void notifyPluginChanged(final PluginInfo plugin) {
+    public void notifyPluginChanged(final PluginInfo plugin,final Iterable<PluginInfo> latestPlugins) {
         final String updatedNodeInfoJson;
         try {
-            updatedNodeInfoJson = computeLatestNodeInfo();
+            updatedNodeInfoJson = computeLatestNodeInfo(latestPlugins);
             nodeInfoDao.updateNodeInfo(CreatorName.get(), updatedNodeInfoJson);
         } catch (final IOException e) {
             logger.warn("Failed to update nodeInfo after plugin change", e);
@@ -105,12 +113,11 @@ public class DefaultKillbillNodesApi implements KillbillNodesApi {
     }
 
 
-    private String computeLatestNodeInfo() throws IOException {
+    private String computeLatestNodeInfo(final Iterable<PluginInfo> rawPluginInfo) throws IOException {
 
         final NodeInfoModelDao nodeInfo = nodeInfoDao.getByNodeName(CreatorName.get());
         NodeInfoModelJson nodeInfoJson = mapper.deserializeNodeInfo(nodeInfo.getNodeInfo());
 
-        final Iterable<PluginInfo> rawPluginInfo = pluginInfoApi.getPluginsInfo();
         final List<PluginInfo> pluginInfos = rawPluginInfo.iterator().hasNext() ? ImmutableList.<PluginInfo>copyOf(rawPluginInfo) : ImmutableList.<PluginInfo>of();
 
         final NodeInfoModelJson updatedNodeInfoJson = new NodeInfoModelJson(CreatorName.get(),
diff --git a/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesService.java b/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesService.java
index a5b2a74..6236bb3 100644
--- a/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesService.java
+++ b/util/src/main/java/org/killbill/billing/util/nodes/DefaultKillbillNodesService.java
@@ -66,10 +66,22 @@ public class DefaultKillbillNodesService implements KillbillNodesService {
         return NODES_SERVICE_NAME;
     }
 
-    @LifecycleHandlerType(LifecycleHandlerType.LifecycleLevel.START_SERVICE)
+    @LifecycleHandlerType(LifecycleLevel.BOOT)
+    public void init() {
+        try {
+            // Compute a first version early on before plugins were installed to at least provide info about Kill Bill component versions
+            createBootNodeInfo(true);
+        } catch (JsonProcessingException e) {
+            logger.error("Failed to create bootNodeInfo", e);
+        }
+    }
+
+
+    @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
     public void start() {
         try {
-            createBootNodeInfo();
+            // Re-Compute including the plugins
+            createBootNodeInfo(false);
         } catch (JsonProcessingException e) {
             logger.error("Failed to create bootNodeInfo", e);
         }
@@ -80,10 +92,10 @@ public class DefaultKillbillNodesService implements KillbillNodesService {
         nodeInfoDao.delete(CreatorName.get());
     }
 
-    private void createBootNodeInfo() throws JsonProcessingException {
+    private void createBootNodeInfo(final boolean skipPlugins) throws JsonProcessingException {
 
         final DateTime bootTime = clock.getUTCNow();
-        final Iterable<PluginInfo> rawPluginInfo = pluginInfoApi.getPluginsInfo();
+        final Iterable<PluginInfo> rawPluginInfo = skipPlugins ? ImmutableList.<PluginInfo>of() : pluginInfoApi.getPluginsInfo();
         final List<PluginInfo> pluginInfo = rawPluginInfo.iterator().hasNext() ? ImmutableList.<PluginInfo>copyOf(rawPluginInfo) : ImmutableList.<PluginInfo>of();
         final String kbVersion = org.killbill.billing.util.nodes.KillbillVersions.getKillbillVersion();
         final String kbApiVersion  = org.killbill.billing.util.nodes.KillbillVersions.getApiVersion();
diff --git a/util/src/main/java/org/killbill/billing/util/security/AnnotationHierarchicalResolver.java b/util/src/main/java/org/killbill/billing/util/security/AnnotationHierarchicalResolver.java
index 3973932..7ae7360 100644
--- a/util/src/main/java/org/killbill/billing/util/security/AnnotationHierarchicalResolver.java
+++ b/util/src/main/java/org/killbill/billing/util/security/AnnotationHierarchicalResolver.java
@@ -18,6 +18,7 @@ package org.killbill.billing.util.security;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -26,13 +27,26 @@ import org.apache.shiro.aop.MethodInvocation;
 
 public class AnnotationHierarchicalResolver implements AnnotationResolver {
 
+    private final Map<String, Annotation> methodToAnnotation = new HashMap<String, Annotation>();
+
     @Override
     public Annotation getAnnotation(final MethodInvocation mi, final Class<? extends Annotation> clazz) {
         return getAnnotationFromMethod(mi.getMethod(), clazz);
     }
 
     public Annotation getAnnotationFromMethod(final Method method, final Class<? extends Annotation> clazz) {
-        return findAnnotation(method, clazz);
+        final String key = method.toString();
+        Annotation annotation = methodToAnnotation.get(key);
+        if (annotation == null) {
+            synchronized (methodToAnnotation) {
+                annotation = methodToAnnotation.get(key);
+                if (annotation == null) {
+                    annotation = findAnnotation(method, clazz);
+                    methodToAnnotation.put(key, annotation);
+                }
+            }
+        }
+        return annotation;
     }
 
     // The following comes from spring-core (AnnotationUtils) to handle annotations on interfaces
diff --git a/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java b/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
index f807229..70ed26c 100644
--- a/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
+++ b/util/src/main/java/org/killbill/billing/util/security/api/DefaultSecurityApi.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
  *
@@ -21,6 +23,7 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.annotation.Nullable;
@@ -32,8 +35,6 @@ import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.authz.AuthorizationException;
 import org.apache.shiro.mgt.DefaultSecurityManager;
 import org.apache.shiro.realm.Realm;
-import org.apache.shiro.realm.jdbc.JdbcRealm;
-import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.SimplePrincipalCollection;
 import org.apache.shiro.subject.Subject;
 import org.killbill.billing.ErrorCode;
@@ -51,6 +52,9 @@ import org.killbill.billing.util.security.shiro.realm.KillBillJdbcRealm;
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
 import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -157,7 +161,7 @@ public class DefaultSecurityApi implements SecurityApi {
                     subject.checkPermission(permissionsString[0]);
                 }
             }
-        } catch (AuthorizationException e) {
+        } catch (final AuthorizationException e) {
             throw new SecurityApiException(e, ErrorCode.SECURITY_NOT_ENOUGH_PERMISSIONS);
         }
     }
@@ -178,7 +182,6 @@ public class DefaultSecurityApi implements SecurityApi {
         invalidateJDBCAuthorizationCache(username);
     }
 
-
     @Override
     public void invalidateUser(final String username, final CallContext callContext) throws SecurityApiException {
         userDao.invalidateUser(username, callContext.getUserName());
@@ -214,16 +217,23 @@ public class DefaultSecurityApi implements SecurityApi {
         }));
     }
 
-    private List<String> sanitizeAndValidatePermissions(final List<String> permissions) throws SecurityApiException {
-
-        if (permissions == null || permissions.isEmpty()) {
-            throw new SecurityApiException(ErrorCode.SECURITY_INVALID_PERMISSIONS, "null");
+    private List<String> sanitizeAndValidatePermissions(final List<String> permissionsRaw) throws SecurityApiException {
+        if (permissionsRaw == null) {
+            return ImmutableList.<String>of();
         }
 
-        final HashMap<String, Set<String>> groupToValues = new HashMap<String, Set<String>>();
-        for (final String curPerm : permissions) {
+        final Collection<String> permissions = Collections2.<String>filter(Lists.<String, String>transform(permissionsRaw,
+                                                                                                           new Function<String, String>() {
+                                                                                                               @Override
+                                                                                                               public String apply(final String input) {
+                                                                                                                   return Strings.emptyToNull(input);
+                                                                                                               }
+                                                                                                           }),
+                                                                           Predicates.<String>notNull());
 
-            if (curPerm.equals("*")) {
+        final Map<String, Set<String>> groupToValues = new HashMap<String, Set<String>>();
+        for (final String curPerm : permissions) {
+            if ("*".equals(curPerm)) {
                 return ImmutableList.of("*");
             }
 
@@ -234,9 +244,6 @@ public class DefaultSecurityApi implements SecurityApi {
 
             boolean resolved = false;
             for (final Permission cur : Permission.values()) {
-                if (resolved) {
-                    break;
-                }
                 if (!cur.getGroup().equals(permissionParts[0])) {
                     continue;
                 }
@@ -246,7 +253,7 @@ public class DefaultSecurityApi implements SecurityApi {
                     groupPermissions = new HashSet<String>();
                     groupToValues.put(permissionParts[0], groupPermissions);
                 }
-                if (permissionParts.length == 1 || permissionParts[1].equals("*")) {
+                if (permissionParts.length == 1 || "*".equals(permissionParts[1])) {
                     groupPermissions.clear();
                     groupPermissions.add("*");
                     resolved = true;
@@ -265,9 +272,9 @@ public class DefaultSecurityApi implements SecurityApi {
         }
 
         final List<String> sanitizedPermissions = new ArrayList<String>();
-        for (String group : groupToValues.keySet()) {
+        for (final String group : groupToValues.keySet()) {
             final Set<String> groupPermissions = groupToValues.get(group);
-            for (String value : groupPermissions) {
+            for (final String value : groupPermissions) {
                 sanitizedPermissions.add(String.format("%s:%s", group, value));
             }
         }
@@ -298,7 +305,7 @@ public class DefaultSecurityApi implements SecurityApi {
         }).orNull();
 
         if (killBillJdbcRealm != null) {
-            SimplePrincipalCollection principals = new SimplePrincipalCollection();
+            final SimplePrincipalCollection principals = new SimplePrincipalCollection();
             principals.add(username, killBillJdbcRealm.getName());
             killBillJdbcRealm.clearCachedAuthorizationInfo(principals);
         }
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
index 28b9ca3..07c8737 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/DefaultUserDao.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -28,7 +28,7 @@ import org.apache.shiro.util.ByteSource;
 import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.security.SecurityApiException;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.security.shiro.KillbillCredentialsMatcher;
 import org.killbill.clock.Clock;
 import org.killbill.commons.jdbi.mapper.LowerToCamelBeanMapperFactory;
@@ -61,7 +61,6 @@ public class DefaultUserDao implements UserDao {
 
     @Override
     public void insertUser(final String username, final String password, final List<String> roles, final String createdBy) throws SecurityApiException {
-
         final ByteSource salt = rng.nextBytes();
         final String hashedPasswordBase64 = new SimpleHash(KillbillCredentialsMatcher.HASH_ALGORITHM_NAME,
                                                            password, salt.toBase64(), securityConfig.getShiroNbHashIterations()).toBase64();
@@ -71,12 +70,7 @@ public class DefaultUserDao implements UserDao {
             @Override
             public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
                 final UserRolesSqlDao userRolesSqlDao = handle.attach(UserRolesSqlDao.class);
-                for (String role : roles) {
-                    final RolesPermissionsSqlDao rolesPermissionsSqlDao = handle.attach(RolesPermissionsSqlDao.class);
-                    final List<RolesPermissionsModelDao> currentRolePermissions = rolesPermissionsSqlDao.getByRoleName(role);
-                    if (currentRolePermissions.isEmpty()) {
-                        throw new SecurityApiException(ErrorCode.SECURITY_INVALID_ROLE, role);
-                    }
+                for (final String role : roles) {
                     userRolesSqlDao.create(new UserRolesModelDao(username, role, createdDate, createdBy));
                 }
 
@@ -99,7 +93,6 @@ public class DefaultUserDao implements UserDao {
                 return userRolesSqlDao.getByUsername(username);
             }
         });
-
     }
 
     @Override
@@ -108,13 +101,12 @@ public class DefaultUserDao implements UserDao {
         dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
             public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
-
                 final RolesPermissionsSqlDao rolesPermissionsSqlDao = handle.attach(RolesPermissionsSqlDao.class);
-                final List<RolesPermissionsModelDao> existingRole  = rolesPermissionsSqlDao.getByRoleName(role);
+                final List<RolesPermissionsModelDao> existingRole = rolesPermissionsSqlDao.getByRoleName(role);
                 if (!existingRole.isEmpty()) {
                     throw new SecurityApiException(ErrorCode.SECURITY_ROLE_ALREADY_EXISTS, role);
                 }
-                for (String permission : permissions) {
+                for (final String permission : permissions) {
                     rolesPermissionsSqlDao.create(new RolesPermissionsModelDao(role, permission, createdDate, createdBy));
                 }
                 return null;
@@ -136,7 +128,6 @@ public class DefaultUserDao implements UserDao {
 
     @Override
     public void updateUserPassword(final String username, final String password, final String updatedBy) throws SecurityApiException {
-
         final ByteSource salt = rng.nextBytes();
         final String hashedPasswordBase64 = new SimpleHash(KillbillCredentialsMatcher.HASH_ALGORITHM_NAME,
                                                            password, salt.toBase64(), securityConfig.getShiroNbHashIterations()).toBase64();
@@ -204,7 +195,6 @@ public class DefaultUserDao implements UserDao {
         dbi.inTransaction(new TransactionCallback<Void>() {
             @Override
             public Void inTransaction(final Handle handle, final TransactionStatus status) throws Exception {
-
                 final DateTime updatedDate = clock.getUTCNow();
                 final UsersSqlDao usersSqlDao = handle.attach(UsersSqlDao.class);
                 final UserModelDao userModelDao = usersSqlDao.getByUsername(username);
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/KillbillCredentialsMatcher.java b/util/src/main/java/org/killbill/billing/util/security/shiro/KillbillCredentialsMatcher.java
index 1830226..c24eb12 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/KillbillCredentialsMatcher.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/KillbillCredentialsMatcher.java
@@ -21,7 +21,7 @@ package org.killbill.billing.util.security.shiro;
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
 import org.apache.shiro.crypto.hash.Sha512Hash;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 
 public class KillbillCredentialsMatcher {
 
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJdbcRealm.java b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJdbcRealm.java
index 87c8588..6970497 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJdbcRealm.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJdbcRealm.java
@@ -24,7 +24,7 @@ import javax.sql.DataSource;
 import org.apache.shiro.realm.jdbc.JdbcRealm;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.killbill.billing.platform.glue.KillBillPlatformModuleBase;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.security.shiro.KillbillCredentialsMatcher;
 
 public class KillBillJdbcRealm extends JdbcRealm {
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
index ddbd3bb..7779670 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/realm/KillBillJndiLdapRealm.java
@@ -42,7 +42,7 @@ import org.apache.shiro.subject.PrincipalCollection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
diff --git a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
index 44f530c..068c90a 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
@@ -74,7 +74,7 @@ public class DefaultTagUserApi implements TagUserApi {
 
     @Override
     public List<TagDefinition> getTagDefinitions(final TenantContext context) {
-        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContext(context)),
+        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getTagDefinitions(internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
                                                                           new Function<TagDefinitionModelDao, TagDefinition>() {
                                                                               @Override
                                                                               public TagDefinition apply(final TagDefinitionModelDao input) {
@@ -88,26 +88,26 @@ public class DefaultTagUserApi implements TagUserApi {
         if (definitionName.matches(".*[A-Z].*")) {
             throw new TagDefinitionApiException(ErrorCode.TAG_DEFINITION_HAS_UPPERCASE, definitionName);
         }
-        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContext(context));
+        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.create(definitionName, description, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
         return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
     }
 
     @Override
     public void deleteTagDefinition(final UUID definitionId, final CallContext context) throws TagDefinitionApiException {
-        tagDefinitionDao.deleteById(definitionId, internalCallContextFactory.createInternalCallContext(context));
+        tagDefinitionDao.deleteById(definitionId, internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(context));
     }
 
     @Override
     public TagDefinition getTagDefinition(final UUID tagDefinitionId, final TenantContext context)
             throws TagDefinitionApiException {
-        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContext(context));
+        final TagDefinitionModelDao tagDefinitionModelDao = tagDefinitionDao.getById(tagDefinitionId, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
         return new DefaultTagDefinition(tagDefinitionModelDao, TagModelDaoHelper.isControlTag(tagDefinitionModelDao.getName()));
     }
 
     @Override
     public List<TagDefinition> getTagDefinitions(final Collection<UUID> tagDefinitionIds, final TenantContext context)
             throws TagDefinitionApiException {
-        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContext(context)),
+        return ImmutableList.<TagDefinition>copyOf(Collections2.transform(tagDefinitionDao.getByIds(tagDefinitionIds, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
                                                                           new Function<TagDefinitionModelDao, TagDefinition>() {
                                                                               @Override
                                                                               public TagDefinition apply(final TagDefinitionModelDao input) {
@@ -148,7 +148,7 @@ public class DefaultTagUserApi implements TagUserApi {
                                               new SourcePaginationBuilder<TagModelDao, TagApiException>() {
                                                   @Override
                                                   public Pagination<TagModelDao> build() {
-                                                      return tagDao.searchTags(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return tagDao.searchTags(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               TAG_MODEL_DAO_TAG_FUNCTION);
@@ -160,7 +160,7 @@ public class DefaultTagUserApi implements TagUserApi {
                                               new SourcePaginationBuilder<TagModelDao, TagApiException>() {
                                                   @Override
                                                   public Pagination<TagModelDao> build() {
-                                                      return tagDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+                                                      return tagDao.get(offset, limit, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context));
                                                   }
                                               },
                                               TAG_MODEL_DAO_TAG_FUNCTION);
@@ -177,13 +177,13 @@ public class DefaultTagUserApi implements TagUserApi {
     @Override
     public TagDefinition getTagDefinitionForName(final String tagDefinitionName, final TenantContext context)
             throws TagDefinitionApiException {
-        return new DefaultTagDefinition(tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContext(context)),
+        return new DefaultTagDefinition(tagDefinitionDao.getByName(tagDefinitionName, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)),
                                         TagModelDaoHelper.isControlTag(tagDefinitionName));
     }
 
     @Override
     public List<Tag> getTagsForObject(final UUID objectId, final ObjectType objectType, final boolean includedDeleted, final TenantContext context) {
-        return withModelTransform(tagDao.getTagsForObject(objectId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(context)));
+        return withModelTransform(tagDao.getTagsForObject(objectId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context)));
     }
 
     @Override
diff --git a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
index da85734..f48a2e8 100644
--- a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
+++ b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestBase.java
@@ -25,9 +25,11 @@ import java.util.concurrent.TimeoutException;
 
 import org.killbill.billing.events.AccountChangeInternalEvent;
 import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
+import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.NullInvoiceInternalEvent;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
 import org.killbill.billing.events.PaymentInfoInternalEvent;
@@ -105,6 +107,9 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
             case ACCOUNT_CHANGE:
                 onAccountChange((AccountChangeInternalEvent) curEvent);
                 break;
+            case BLOCKING_STATE:
+                onBlockingState((BlockingTransitionInternalEvent) curEvent);
+                break;
             case SUBSCRIPTION_TRANSITION:
                 // We only dispatch the event for the effective date and not the requested date since we have both
                 // for subscription events.
@@ -127,6 +132,9 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
             case PAYMENT_PLUGIN_ERROR:
                 onPaymentPluginError((PaymentPluginErrorInternalEvent) curEvent);
                 break;
+            case INVOICE_PAYMENT_ERROR:
+                onInvoicePaymentError((InvoicePaymentErrorInternalEvent) curEvent);
+                break;
             default:
                 throw new RuntimeException("Unexpected event type " + curEvent.getBusEventType());
         }
@@ -145,6 +153,11 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
     }
 
     @Override
+    public void onBlockingState(final BlockingTransitionInternalEvent curEvent) {
+
+    }
+
+    @Override
     public void onInvoiceCreation(final InvoiceCreationInternalEvent curEvent) {
     }
 
@@ -163,4 +176,8 @@ public class CompletionUserRequestBase implements CompletionUserRequest {
     @Override
     public void onPaymentPluginError(final PaymentPluginErrorInternalEvent curEvent) {
     }
+
+    @Override
+    public void onInvoicePaymentError(final InvoicePaymentErrorInternalEvent curEvent) {
+    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
index 6668dcf..6876550 100644
--- a/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
+++ b/util/src/main/java/org/killbill/billing/util/userrequest/CompletionUserRequestWaiter.java
@@ -21,9 +21,11 @@ import java.util.concurrent.TimeoutException;
 
 import org.killbill.billing.events.AccountChangeInternalEvent;
 import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
+import org.killbill.billing.events.InvoicePaymentErrorInternalEvent;
 import org.killbill.billing.events.NullInvoiceInternalEvent;
 import org.killbill.billing.events.PaymentErrorInternalEvent;
 import org.killbill.billing.events.PaymentInfoInternalEvent;
@@ -37,7 +39,9 @@ public interface CompletionUserRequestWaiter {
 
     public void onAccountChange(final AccountChangeInternalEvent curEvent);
 
-    public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent curEventEffective);
+    public void onSubscriptionBaseTransition(final EffectiveSubscriptionInternalEvent curEvent);
+
+    public void onBlockingState(final BlockingTransitionInternalEvent curEvent);
 
     public void onInvoiceCreation(final InvoiceCreationInternalEvent curEvent);
 
@@ -48,4 +52,6 @@ public interface CompletionUserRequestWaiter {
     public void onPaymentError(final PaymentErrorInternalEvent curEvent);
 
     public void onPaymentPluginError(final PaymentPluginErrorInternalEvent curEvent);
+
+    public void onInvoicePaymentError(final InvoicePaymentErrorInternalEvent curEvent);
 }
diff --git a/util/src/main/resources/ehcache.xml b/util/src/main/resources/ehcache.xml
index fbf46af..61d098d 100644
--- a/util/src/main/resources/ehcache.xml
+++ b/util/src/main/resources/ehcache.xml
@@ -22,12 +22,12 @@
          xsi:noNamespaceSchemaLocation="ehcache.xsd">
 
     <defaultCache
-            maxElementsInMemory="0"
+            maxElementsInMemory="100000"
             maxElementsOnDisk="0"
-            eternal="false"
-            timeToIdleSeconds="0"
-            timeToLiveSeconds="0"
+            eternal="true"
             overflowToDisk="false"
+            diskPersistent="false"
+            memoryStoreEvictionPolicy="LFU"
             statistics="true"
             />
 
@@ -137,8 +137,19 @@
            overflowToDisk="false"
            diskPersistent="false"
            memoryStoreEvictionPolicy="LFU"
-           statistics="true"
-            >
+           statistics="true">
+        <cacheEventListenerFactory
+                class="org.killbill.billing.util.cache.ExpirationListenerFactory"
+                properties=""/>
+    </cache>
+
+    <cache name="tenant-config"
+           maxElementsInMemory="1000"
+           maxElementsOnDisk="0"
+           overflowToDisk="false"
+           diskPersistent="false"
+           memoryStoreEvictionPolicy="LFU"
+           statistics="true">
         <cacheEventListenerFactory
                 class="org.killbill.billing.util.cache.ExpirationListenerFactory"
                 properties=""/>
diff --git a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
index 13022fe..d05e54e 100644
--- a/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -298,7 +298,6 @@ values (
 <accountRecordIdValueWithComma()>
 <tenantRecordIdValueWithComma()>
 )
-;
 >>
 
 /** Audits, History **/
diff --git a/util/src/test/java/org/killbill/billing/api/TestApiListener.java b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
index cff1e27..be41d8d 100644
--- a/util/src/test/java/org/killbill/billing/api/TestApiListener.java
+++ b/util/src/test/java/org/killbill/billing/api/TestApiListener.java
@@ -30,7 +30,6 @@ import javax.inject.Inject;
 import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.BroadcastInternalEvent;
 import org.killbill.billing.events.CustomFieldEvent;
-import org.killbill.billing.events.EffectiveEntitlementInternalEvent;
 import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
 import org.killbill.billing.events.InvoiceCreationInternalEvent;
@@ -103,12 +102,9 @@ public class TestApiListener {
     }
 
     public enum NextEvent {
-        MIGRATE_ENTITLEMENT,
-        MIGRATE_BILLING,
         BROADCAST_SERVICE,
         CREATE,
         TRANSFER,
-        RE_CREATE,
         CHANGE,
         CANCEL,
         UNCANCEL,
@@ -125,13 +121,12 @@ public class TestApiListener {
         PAYMENT,
         PAYMENT_ERROR,
         PAYMENT_PLUGIN_ERROR,
-        REPAIR_BUNDLE,
         TAG,
         TAG_DEFINITION,
         CUSTOM_FIELD,
+        BCD_CHANGE
     }
 
-
     @Subscribe
     public void handleBroadcastEvents(final BroadcastInternalEvent event) {
         log.info(String.format("Got BroadcastInternalEvent event %s", event.toString()));
@@ -139,22 +134,6 @@ public class TestApiListener {
         notifyIfStackEmpty();
     }
 
-
-    @Subscribe
-    public void handleEntitlementEvents(final EffectiveEntitlementInternalEvent eventEffective) {
-        log.info(String.format("Got entitlement event %s", eventEffective.toString()));
-        switch (eventEffective.getTransitionType()) {
-            case BLOCK_BUNDLE:
-                assertEqualsNicely(NextEvent.PAUSE);
-                notifyIfStackEmpty();
-                break;
-            case UNBLOCK_BUNDLE:
-                assertEqualsNicely(NextEvent.RESUME);
-                notifyIfStackEmpty();
-                break;
-        }
-    }
-
     @Subscribe
     public void handleEntitlementEvents(final BlockingTransitionInternalEvent event) {
         log.info(String.format("Got entitlement event %s", event.toString()));
@@ -170,22 +149,10 @@ public class TestApiListener {
                 assertEqualsNicely(NextEvent.TRANSFER);
                 notifyIfStackEmpty();
                 break;
-            case MIGRATE_ENTITLEMENT:
-                assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
-                notifyIfStackEmpty();
-                break;
-            case MIGRATE_BILLING:
-                assertEqualsNicely(NextEvent.MIGRATE_BILLING);
-                notifyIfStackEmpty();
-                break;
             case CREATE:
                 assertEqualsNicely(NextEvent.CREATE);
                 notifyIfStackEmpty();
                 break;
-            case RE_CREATE:
-                assertEqualsNicely(NextEvent.RE_CREATE);
-                notifyIfStackEmpty();
-                break;
             case CANCEL:
                 assertEqualsNicely(NextEvent.CANCEL);
                 notifyIfStackEmpty();
@@ -202,6 +169,10 @@ public class TestApiListener {
                 assertEqualsNicely(NextEvent.PHASE);
                 notifyIfStackEmpty();
                 break;
+            case BCD_CHANGE:
+                assertEqualsNicely(NextEvent.BCD_CHANGE);
+                notifyIfStackEmpty();
+                break;
             default:
                 throw new RuntimeException("Unexpected event type " + eventEffective.getRequestedTransitionTime());
         }
diff --git a/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java b/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java
index 6882a8b..2066bcf 100644
--- a/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java
+++ b/util/src/test/java/org/killbill/billing/callcontext/MutableInternalCallContext.java
@@ -23,6 +23,7 @@ import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
+import org.joda.time.LocalTime;
 import org.killbill.billing.util.callcontext.CallOrigin;
 import org.killbill.billing.util.callcontext.UserType;
 
@@ -31,18 +32,21 @@ public class MutableInternalCallContext extends InternalCallContext {
     private final Long initialAccountRecordId;
     private final Long initialTenantRecordId;
     private final DateTimeZone initialReferenceDateTimeZone;
+    private final LocalTime initialReferenceTime;
     private final DateTime initialCreatedDate;
     private final DateTime initialUpdatedDate;
 
     private Long accountRecordId;
     private Long tenantRecordId;
-    private DateTimeZone referenceDateTimeZone;
+    private DateTimeZone fixedOffsetTimeZone;
+    private LocalTime referenceTime;
     private DateTime createdDate;
     private DateTime updatedDate;
 
     public MutableInternalCallContext(final Long tenantRecordId,
                                       @Nullable final Long accountRecordId,
-                                      @Nullable final DateTimeZone referenceDateTimeZone,
+                                      @Nullable final DateTimeZone fixedOffsetTimeZone,
+                                      @Nullable final DateTime referenceTime,
                                       final UUID userToken,
                                       final String userName,
                                       final CallOrigin callOrigin,
@@ -51,10 +55,11 @@ public class MutableInternalCallContext extends InternalCallContext {
                                       final String comment,
                                       final DateTime createdDate,
                                       final DateTime updatedDate) {
-        super(tenantRecordId, accountRecordId, referenceDateTimeZone, userToken, userName, callOrigin, userType, reasonCode, comment, createdDate, updatedDate);
+        super(tenantRecordId, accountRecordId, fixedOffsetTimeZone, referenceTime, userToken, userName, callOrigin, userType, reasonCode, comment, createdDate, updatedDate);
         this.initialAccountRecordId = accountRecordId;
         this.initialTenantRecordId = tenantRecordId;
-        this.initialReferenceDateTimeZone = referenceDateTimeZone;
+        this.initialReferenceDateTimeZone = fixedOffsetTimeZone;
+        this.initialReferenceTime = super.getReferenceTime();
         this.initialCreatedDate = createdDate;
         this.initialUpdatedDate = updatedDate;
 
@@ -80,12 +85,25 @@ public class MutableInternalCallContext extends InternalCallContext {
     }
 
     @Override
-    public DateTimeZone getReferenceDateTimeZone() {
-        return referenceDateTimeZone;
+    public DateTimeZone getFixedOffsetTimeZone() {
+        return fixedOffsetTimeZone;
     }
 
-    public void setReferenceDateTimeZone(final DateTimeZone referenceDateTimeZone) {
-        this.referenceDateTimeZone = referenceDateTimeZone;
+    public void setFixedOffsetTimeZone(final DateTimeZone fixedOffsetTimeZone) {
+        this.fixedOffsetTimeZone = fixedOffsetTimeZone;
+    }
+
+    @Override
+    public LocalTime getReferenceTime() {
+        return referenceTime;
+    }
+
+    public void setReferenceTime(final LocalTime referenceTime) {
+        this.referenceTime = referenceTime;
+    }
+
+    public void setReferenceTime(final DateTime referenceDateTime) {
+        this.referenceTime = computeReferenceTime(referenceDateTime);
     }
 
     @Override
@@ -109,7 +127,8 @@ public class MutableInternalCallContext extends InternalCallContext {
     public void reset() {
         setAccountRecordId(initialAccountRecordId);
         setTenantRecordId(initialTenantRecordId);
-        setReferenceDateTimeZone(initialReferenceDateTimeZone);
+        setFixedOffsetTimeZone(initialReferenceDateTimeZone);
+        setReferenceTime(initialReferenceTime);
         setCreatedDate(initialCreatedDate);
         setUpdatedDate(initialUpdatedDate);
     }
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
index 65f7fa7..3aea65b 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestModule.java
@@ -42,6 +42,7 @@ public class GuicyKillbillTestModule extends KillBillModule {
     private final MutableInternalCallContext internalCallContext = new MutableInternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID,
                                                                                                   1687L,
                                                                                                   DateTimeZone.UTC,
+                                                                                                  GuicyKillbillTestSuite.getClock().getUTCNow(),
                                                                                                   UUID.randomUUID(),
                                                                                                   UUID.randomUUID().toString(),
                                                                                                   CallOrigin.TEST,
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
index b4c5538..badf00a 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuite.java
@@ -19,13 +19,18 @@
 package org.killbill.billing;
 
 import java.lang.reflect.Method;
+import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.callcontext.MutableInternalCallContext;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.platform.test.config.TestKillbillConfigSource;
 import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.clock.Clock;
 import org.killbill.clock.ClockMock;
 import org.skife.config.ConfigSource;
 import org.slf4j.Logger;
@@ -44,6 +49,9 @@ public class GuicyKillbillTestSuite {
     private boolean hasFailed = false;
 
     @Inject
+    protected InternalCallContextFactory internalCallContextFactory;
+
+    @Inject
     protected MutableInternalCallContext internalCallContext;
 
     @Inject
@@ -95,6 +103,23 @@ public class GuicyKillbillTestSuite {
         return theStaticClock;
     }
 
+    public static void refreshCallContext(final UUID accountId,
+                                          final Clock clock,
+                                          final InternalCallContextFactory internalCallContextFactory,
+                                          final TenantContext callContext,
+                                          final MutableInternalCallContext internalCallContext) {
+        final InternalTenantContext tmp = internalCallContextFactory.createInternalTenantContext(accountId, callContext);
+        internalCallContext.setAccountRecordId(tmp.getAccountRecordId());
+        internalCallContext.setFixedOffsetTimeZone(tmp.getFixedOffsetTimeZone());
+        internalCallContext.setReferenceTime(tmp.getReferenceTime());
+        internalCallContext.setCreatedDate(clock.getUTCNow());
+        internalCallContext.setUpdatedDate(clock.getUTCNow());
+    }
+
+    protected void refreshCallContext(final UUID accountId) {
+        refreshCallContext(accountId, clock, internalCallContextFactory, callContext, internalCallContext);
+    }
+
     @BeforeMethod(alwaysRun = true)
     public void beforeMethodAlwaysRun(final Method method) throws Exception {
         log.info("***************************************************************************************************");
diff --git a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
index 734aaef..cc2e479 100644
--- a/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
+++ b/util/src/test/java/org/killbill/billing/GuicyKillbillTestSuiteNoDB.java
@@ -16,7 +16,45 @@
 
 package org.killbill.billing;
 
-public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite  {
+import java.util.UUID;
 
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.account.api.AccountInternalApi;
+import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.account.api.ImmutableAccountInternalApi;
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.callcontext.MutableInternalCallContext;
+import org.killbill.billing.dao.MockNonEntityDao;
+import org.killbill.billing.util.callcontext.CallContext;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.mockito.Mockito;
 
+public class GuicyKillbillTestSuiteNoDB extends GuicyKillbillTestSuite {
+
+    public static Account createMockAccount(final Account accountData,
+                                            final AccountUserApi accountUserApi,
+                                            final AccountInternalApi accountInternalApi,
+                                            final ImmutableAccountInternalApi immutableAccountInternalApi,
+                                            final NonEntityDao nonEntityDao,
+                                            final Clock clock,
+                                            final InternalCallContextFactory internalCallContextFactory,
+                                            final CallContext callContext,
+                                            final MutableInternalCallContext internalCallContext) throws AccountApiException {
+        final Account account = accountUserApi.createAccount(accountData, callContext);
+
+        Mockito.when(accountInternalApi.getAccountById(Mockito.<UUID>eq(account.getId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+        Mockito.when(accountInternalApi.getAccountByRecordId(Mockito.<Long>eq(internalCallContext.getAccountRecordId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+        Mockito.when(accountInternalApi.getAccountByKey(Mockito.<String>eq(account.getExternalKey()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+        Mockito.when(immutableAccountInternalApi.getImmutableAccountDataByRecordId(Mockito.<Long>eq(internalCallContext.getAccountRecordId()), Mockito.<InternalTenantContext>any())).thenReturn(account);
+
+        ((MockNonEntityDao) nonEntityDao).addTenantRecordIdMapping(account.getId(), internalCallContext);
+        ((MockNonEntityDao) nonEntityDao).addAccountRecordIdMapping(account.getId(), internalCallContext);
+
+        refreshCallContext(account.getId(), clock, internalCallContextFactory, callContext, internalCallContext);
+
+        return account;
+    }
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
index 18e47eb..d6798fc 100644
--- a/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/org/killbill/billing/mock/api/MockAccountUserApi.java
@@ -16,13 +16,13 @@
 
 package org.killbill.billing.mock.api;
 
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.joda.time.DateTimeZone;
-
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountData;
@@ -34,10 +34,11 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.DefaultPagination;
 import org.killbill.billing.util.entity.Pagination;
+import org.testng.Assert;
 
 public class MockAccountUserApi implements AccountUserApi {
 
-    private final CopyOnWriteArrayList<Account> accounts = new CopyOnWriteArrayList<Account>();
+    private final ConcurrentLinkedQueue<Account> accounts = new ConcurrentLinkedQueue<Account>();
 
     public Account createAccountFromParams(final UUID id,
                                            final String externalKey,
@@ -56,7 +57,8 @@ public class MockAccountUserApi implements AccountUserApi {
                                            final String stateOrProvince,
                                            final String country,
                                            final String postalCode,
-                                           final String phone) {
+                                           final String phone,
+                                           final String notes) {
         final Account result = new MockAccountBuilder(id)
                 .externalKey(externalKey)
                 .email(email)
@@ -74,6 +76,7 @@ public class MockAccountUserApi implements AccountUserApi {
                 .country(country)
                 .postalCode(postalCode)
                 .phone(phone)
+                .notes(notes)
                 .isNotifiedForInvoices(false)
                 .build();
         accounts.add(result);
@@ -153,7 +156,19 @@ public class MockAccountUserApi implements AccountUserApi {
 
     @Override
     public void updateAccount(final Account account, final CallContext context) {
-        throw new UnsupportedOperationException();
+        final Iterator<Account> iterator = accounts.iterator();
+        while (iterator.hasNext()) {
+            final Account account1 = iterator.next();
+            if (account.getId().equals(account1.getId())) {
+                iterator.remove();
+                break;
+            }
+        }
+        try {
+            createAccount(account, context);
+        } catch (final AccountApiException e) {
+            Assert.fail(e.toString());
+        }
     }
 
     @Override
@@ -167,4 +182,9 @@ public class MockAccountUserApi implements AccountUserApi {
             throws AccountApiException {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public List<Account> getChildrenAccounts(final UUID uuid, final TenantContext tenantContext) throws AccountApiException {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
index 3fc2521..a96e1b0 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockAccountModule.java
@@ -19,18 +19,15 @@
 package org.killbill.billing.mock.glue;
 
 import org.joda.time.DateTimeZone;
-import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.AccountUserApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
 import org.killbill.billing.account.api.ImmutableAccountInternalApi;
-import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.glue.AccountModule;
 import org.killbill.billing.mock.api.MockAccountUserApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.mockito.Mockito;
-import org.testng.Assert;
 
 public class MockAccountModule extends KillBillModule implements AccountModule {
 
@@ -53,15 +50,10 @@ public class MockAccountModule extends KillBillModule implements AccountModule {
     public void installInternalApi() {
         final ImmutableAccountData immutableAccountData = Mockito.mock(ImmutableAccountData.class);
         Mockito.when(immutableAccountData.getTimeZone()).thenReturn(DateTimeZone.UTC);
+        Mockito.when(immutableAccountData.getFixedOffsetTimeZone()).thenReturn(DateTimeZone.UTC);
 
         final AccountInternalApi accountInternalApi = Mockito.mock(AccountInternalApi.class);
         final ImmutableAccountInternalApi immutableAccountInternalApi = Mockito.mock(ImmutableAccountInternalApi.class);
-        try {
-            Mockito.when(accountInternalApi.getImmutableAccountDataByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(immutableAccountData);
-            Mockito.when(immutableAccountInternalApi.getImmutableAccountDataByRecordId(Mockito.anyLong(), Mockito.<InternalTenantContext>any())).thenReturn(immutableAccountData);
-        } catch (final AccountApiException e) {
-            Assert.fail(e.getMessage());
-        }
         bind(AccountInternalApi.class).toInstance(accountInternalApi);
         bind(ImmutableAccountInternalApi.class).toInstance(immutableAccountInternalApi);
     }
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
index 755817c..3824d12 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockInvoiceModule.java
@@ -20,7 +20,6 @@ package org.killbill.billing.mock.glue;
 
 import org.killbill.billing.glue.InvoiceModule;
 import org.killbill.billing.invoice.api.InvoiceInternalApi;
-import org.killbill.billing.invoice.api.InvoiceMigrationApi;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.platform.api.KillbillConfigSource;
@@ -44,16 +43,10 @@ public class MockInvoiceModule extends KillBillModule implements InvoiceModule {
     }
 
     @Override
-    public void installInvoiceMigrationApi() {
-        bind(InvoiceMigrationApi.class).toInstance(Mockito.mock(InvoiceMigrationApi.class));
-    }
-
-    @Override
     protected void configure() {
         installInvoiceUserApi();
         installInvoiceInternalApi();
         installInvoicePaymentApi();
-        installInvoiceMigrationApi();
     }
 
     @Override
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockOverdueModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockOverdueModule.java
index 0a88273..381bb9e 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockOverdueModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockOverdueModule.java
@@ -19,9 +19,7 @@
 package org.killbill.billing.mock.glue;
 
 import org.killbill.billing.glue.OverdueModule;
-import org.killbill.billing.overdue.OverdueInternalApi;
 import org.killbill.billing.overdue.api.OverdueApi;
-import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.mockito.Mockito;
@@ -37,11 +35,9 @@ public class MockOverdueModule extends KillBillModule implements OverdueModule {
 
     @Override
     public void installOverdueUserApi() {
-        bind(OverdueInternalApi.class).toInstance(Mockito.mock(OverdueInternalApi.class));
         bind(OverdueApi.class).toInstance(Mockito.mock(OverdueApi.class));
     }
 
-
     @Override
     protected void configure() {
         installOverdueUserApi();
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
index 78bab07..7f6a1d8 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockSubscriptionModule.java
@@ -22,7 +22,6 @@ import org.killbill.billing.glue.SubscriptionModule;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseService;
-import org.killbill.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
 import org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimelineApi;
 import org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApi;
 import org.killbill.billing.util.glue.KillBillModule;
@@ -39,10 +38,6 @@ public class MockSubscriptionModule extends KillBillModule implements Subscripti
         bind(SubscriptionBaseService.class).toInstance(Mockito.mock(SubscriptionBaseService.class));
     }
 
-    @Override
-    public void installSubscriptionMigrationApi() {
-        bind(SubscriptionBaseMigrationApi.class).toInstance(Mockito.mock(SubscriptionBaseMigrationApi.class));
-    }
 
     @Override
     public void installSubscriptionInternalApi() {
@@ -52,7 +47,6 @@ public class MockSubscriptionModule extends KillBillModule implements Subscripti
     @Override
     protected void configure() {
         installSubscriptionService();
-        installSubscriptionMigrationApi();
         installSubscriptionInternalApi();
         installSubscriptionTimelineApi();
         installSubscriptionTransferApi();
diff --git a/util/src/test/java/org/killbill/billing/mock/glue/MockTagModule.java b/util/src/test/java/org/killbill/billing/mock/glue/MockTagModule.java
index 6f77943..7c3e1a5 100644
--- a/util/src/test/java/org/killbill/billing/mock/glue/MockTagModule.java
+++ b/util/src/test/java/org/killbill/billing/mock/glue/MockTagModule.java
@@ -19,16 +19,27 @@
 package org.killbill.billing.mock.glue;
 
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.glue.TagStoreModule;
+import org.killbill.billing.util.tag.DefaultTagInternalApi;
 import org.killbill.billing.util.tag.dao.MockTagDao;
 import org.killbill.billing.util.tag.dao.MockTagDefinitionDao;
 import org.killbill.billing.util.tag.dao.TagDao;
 import org.killbill.billing.util.tag.dao.TagDefinitionDao;
+import org.mockito.Mockito;
 
 public class MockTagModule extends TagStoreModule {
 
+    private final boolean mockInternalApi;
+
     public MockTagModule(final KillbillConfigSource configSource) {
         super(configSource);
+        this.mockInternalApi = false;
+    }
+
+    public MockTagModule(final KillbillConfigSource configSource, final boolean mockInternalApi) {
+        super(configSource);
+        this.mockInternalApi = mockInternalApi;
     }
 
     @Override
@@ -36,4 +47,13 @@ public class MockTagModule extends TagStoreModule {
         bind(TagDefinitionDao.class).to(MockTagDefinitionDao.class).asEagerSingleton();
         bind(TagDao.class).to(MockTagDao.class).asEagerSingleton();
     }
+
+    @Override
+    public void installInternalApi() {
+        if (mockInternalApi) {
+            bind(TagInternalApi.class).toInstance(Mockito.mock(TagInternalApi.class));
+        } else {
+            bind(TagInternalApi.class).to(DefaultTagInternalApi.class).asEagerSingleton();
+        }
+    }
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java b/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
index 65a54d9..36975d9 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockAccountBuilder.java
@@ -20,11 +20,11 @@ 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.AccountData;
 import org.killbill.billing.account.api.MutableAccountData;
 import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.util.account.AccountDateTimeUtils;
 
 public class MockAccountBuilder {
 
@@ -34,6 +34,8 @@ public class MockAccountBuilder {
     private String name = "";
     private int firstNameLength;
     private Currency currency = Currency.USD;
+    private UUID parentAccountId;
+    private boolean isPaymentDelegatedToParent = false;
     private int billingCycleDayLocal;
     private UUID paymentMethodId;
     private DateTimeZone timeZone = DateTimeZone.UTC;
@@ -46,6 +48,7 @@ public class MockAccountBuilder {
     private String country = "";
     private String postalCode = "";
     private String phone = "";
+    private String notes = "";
     private boolean migrated;
     private boolean isNotifiedForInvoices;
     private DateTime createdDate = new DateTime(DateTimeZone.UTC);
@@ -60,7 +63,6 @@ public class MockAccountBuilder {
     }
 
     public MockAccountBuilder(final AccountData data) {
-        this.id = UUID.randomUUID();
         this.address1(data.getAddress1());
         this.address2(data.getAddress2());
         this.billingCycleDayLocal(data.getBillCycleDayLocal());
@@ -68,6 +70,8 @@ public class MockAccountBuilder {
         this.companyName(data.getCompanyName());
         this.country(data.getCountry());
         this.currency(data.getCurrency());
+        this.parentAccountId(data.getParentAccountId());
+        this.isPaymentDelegatedToParent(data.isPaymentDelegatedToParent());
         this.email(data.getEmail());
         this.externalKey(data.getExternalKey());
         this.firstNameLength(data.getFirstNameLength());
@@ -77,9 +81,17 @@ public class MockAccountBuilder {
         this.name(data.getName());
         this.paymentMethodId(data.getPaymentMethodId());
         this.phone(data.getPhone());
+        this.notes(data.getNotes());
         this.postalCode(data.getPostalCode());
         this.stateOrProvince(data.getStateOrProvince());
         this.timeZone(data.getTimeZone());
+        if (data instanceof Account) {
+            this.id = ((Account) data).getId();
+            this.createdDate(((Account) data).getCreatedDate());
+            this.updatedDate(((Account) data).getUpdatedDate());
+        } else {
+            this.id = UUID.randomUUID();
+        }
     }
 
     public MockAccountBuilder externalKey(final String externalKey) {
@@ -112,6 +124,16 @@ public class MockAccountBuilder {
         return this;
     }
 
+    public MockAccountBuilder parentAccountId(final UUID parentAccountId) {
+        this.parentAccountId = parentAccountId;
+        return this;
+    }
+
+    public MockAccountBuilder isPaymentDelegatedToParent(final boolean isPaymentDelegatedToParent) {
+        this.isPaymentDelegatedToParent = isPaymentDelegatedToParent;
+        return this;
+    }
+
     public MockAccountBuilder paymentMethodId(final UUID paymentMethodId) {
         this.paymentMethodId = paymentMethodId;
         return this;
@@ -167,6 +189,11 @@ public class MockAccountBuilder {
         return this;
     }
 
+    public MockAccountBuilder notes(final String notes) {
+        this.notes = notes;
+        return this;
+    }
+
     public MockAccountBuilder migrated(final boolean migrated) {
         this.migrated = migrated;
         return this;
@@ -206,113 +233,120 @@ public class MockAccountBuilder {
 
             @Override
             public String getName() {
-
                 return name;
             }
 
             @Override
             public Integer getFirstNameLength() {
-
                 return firstNameLength;
             }
 
             @Override
             public String getEmail() {
-
                 return email;
             }
 
             @Override
             public Integer getBillCycleDayLocal() {
-
                 return billingCycleDayLocal;
             }
 
             @Override
             public Currency getCurrency() {
-
                 return currency;
             }
 
             @Override
             public UUID getPaymentMethodId() {
-
                 return paymentMethodId;
             }
 
             @Override
             public DateTimeZone getTimeZone() {
-
                 return timeZone;
             }
 
             @Override
-            public String getLocale() {
+            public DateTimeZone getFixedOffsetTimeZone() {
+                return AccountDateTimeUtils.getFixedOffsetTimeZone(this);
+            }
+
+            @Override
+            public DateTime getReferenceTime() {
+                return AccountDateTimeUtils.getReferenceDateTime(this);
+            }
 
+            @Override
+            public String getLocale() {
                 return locale;
             }
 
             @Override
             public String getAddress1() {
-
                 return address1;
             }
 
             @Override
             public String getAddress2() {
-
                 return address2;
             }
 
             @Override
             public String getCompanyName() {
-
                 return companyName;
             }
 
             @Override
             public String getCity() {
-
                 return city;
             }
 
             @Override
             public String getStateOrProvince() {
-
                 return stateOrProvince;
             }
 
             @Override
             public String getPostalCode() {
-
                 return postalCode;
             }
 
             @Override
             public String getCountry() {
-
                 return country;
             }
 
             @Override
             public String getPhone() {
-
                 return phone;
             }
 
             @Override
-            public Boolean isMigrated() {
+            public String getNotes() {
+                return notes;
+            }
 
+            @Override
+            public Boolean isMigrated() {
                 return migrated;
             }
 
             @Override
             public Boolean isNotifiedForInvoices() {
-
                 return isNotifiedForInvoices;
             }
 
             @Override
+            public UUID getParentAccountId() {
+                return parentAccountId;
+            }
+
+            @Override
+            public Boolean isPaymentDelegatedToParent() {
+                return isPaymentDelegatedToParent;
+            }
+
+            @Override
             public UUID getId() {
                 return id;
             }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java b/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
index 793f2f0..60f76fa 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockEffectiveSubscriptionEvent.java
@@ -39,10 +39,12 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
     private final DateTime effectiveTransitionTime;
     private final EntitlementState previousState;
     private final String previousPriceList;
+    private final Integer previousBillCycleDayLocal;
     private final String previousPlan;
     private final String previousPhase;
     private final EntitlementState nextState;
     private final String nextPriceList;
+    private final Integer nextBillCycleDayLocal;
     private final String nextPlan;
     private final String nextPhase;
     private final Integer remainingEventsForUserOperation;
@@ -61,10 +63,12 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
                                           @JsonProperty("previousPlan") final String previousPlan,
                                           @JsonProperty("previousPhase") final String previousPhase,
                                           @JsonProperty("previousPriceList") final String previousPriceList,
+                                          @JsonProperty("previousBillCycleDayLocal") final Integer previousBillCycleDayLocal,
                                           @JsonProperty("nextState") final EntitlementState nextState,
                                           @JsonProperty("nextPlan") final String nextPlan,
                                           @JsonProperty("nextPhase") final String nextPhase,
                                           @JsonProperty("nextPriceList") final String nextPriceList,
+                                          @JsonProperty("nextBillCycleDayLocal") final Integer nextBillCycleDayLocal,
                                           @JsonProperty("totalOrdering") final Long totalOrdering,
                                           @JsonProperty("transitionType") final SubscriptionBaseTransitionType transitionType,
                                           @JsonProperty("remainingEventsForUserOperation") final Integer remainingEventsForUserOperation,
@@ -80,11 +84,13 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
         this.effectiveTransitionTime = effectiveTransitionTime;
         this.previousState = previousState;
         this.previousPriceList = previousPriceList;
+        this.previousBillCycleDayLocal = previousBillCycleDayLocal;
         this.previousPlan = previousPlan;
         this.previousPhase = previousPhase;
         this.nextState = nextState;
         this.nextPlan = nextPlan;
         this.nextPriceList = nextPriceList;
+        this.nextBillCycleDayLocal = nextBillCycleDayLocal;
         this.nextPhase = nextPhase;
         this.totalOrdering = totalOrdering;
         this.userToken = userToken;
@@ -132,6 +138,11 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
     }
 
     @Override
+    public Integer getPreviousBillCycleDayLocal() {
+        return previousBillCycleDayLocal;
+    }
+
+    @Override
     public String getNextPlan() {
         return nextPlan;
     }
@@ -158,6 +169,11 @@ public class MockEffectiveSubscriptionEvent extends BusEventBase implements Effe
     }
 
     @Override
+    public Integer getNextBillCycleDayLocal() {
+        return nextBillCycleDayLocal;
+    }
+
+    @Override
     public Integer getRemainingEventsForUserOperation() {
         return remainingEventsForUserOperation;
     }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPlan.java b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
index 0b5e92d..09b4e88 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPlan.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPlan.java
@@ -26,6 +26,7 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 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.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 
 public class MockPlan implements Plan {
@@ -52,12 +53,17 @@ public class MockPlan implements Plan {
     }
 
     @Override
+    public String getPriceListName() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String getName() {
         return name;
     }
 
     @Override
-    public Date getEffectiveDateForExistingSubscriptons() {
+    public Date getEffectiveDateForExistingSubscriptions() {
         return new Date();
     }
 
diff --git a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
index 3fddaf9..9722cb2 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockPriceList.java
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.mock;
 
+import java.util.Collection;
 import java.util.UUID;
 
 import org.killbill.billing.catalog.api.BillingPeriod;
@@ -23,6 +24,8 @@ import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 
+import com.google.common.collect.ImmutableList;
+
 public class MockPriceList implements PriceList {
     private final String name;
     private final Plan plan;
@@ -42,8 +45,8 @@ public class MockPriceList implements PriceList {
     }
 
     @Override
-    public Plan findPlan(final Product product, final BillingPeriod period) {
-        return plan;
+    public Collection<Plan> findPlans(final Product product, final BillingPeriod period) {
+        return ImmutableList.of(plan);
     }
 
     public Plan getPlan() {
@@ -51,7 +54,8 @@ public class MockPriceList implements PriceList {
     }
 
     @Override
-    public Plan[] getPlans() {
-        return new Plan[] { plan };
+    public Collection<Plan> getPlans() {
+        return ImmutableList.of(plan);
     }
+
 }
diff --git a/util/src/test/java/org/killbill/billing/mock/MockProduct.java b/util/src/test/java/org/killbill/billing/mock/MockProduct.java
index 046c18a..3e3ad3e 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockProduct.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockProduct.java
@@ -16,17 +16,21 @@
 
 package org.killbill.billing.mock;
 
+import java.util.Collection;
+
 import org.killbill.billing.catalog.api.Limit;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 
+import com.google.common.collect.ImmutableList;
+
 public class MockProduct implements Product {
 
     private final String name;
     private final ProductCategory category;
     private final String catalogName;
-    private final Product[] included;
-    private final Product[] available;
+    private final Collection<Product> included;
+    private final Collection<Product> available;
 
     public MockProduct() {
         this("TestProduct", ProductCategory.BASE, "Vehicules");
@@ -40,8 +44,8 @@ public class MockProduct implements Product {
         this.name = name;
         this.category = category;
         this.catalogName = catalogName;
-        this.included = included;
-        this.available = available;
+        this.included = ImmutableList.copyOf(included);
+        this.available = ImmutableList.copyOf(available);
     }
 
     @Override
@@ -60,12 +64,12 @@ public class MockProduct implements Product {
     }
 
     @Override
-    public Product[] getAvailable() {
+    public Collection<Product> getAvailable() {
         return available;
     }
 
     @Override
-    public Product[] getIncluded() {
+    public Collection<Product> getIncluded() {
         return included;
     }
 
diff --git a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
index 8dd38d7..c3287d1 100644
--- a/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
+++ b/util/src/test/java/org/killbill/billing/mock/MockSubscription.java
@@ -20,8 +20,8 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanSpecifier;
 import org.mockito.Mockito;
 
 import org.killbill.billing.catalog.api.BillingActionPolicy;
@@ -37,9 +37,6 @@ import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
 import org.killbill.billing.util.callcontext.CallContext;
-import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
-
-import com.google.common.collect.ImmutableList;
 
 public class MockSubscription implements SubscriptionBase {
 
@@ -49,30 +46,20 @@ public class MockSubscription implements SubscriptionBase {
     private Plan plan;
     private final PlanPhase phase;
     private final DateTime startDate;
-    private final List<EffectiveSubscriptionInternalEvent> transitions;
+    private final DateTime firstRecurringNonZeroChargeDate;
+    private SubscriptionBase sub;
 
-    public MockSubscription(final UUID id, final UUID bundleId, final Plan plan, final DateTime startDate, final List<EffectiveSubscriptionInternalEvent> transitions) {
+    public MockSubscription(final UUID id, final UUID bundleId, final Plan plan, final DateTime startDate, final DateTime firstRecurringNonZeroChargeDate) {
         this.id = id;
         this.bundleId = bundleId;
         this.state = EntitlementState.ACTIVE;
         this.plan = plan;
         this.phase = null;
         this.startDate = startDate;
-        this.transitions = transitions;
-    }
-
-    public MockSubscription(final EntitlementState state, final Plan plan, final PlanPhase phase) {
-        this.id = UUID.randomUUID();
-        this.bundleId = UUID.randomUUID();
-        this.state = state;
-        this.plan = plan;
-        this.phase = phase;
-        this.startDate = new DateTime(DateTimeZone.UTC);
-        this.transitions = ImmutableList.<EffectiveSubscriptionInternalEvent>of();
+        this.firstRecurringNonZeroChargeDate = firstRecurringNonZeroChargeDate;
+        this.sub = Mockito.mock(SubscriptionBase.class);
     }
 
-    SubscriptionBase sub = Mockito.mock(SubscriptionBase.class);
-
     @Override
     public boolean cancel(final CallContext context) throws SubscriptionBaseApiException {
         return sub.cancel(context);
@@ -95,20 +82,20 @@ public class MockSubscription implements SubscriptionBase {
     }
 
     @Override
-    public DateTime changePlan(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
-        return sub.changePlan(productName, term, priceList, overrides, context);
+    public DateTime changePlan(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final CallContext context) throws SubscriptionBaseApiException {
+        return sub.changePlan(spec, overrides, context);
     }
 
     @Override
-    public DateTime changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate,
+    public DateTime changePlanWithDate(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate,
                                        final CallContext context) throws SubscriptionBaseApiException {
-        return sub.changePlanWithDate(productName, term, priceList, overrides, requestedDate, context);
+        return sub.changePlanWithDate(spec, overrides, requestedDate, context);
     }
 
     @Override
-    public DateTime changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList,
+    public DateTime changePlanWithPolicy(final PlanSpecifier spec,
                                          final List<PlanPhasePriceOverride> overrides, final BillingActionPolicy policy, final CallContext context) throws SubscriptionBaseApiException {
-        return sub.changePlanWithPolicy(productName, term, priceList, overrides, policy, context);
+        return sub.changePlanWithPolicy(spec, overrides, policy, context);
     }
 
     @Override
@@ -167,11 +154,26 @@ public class MockSubscription implements SubscriptionBase {
     }
 
     @Override
+    public DateTime getDateOfFirstRecurringNonZeroCharge() {
+        return firstRecurringNonZeroChargeDate;
+    }
+
+    @Override
+    public boolean isMigrated() {
+        return false;
+    }
+
+    @Override
     public ProductCategory getCategory() {
         return sub.getCategory();
     }
 
     @Override
+    public Integer getBillCycleDayLocal() {
+        return null;
+    }
+
+    @Override
     public DateTime getFutureEndDate() {
         return sub.getFutureEndDate();
     }
@@ -217,13 +219,11 @@ public class MockSubscription implements SubscriptionBase {
 
     @Override
     public SubscriptionBaseTransition getPendingTransition() {
-        // TODO Auto-generated method stub
         return null;
     }
 
     @Override
     public SubscriptionBaseTransition getPreviousTransition() {
-        // TODO Auto-generated method stub
         return null;
     }
 
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestDefaultCallContext.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestDefaultCallContext.java
index 376f8f5..b5866d2 100644
--- a/util/src/test/java/org/killbill/billing/util/callcontext/TestDefaultCallContext.java
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestDefaultCallContext.java
@@ -19,11 +19,10 @@ package org.killbill.billing.util.callcontext;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.callcontext.DefaultCallContext;
 import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 public class TestDefaultCallContext extends UtilTestSuiteNoDB {
 
diff --git a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
index 311c7d6..9a5c01e 100644
--- a/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
+++ b/util/src/test/java/org/killbill/billing/util/callcontext/TestInternalCallContextFactory.java
@@ -90,9 +90,9 @@ public class TestInternalCallContextFactory extends UtilTestSuiteWithEmbeddedDB 
     private void verifyInternalCallContext(final InternalCallContext context) {
         Assert.assertEquals(context.getCallOrigin(), callContext.getCallOrigin());
         Assert.assertEquals(context.getComments(), callContext.getComments());
-        Assert.assertEquals(context.getCreatedDate(), callContext.getCreatedDate());
+        Assert.assertTrue(context.getCreatedDate().compareTo(callContext.getCreatedDate()) >= 0);
         Assert.assertEquals(context.getReasonCode(), callContext.getReasonCode());
-        Assert.assertEquals(context.getUpdatedDate(), callContext.getUpdatedDate());
+        Assert.assertTrue(context.getUpdatedDate().compareTo(callContext.getUpdatedDate()) >= 0);
         Assert.assertEquals(context.getCreatedBy(), callContext.getUserName());
         Assert.assertEquals(context.getUserToken(), callContext.getUserToken());
         Assert.assertEquals(context.getContextUserType(), callContext.getUserType());
diff --git a/util/src/test/java/org/killbill/billing/util/config/TestCacheConfig.java b/util/src/test/java/org/killbill/billing/util/config/TestCacheConfig.java
new file mode 100644
index 0000000..717c482
--- /dev/null
+++ b/util/src/test/java/org/killbill/billing/util/config/TestCacheConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.util.config;
+
+import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.killbill.billing.util.config.tenant.PerTenantConfig;
+import org.killbill.billing.util.jackson.ObjectMapper;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestCacheConfig extends UtilTestSuiteNoDB {
+
+    @Test(groups = "fast")
+    public void testDeSerialization() throws Exception {
+
+        final ObjectMapper objectMapper = new ObjectMapper();
+
+        final PerTenantConfig input = new PerTenantConfig();
+        input.put("key1", "foo");
+        input.put("key2", "bar");
+        input.put("key3", "34346");
+        input.put("key4", "23.999");
+
+        final String inputString = objectMapper.writeValueAsString(input);
+
+        final PerTenantConfig result = objectMapper.readValue(inputString, PerTenantConfig.class);
+        Assert.assertEquals(result.size(), 4);
+    }
+}
diff --git a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
index 855320f..a4ffe97 100644
--- a/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
+++ b/util/src/test/java/org/killbill/billing/util/export/dao/TestDatabaseExportDao.java
@@ -81,12 +81,13 @@ public class TestDatabaseExportDao extends UtilTestSuiteWithEmbeddedDB {
         // Verify new dump
         final String newDump = getDump();
         Assert.assertEquals(newDump, "-- accounts record_id,id,external_key,email,name,first_name_length,currency,billing_cycle_day_local,payment_method_id,time_zone,locale,address1,address2,company_name,city,state_or_province,country,postal_code,phone,migrated,is_notified_for_invoices,created_date,created_by,updated_date,updated_by,tenant_record_id\n" +
-                                     String.format("%s,\"%s\",,%s,%s,%s,,,,,,,,,,,,,,false,%s,\"%s\",%s,\"%s\",%s,%s", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength,
+                                     String.format("%s,%s,,%s,%s,%s,,,,,,,,,,,,,,false,%s,%s,%s,%s,%s,%s", internalCallContext.getAccountRecordId(), accountId, accountEmail, accountName, firstNameLength,
                                                    isNotifiedForInvoices, "1970-05-24T18:33:02.000+0000", createdBy, "1982-02-18T20:03:42.000+0000", updatedBy, internalCallContext.getTenantRecordId()) + "\n" +
                                      "-- " + tableNameA + " record_id,a_column,account_record_id,tenant_record_id\n" +
                                      "1,a," + internalCallContext.getAccountRecordId() + "," + internalCallContext.getTenantRecordId() + "\n" +
                                      "-- " + tableNameB + " record_id,b_column,account_record_id,tenant_record_id\n" +
                                      "1,b," + internalCallContext.getAccountRecordId() + "," + internalCallContext.getTenantRecordId() + "\n");
+
     }
 
     private String getDump() {
diff --git a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
index 030cd05..25b262c 100644
--- a/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
+++ b/util/src/test/java/org/killbill/billing/util/glue/TestUtilModule.java
@@ -42,6 +42,7 @@ public class TestUtilModule extends KillBillModule {
     protected void configure() {
         //install(new CallContextModule());
         install(new CacheModule(configSource));
+        install(new ConfigModule(configSource));
         install(new MockTenantModule(configSource));
         installHacks();
     }
diff --git a/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJdbcRealm.java b/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJdbcRealm.java
index 931e546..30f90af 100644
--- a/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJdbcRealm.java
+++ b/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJdbcRealm.java
@@ -1,6 +1,6 @@
 /*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
  *
  * The Billing Project 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
@@ -97,7 +97,6 @@ public class TestKillBillJdbcRealm extends UtilTestSuiteWithEmbeddedDB {
         } catch (final AuthenticationException e) {
         }
 
-
         final AuthenticationToken newGoodToken = new UsernamePasswordToken(username, newPassword);
         securityManager.login(subject, newGoodToken);
         Assert.assertTrue(true);
@@ -115,9 +114,16 @@ public class TestKillBillJdbcRealm extends UtilTestSuiteWithEmbeddedDB {
     }
 
     @Test(groups = "slow")
+    public void testEmptyPermissions() throws SecurityApiException {
+        securityApi.addRoleDefinition("sanity1", null, callContext);
+        validateUserRoles(securityApi.getRoleDefinition("sanity1", callContext), ImmutableList.<String>of());
+
+        securityApi.addRoleDefinition("sanity2", ImmutableList.<String>of(), callContext);
+        validateUserRoles(securityApi.getRoleDefinition("sanity2", callContext), ImmutableList.<String>of());
+    }
+
+    @Test(groups = "slow")
     public void testInvalidPermissions() {
-        testInvalidPermissionScenario(null);
-        testInvalidPermissionScenario(ImmutableList.<String>of());
         testInvalidPermissionScenario(ImmutableList.of("foo"));
         testInvalidPermissionScenario(ImmutableList.of("account:garbage"));
         testInvalidPermissionScenario(ImmutableList.of("tag:delete_tag_definition", "account:hsgdsgdjsgd"));
@@ -162,7 +168,6 @@ public class TestKillBillJdbcRealm extends UtilTestSuiteWithEmbeddedDB {
         securityApi.addRoleDefinition("newRestricted", ImmutableList.of("account:*", "invoice", "tag:delete_tag_definition"), callContext);
         securityApi.updateUserRoles(username, ImmutableList.of("newRestricted"), callContext);
 
-
         final Subject newSubject = securityManager.login(null, goodToken);
         newSubject.checkPermission(Permission.ACCOUNT_CAN_CHARGE.toString());
         newSubject.checkPermission(Permission.INVOICE_CAN_CREDIT.toString());
diff --git a/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java b/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java
index 262a5a7..1440ff6 100644
--- a/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java
+++ b/util/src/test/java/org/killbill/billing/util/security/shiro/realm/TestKillBillJndiLdapRealm.java
@@ -31,7 +31,7 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import org.killbill.billing.util.UtilTestSuiteNoDB;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 
 import com.google.common.collect.Sets;
 
diff --git a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
index 5317d3f..17ce189 100644
--- a/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
+++ b/util/src/test/java/org/killbill/billing/util/UtilTestSuiteWithEmbeddedDB.java
@@ -26,7 +26,7 @@ import org.killbill.billing.security.api.SecurityApi;
 import org.killbill.billing.util.audit.dao.AuditDao;
 import org.killbill.billing.util.broadcast.dao.BroadcastDao;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.config.SecurityConfig;
+import org.killbill.billing.util.config.definition.SecurityConfig;
 import org.killbill.billing.util.customfield.api.DefaultCustomFieldUserApi;
 import org.killbill.billing.util.customfield.dao.CustomFieldDao;
 import org.killbill.billing.util.dao.NonEntityDao;