killbill-uncached

invoice: add mechanism to shut down the invoicing system Invoice

12/22/2016 10:37:46 PM

Details

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 6c9b9b0..5b501ad 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
@@ -18,6 +18,8 @@
 
 package org.killbill.billing.beatrix.integration;
 
+import javax.annotation.Nullable;
+
 import org.killbill.billing.GuicyKillbillTestWithEmbeddedDBModule;
 import org.killbill.billing.account.glue.DefaultAccountModule;
 import org.killbill.billing.api.TestApiListener;
@@ -42,6 +44,7 @@ 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.definition.InvoiceConfig;
 import org.killbill.billing.util.config.definition.PaymentConfig;
 import org.killbill.billing.util.email.EmailModule;
 import org.killbill.billing.util.email.templates.TemplateModule;
@@ -68,8 +71,15 @@ public class BeatrixIntegrationModule extends KillBillModule {
     // Same name the osgi-payment-test plugin uses to register its service
     public static final String OSGI_PLUGIN_NAME = "osgi-payment-plugin";
 
+    private final InvoiceConfig invoiceConfig;
+
     public BeatrixIntegrationModule(final KillbillConfigSource configSource) {
+        this(configSource, null);
+    }
+
+    public BeatrixIntegrationModule(final KillbillConfigSource configSource, @Nullable final InvoiceConfig invoiceConfig) {
         super(configSource);
+        this.invoiceConfig = invoiceConfig;
     }
 
     @Override
@@ -113,7 +123,7 @@ public class BeatrixIntegrationModule extends KillBillModule {
         bind(TestApiListener.class).asEagerSingleton();
     }
 
-    private static final class DefaultInvoiceModuleWithSwitchRepairLogic extends DefaultInvoiceModule {
+    private final class DefaultInvoiceModuleWithSwitchRepairLogic extends DefaultInvoiceModule {
 
         private DefaultInvoiceModuleWithSwitchRepairLogic(final KillbillConfigSource configSource) {
             super(configSource);
@@ -122,6 +132,15 @@ public class BeatrixIntegrationModule extends KillBillModule {
         protected void installInvoiceGenerator() {
             bind(InvoiceGenerator.class).to(DefaultInvoiceGenerator.class).asEagerSingleton();
         }
+
+        @Override
+        protected void installConfig() {
+            if (invoiceConfig != null) {
+                super.installConfig(invoiceConfig);
+            } else {
+                super.installConfig();
+            }
+        }
     }
 
     private static final class PaymentPluginMockModule extends PaymentModule {
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 7697a94..5ed3690 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
@@ -48,6 +48,7 @@ import org.killbill.billing.beatrix.util.InvoiceChecker;
 import org.killbill.billing.beatrix.util.PaymentChecker;
 import org.killbill.billing.beatrix.util.RefundChecker;
 import org.killbill.billing.beatrix.util.SubscriptionChecker;
+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.Currency;
@@ -65,6 +66,7 @@ import org.killbill.billing.entitlement.api.EntitlementApi;
 import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
 import org.killbill.billing.entitlement.api.SubscriptionEventType;
+import org.killbill.billing.invoice.ParkedAccountsManager;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.invoice.api.DryRunType;
 import org.killbill.billing.invoice.api.Invoice;
@@ -111,11 +113,14 @@ 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.CacheControllerDispatcher;
+import org.killbill.billing.util.config.definition.InvoiceConfig;
 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.config.ConfigurationObjectFactory;
+import org.skife.config.TimeSpan;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -283,13 +288,20 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
     @Inject
     protected CacheControllerDispatcher controllerDispatcher;
 
+    @Inject
+    protected ParkedAccountsManager parkedAccountsManager;
+
+    protected ConfigurableInvoiceConfig invoiceConfig;
+
     protected void assertListenerStatus() {
         busHandler.assertListenerStatus();
     }
 
     @BeforeClass(groups = "slow")
     public void beforeClass() throws Exception {
-        final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource));
+        final InvoiceConfig defaultInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
+        invoiceConfig = new ConfigurableInvoiceConfig(defaultInvoiceConfig);
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource, invoiceConfig));
         g.injectMembers(this);
     }
 
@@ -902,4 +914,90 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB {
             return res;
         }
     }
+
+    static class ConfigurableInvoiceConfig implements InvoiceConfig {
+
+        private final InvoiceConfig defaultInvoiceConfig;
+
+        private boolean isInvoicingSystemEnabled;
+
+        public ConfigurableInvoiceConfig(final InvoiceConfig defaultInvoiceConfig) {
+            this.defaultInvoiceConfig = defaultInvoiceConfig;
+            isInvoicingSystemEnabled = defaultInvoiceConfig.isInvoicingSystemEnabled();
+        }
+
+        @Override
+        public int getNumberOfMonthsInFuture() {
+            return defaultInvoiceConfig.getNumberOfMonthsInFuture();
+        }
+
+        @Override
+        public int getNumberOfMonthsInFuture(final InternalTenantContext tenantContext) {
+            return defaultInvoiceConfig.getNumberOfMonthsInFuture();
+        }
+
+        @Override
+        public boolean isSanitySafetyBoundEnabled() {
+            return defaultInvoiceConfig.isSanitySafetyBoundEnabled();
+        }
+
+        @Override
+        public boolean isSanitySafetyBoundEnabled(final InternalTenantContext tenantContext) {
+            return defaultInvoiceConfig.isSanitySafetyBoundEnabled();
+        }
+
+        @Override
+        public int getMaxDailyNumberOfItemsSafetyBound() {
+            return defaultInvoiceConfig.getMaxDailyNumberOfItemsSafetyBound();
+        }
+
+        @Override
+        public int getMaxDailyNumberOfItemsSafetyBound(final InternalTenantContext tenantContext) {
+            return defaultInvoiceConfig.getMaxDailyNumberOfItemsSafetyBound();
+        }
+
+        @Override
+        public TimeSpan getDryRunNotificationSchedule() {
+            return defaultInvoiceConfig.getDryRunNotificationSchedule();
+        }
+
+        @Override
+        public TimeSpan getDryRunNotificationSchedule(final InternalTenantContext tenantContext) {
+            return defaultInvoiceConfig.getDryRunNotificationSchedule();
+        }
+
+        @Override
+        public int getMaxRawUsagePreviousPeriod() {
+            return defaultInvoiceConfig.getMaxRawUsagePreviousPeriod();
+        }
+
+        @Override
+        public int getMaxRawUsagePreviousPeriod(final InternalTenantContext tenantContext) {
+            return defaultInvoiceConfig.getMaxRawUsagePreviousPeriod();
+        }
+
+        @Override
+        public int getMaxGlobalLockRetries() {
+            return defaultInvoiceConfig.getMaxGlobalLockRetries();
+        }
+
+        @Override
+        public boolean isEmailNotificationsEnabled() {
+            return defaultInvoiceConfig.isEmailNotificationsEnabled();
+        }
+
+        @Override
+        public boolean isInvoicingSystemEnabled() {
+            return isInvoicingSystemEnabled;
+        }
+
+        @Override
+        public boolean isInvoicingSystemEnabled(final InternalTenantContext tenantContext) {
+            return isInvoicingSystemEnabled();
+        }
+
+        public void setInvoicingSystemEnabled(final boolean invoicingSystemEnabled) {
+            isInvoicingSystemEnabled = invoicingSystemEnabled;
+        }
+    }
 }
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceSystemDisabling.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceSystemDisabling.java
new file mode 100644
index 0000000..5b5f1ca
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoiceSystemDisabling.java
@@ -0,0 +1,124 @@
+/*
+ * 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.Collection;
+
+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.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.DryRunType;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+
+public class TestInvoiceSystemDisabling extends TestIntegrationBase {
+
+    @Override
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        // Tests will delete the database entries, yet ParkedAccountsManager is injected once
+        busHandler.pushExpectedEvent(NextEvent.TAG_DEFINITION);
+        parkedAccountsManager.retrieveOrCreateParkTagDefinition(clock);
+        assertListenerStatus();
+    }
+
+    @Test(groups = "slow")
+    public void testInvoiceSystemDisablingBasic() 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);
+
+        Assert.assertFalse(parkedAccountsManager.isParked(account.getId(), internalCallContext));
+
+        // Stop invoicing system
+        invoiceConfig.setInvoicingSystemEnabled(false);
+
+        final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(),
+                                                                                             "bundleKey",
+                                                                                             "Shotgun",
+                                                                                             ProductCategory.BASE,
+                                                                                             BillingPeriod.MONTHLY,
+                                                                                             NextEvent.CREATE,
+                                                                                             NextEvent.BLOCK,
+                                                                                             NextEvent.TAG);
+
+        Assert.assertTrue(parkedAccountsManager.isParked(account.getId(), internalCallContext));
+        Collection<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 0);
+
+        // Move to end of trial =>  2012, 5, 1
+        addDaysAndCheckForCompletion(30, NextEvent.PHASE);
+
+        Assert.assertTrue(parkedAccountsManager.isParked(account.getId(), internalCallContext));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 0);
+
+        // Dry-run generation
+        Invoice invoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), new TestDryRunArguments(DryRunType.TARGET_DATE), callContext);
+        assertListenerStatus();
+        final ImmutableList<ExpectedInvoiceItemCheck> expected = ImmutableList.<ExpectedInvoiceItemCheck>of(new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, BigDecimal.ZERO),
+                                                                                                            new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+        invoiceChecker.checkInvoiceNoAudits(invoice, callContext, expected);
+
+        // Still parked
+        Assert.assertTrue(parkedAccountsManager.isParked(account.getId(), internalCallContext));
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 0);
+
+        // Non dry-run generation
+        busHandler.pushExpectedEvents(NextEvent.TAG, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+        invoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), null, callContext);
+        assertListenerStatus();
+
+        // Now unparked
+        Assert.assertFalse(parkedAccountsManager.isParked(account.getId(), internalCallContext));
+        invoiceChecker.checkInvoice(invoice, callContext, expected);
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 1);
+        invoiceChecker.checkInvoice(account.getId(), 1, callContext, expected);
+
+        // Restart invoicing system and verify next notification
+        invoiceConfig.setInvoicingSystemEnabled(true);
+        addDaysAndCheckForCompletion(31, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+
+        invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), false, callContext);
+        assertEquals(invoices.size(), 2);
+        invoiceChecker.checkInvoice(account.getId(),
+                                    2,
+                                    callContext,
+                                    new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+    }
+}
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
index d519cc0..45de5f6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/config/MultiTenantInvoiceConfig.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/config/MultiTenantInvoiceConfig.java
@@ -119,6 +119,20 @@ public class MultiTenantInvoiceConfig extends MultiTenantConfigBase implements I
     }
 
     @Override
+    public boolean isInvoicingSystemEnabled() {
+        return staticConfig.isInvoicingSystemEnabled();
+    }
+
+    @Override
+    public boolean isInvoicingSystemEnabled(final InternalTenantContext tenantContext) {
+        final String result = getStringTenantConfig("isInvoicingSystemEnabled", tenantContext);
+        if (result != null) {
+            return Boolean.parseBoolean(result);
+        }
+        return isInvoicingSystemEnabled();
+    }
+
+    @Override
     protected Class<? extends KillbillConfig> getConfigClass() {
         return InvoiceConfig.class;
     }
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 8f56d81..d400a3f 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
@@ -65,7 +65,6 @@ import com.google.inject.name.Names;
 
 public class DefaultInvoiceModule extends KillBillModule implements InvoiceModule {
 
-
     InvoiceConfig staticInvoiceConfig;
 
     public DefaultInvoiceModule(final KillbillConfigSource configSource) {
@@ -94,7 +93,11 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
     }
 
     protected void installConfig() {
-        staticInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
+        installConfig(new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class));
+    }
+
+    protected void installConfig(final InvoiceConfig staticInvoiceConfig) {
+        this.staticInvoiceConfig = staticInvoiceConfig;
         bind(InvoiceConfig.class).annotatedWith(Names.named(STATIC_CONFIG)).toInstance(staticInvoiceConfig);
         bind(InvoiceConfig.class).to(MultiTenantInvoiceConfig.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 4ad8b4f..45fb51c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -223,6 +223,13 @@ public class InvoiceDispatcher {
                                   @Nullable final LocalDate targetDate,
                                   @Nullable final DryRunArguments dryRunArguments,
                                   final InternalCallContext context) throws InvoiceApiException {
+        // Note that all API calls (dryRun or not) will bypass this (see processAccount below)
+        if (!invoiceConfig.isInvoicingSystemEnabled(context)) {
+            log.warn("Invoicing system is off, parking accountId='{}'", accountId);
+            parkAccount(accountId, context);
+            return null;
+        }
+
         return processAccount(false, accountId, targetDate, dryRunArguments, context);
     }
 
@@ -312,16 +319,20 @@ public class InvoiceDispatcher {
         } catch (final InvoiceApiException e) {
             if (e.getCode() == ErrorCode.UNEXPECTED_ERROR.getCode() && !isDryRun) {
                 log.warn("Illegal invoicing state detected for accountId='{}', dryRunArguments='{}', parking account", accountId, dryRunArguments, e);
-                try {
-                    parkedAccountsManager.parkAccount(accountId, context);
-                } catch (final TagApiException ignored) {
-                    log.warn("Unable to park account", ignored);
-                }
+                parkAccount(accountId, context);
             }
             throw e;
         }
     }
 
+    private void parkAccount(final UUID accountId, final InternalCallContext context) {
+        try {
+            parkedAccountsManager.parkAccount(accountId, context);
+        } catch (final TagApiException ignored) {
+            log.warn("Unable to park account", ignored);
+        }
+    }
+
     private void filterInvoiceItemsForDryRun(final Iterable<UUID> filteredSubscriptionIdsForDryRun, final Invoice invoice) {
         if (!filteredSubscriptionIdsForDryRun.iterator().hasNext()) {
             return;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/ParkedAccountsManager.java b/invoice/src/main/java/org/killbill/billing/invoice/ParkedAccountsManager.java
index c03c993..11c6e21 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/ParkedAccountsManager.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/ParkedAccountsManager.java
@@ -66,6 +66,7 @@ public class ParkedAccountsManager {
         retrieveOrCreateParkTagDefinition(clock);
     }
 
+    // Idempotent
     public void parkAccount(final UUID accountId, final InternalCallContext internalCallContext) throws TagApiException {
         final CallContext callContext = createCallContext(internalCallContext);
         tagUserApi.addTag(accountId, ObjectType.ACCOUNT, tagDefinitionId, callContext);
@@ -96,7 +97,7 @@ public class ParkedAccountsManager {
     }
 
     @VisibleForTesting
-    void retrieveOrCreateParkTagDefinition(final Clock clock) throws TagDefinitionApiException {
+    public void retrieveOrCreateParkTagDefinition(final Clock clock) throws TagDefinitionApiException {
         final InternalCallContext callContext = new InternalCallContext(InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID,
                                                                         null,
                                                                         null,
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 e6eb41f..bdf3a39 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
@@ -110,68 +110,6 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
     protected void beforeClass() throws Exception {
         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 isSanitySafetyBoundEnabled() {
-                return true;
-            }
-
-            @Override
-            public boolean isSanitySafetyBoundEnabled(final InternalTenantContext tenantContext) {
-                return true;
-            }
-
-            @Override
-            public int getMaxDailyNumberOfItemsSafetyBound() {
-                return 10;
-            }
-
-            @Override
-            public int getMaxDailyNumberOfItemsSafetyBound(final InternalTenantContext tenantContext) {
-                return 10;
-            }
-
-            @Override
-            public boolean isEmailNotificationsEnabled() {
-                return false;
-            }
-
-            @Override
-            public TimeSpan getDryRunNotificationSchedule() {
-                return new TimeSpan("0s");
-            }
-
-            @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;
-            }
-        };
         this.account = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
                                                .firstNameLength(6)
                                                .email(UUID.randomUUID().toString().substring(1, 8))
diff --git a/util/src/main/java/org/killbill/billing/util/config/definition/InvoiceConfig.java b/util/src/main/java/org/killbill/billing/util/config/definition/InvoiceConfig.java
index b2ac2b0..8170bd6 100644
--- a/util/src/main/java/org/killbill/billing/util/config/definition/InvoiceConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/definition/InvoiceConfig.java
@@ -85,4 +85,14 @@ public interface InvoiceConfig extends KillbillConfig {
     @Default("false")
     @Description("Whether to send email notifications on invoice creation (for configured accounts)")
     boolean isEmailNotificationsEnabled();
+
+    @Config("org.killbill.invoice.enabled")
+    @Default("true")
+    @Description("Whether the invoicing system is enabled")
+    boolean isInvoicingSystemEnabled();
+
+    @Config("org.killbill.invoice.enabled")
+    @Default("true")
+    @Description("Whether the invoicing system is enabled")
+    boolean isInvoicingSystemEnabled(@Param("dummy") final InternalTenantContext tenantContext);
 }