killbill-aplcache
Changes
beatrix/src/test/resources/catalogSample.xml 585(+395 -190)
entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java 15(+9 -6)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java 3(+2 -1)
entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java 9(+8 -1)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java 2(+1 -1)
Details
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
index 0d87399..fd90b86 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
@@ -36,7 +36,7 @@ public interface EntitlementBillingApi {
public SortedSet<BillingEvent> getBillingEventsForAccount(UUID accountId);
public UUID getAccountIdFromSubscriptionId(UUID subscriptionId);
-
+
public void setChargedThroughDate(UUID subscriptionId, DateTime ctd, CallContext context);
public void setChargedThroughDateFromTransaction(Transmogrifier transactionalDao, UUID subscriptionId,
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
index 28084b2..9f284a1 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/EntitlementService.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.api;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.lifecycle.KillbillService;
@@ -31,4 +32,6 @@ public interface EntitlementService extends KillbillService {
public EntitlementBillingApi getBillingApi();
public EntitlementMigrationApi getMigrationApi();
+
+ public EntitlementRepairApi getRepairApi();
}
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java
index 63af70f..e5bebad 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/repair/RepairEntitlementEvent.java
@@ -23,7 +23,9 @@ import com.ning.billing.util.bus.BusEvent;
public interface RepairEntitlementEvent extends BusEvent {
- UUID getBundleId();
+ public UUID getAccountId();
+
+ public UUID getBundleId();
- DateTime getEffectiveDate();
+ public DateTime getEffectiveDate();
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
index ffa25e6..e62fede 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestBusHandler.java
@@ -27,6 +27,7 @@ import org.testng.Assert;
import com.google.common.base.Joiner;
import com.google.common.eventbus.Subscribe;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.invoice.api.InvoiceCreationEvent;
import com.ning.billing.payment.api.PaymentErrorEvent;
@@ -55,7 +56,15 @@ public class TestBusHandler {
RESUME,
PHASE,
INVOICE,
- PAYMENT
+ PAYMENT,
+ REPAIR_BUNDLE
+ }
+
+ @Subscribe
+ public void handleEntitlementEvents(RepairEntitlementEvent event) {
+ log.info(String.format("TestBusHandler Got RepairEntitlementEvent event %s", event.toString()));
+ assertEqualsNicely(NextEvent.REPAIR_BUNDLE);
+ notifyIfStackEmpty();
}
@Subscribe
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index e334966..61912fc 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -21,241 +21,34 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
-import java.io.IOException;
import java.math.BigDecimal;
-import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.dbi.MysqlTestingHelper;
import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.invoice.api.Invoice;
-import com.ning.billing.invoice.api.InvoiceItem;
-import com.ning.billing.invoice.model.InvoicingConfiguration;
-import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.UserType;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
+
import org.joda.time.Interval;
-import org.skife.jdbi.v2.Handle;
-import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.TransactionCallback;
-import org.skife.jdbi.v2.TransactionStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.AfterSuite;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
-import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
-import com.ning.billing.account.api.AccountData;
-import com.ning.billing.account.api.AccountService;
-import com.ning.billing.account.api.AccountUserApi;
import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
-import com.ning.billing.beatrix.lifecycle.Lifecycle;
import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.PriceListSet;
import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.EntitlementService;
-import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionData;
-import com.ning.billing.invoice.api.InvoiceService;
-import com.ning.billing.invoice.api.InvoiceUserApi;
-
-import com.ning.billing.util.clock.ClockMock;
-import com.ning.billing.util.bus.BusService;
@Test(groups = "slow")
@Guice(modules = {MockModule.class})
-public class TestIntegration {
- private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
- private static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
-
- private static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal THIRTY = new BigDecimal("30.0000").setScale(NUMBER_OF_DECIMALS);
- private static final BigDecimal THIRTY_ONE = new BigDecimal("31.0000").setScale(NUMBER_OF_DECIMALS);
-
- private static final Logger log = LoggerFactory.getLogger(TestIntegration.class);
- private static long AT_LEAST_ONE_MONTH_MS = 31L * 24L * 3600L * 1000L;
-
- private static final long DELAY = 5000;
-
- @Inject IDBI dbi;
-
- @Inject
- private ClockMock clock;
- private CallContext context;
-
- @Inject
- private Lifecycle lifecycle;
-
- @Inject
- private BusService busService;
-
- @Inject
- private EntitlementService entitlementService;
-
- @Inject
- private InvoiceService invoiceService;
-
- @Inject
- private AccountService accountService;
-
- @Inject
- private MysqlTestingHelper helper;
-
- private EntitlementUserApi entitlementUserApi;
-
- private InvoiceUserApi invoiceUserApi;
-
- private AccountUserApi accountUserApi;
-
- private TestBusHandler busHandler;
-
- private void setupMySQL() throws IOException
- {
- final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
- final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
- final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
- final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
- final String utilDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
-
- helper.startMysql();
-
- helper.initDb(accountDdl);
- helper.initDb(entitlementDdl);
- helper.initDb(invoiceDdl);
- helper.initDb(paymentDdl);
- helper.initDb(utilDdl);
- }
-
- @BeforeSuite(groups = "slow")
- public void setup() throws Exception{
-
- setupMySQL();
-
- context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
-
- /**
- * Initialize lifecyle for subset of services
- */
- busHandler = new TestBusHandler();
- lifecycle.fireStartupSequencePriorEventRegistration();
- busService.getBus().register(busHandler);
- lifecycle.fireStartupSequencePostEventRegistration();
-
-
-
- /**
- * Retrieve APIs
- */
- entitlementUserApi = entitlementService.getUserApi();
- invoiceUserApi = invoiceService.getUserApi();
- accountUserApi = accountService.getAccountUserApi();
- }
-
- @AfterSuite(groups = "slow")
- public void tearDown() throws Exception {
- lifecycle.fireShutdownSequencePriorEventUnRegistration();
- busService.getBus().unregister(busHandler);
- lifecycle.fireShutdownSequencePostEventUnRegistration();
- helper.stopMysql();
- }
-
-
- @BeforeMethod(groups = "slow")
- public void setupTest() {
-
- log.warn("\n");
- log.warn("RESET TEST FRAMEWORK\n\n");
- busHandler.reset();
- clock.resetDeltaFromReality();
- cleanupData();
- }
-
- @AfterMethod(groups = "slow")
- public void cleanupTest() {
- log.warn("DONE WITH TEST\n");
- }
-
- private void cleanupData() {
- dbi.inTransaction(new TransactionCallback<Void>() {
- @Override
- public Void inTransaction(Handle h, TransactionStatus status)
- throws Exception {
- h.execute("truncate table accounts");
- h.execute("truncate table entitlement_events");
- h.execute("truncate table subscriptions");
- h.execute("truncate table bundles");
- h.execute("truncate table notifications");
- h.execute("truncate table claimed_notifications");
- h.execute("truncate table invoices");
- h.execute("truncate table fixed_invoice_items");
- h.execute("truncate table recurring_invoice_items");
- h.execute("truncate table tag_definitions");
- h.execute("truncate table tags");
- h.execute("truncate table custom_fields");
- h.execute("truncate table invoice_payments");
- h.execute("truncate table payment_attempts");
- h.execute("truncate table payments");
- return null;
- }
- });
- }
-
- private void verifyTestResult(UUID accountId, UUID subscriptionId,
- DateTime startDate, DateTime endDate,
- BigDecimal amount, DateTime chargeThroughDate,
- int totalInvoiceItemCount) {
- SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
-
- List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
- List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
- for (Invoice invoice : invoices) {
- invoiceItems.addAll(invoice.getInvoiceItems());
- }
- assertEquals(invoiceItems.size(), totalInvoiceItemCount);
-
- boolean wasFound = false;
-
- for (InvoiceItem item : invoiceItems) {
- if (item.getStartDate().compareTo(startDate) == 0) {
- if (item.getEndDate().compareTo(endDate) == 0) {
- if (item.getAmount().compareTo(amount) == 0) {
- wasFound = true;
- break;
- }
- }
- }
- }
-
- if (!wasFound) {
- fail();
- }
-
- DateTime ctd = subscription.getChargedThroughDate();
- assertNotNull(ctd);
- log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
- assertTrue(clock.getUTCNow().isBefore(ctd));
- assertTrue(ctd.compareTo(chargeThroughDate) == 0);
- }
+public class TestIntegration extends TestIntegrationBase {
@Test(groups = "slow", enabled = true)
public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
@@ -642,88 +435,4 @@ public class TestIntegration {
assertNotNull(invoices);
assertTrue(invoices.size() == 3);
}
-
- protected AccountData getAccountData(final int billingDay) {
-
- final String someRandomKey = RandomStringUtils.randomAlphanumeric(10);
- return new AccountData() {
- @Override
- public String getName() {
- return "firstName lastName";
- }
- @Override
- public int getFirstNameLength() {
- return "firstName".length();
- }
- @Override
- public String getEmail() {
- return someRandomKey + "@laposte.fr";
- }
- @Override
- public String getPhone() {
- return "4152876341";
- }
- @Override
- public String getExternalKey() {
- return someRandomKey;
- }
- @Override
- public int getBillCycleDay() {
- return billingDay;
- }
- @Override
- public Currency getCurrency() {
- return Currency.USD;
- }
- @Override
- public String getPaymentProviderName() {
- return MockModule.PLUGIN_NAME;
- }
-
- @Override
- public DateTimeZone getTimeZone() {
- return null;
- }
-
- @Override
- public String getLocale() {
- return null;
- }
-
- @Override
- public String getAddress1() {
- return null;
- }
-
- @Override
- public String getAddress2() {
- return null;
- }
-
- @Override
- public String getCompanyName() {
- return null;
- }
-
- @Override
- public String getCity() {
- return null;
- }
-
- @Override
- public String getStateOrProvince() {
- return null;
- }
-
- @Override
- public String getPostalCode() {
- return null;
- }
-
- @Override
- public String getCountry() {
- return null;
- }
- };
- }
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
new file mode 100644
index 0000000..ca2ceae
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegrationBase.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.RandomStringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountService;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceService;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.model.InvoicingConfiguration;
+import com.ning.billing.util.bus.BusService;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.DefaultCallContextFactory;
+import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestIntegrationBase {
+
+ protected static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
+ protected static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
+
+ protected static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal THIRTY = new BigDecimal("30.0000").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal THIRTY_ONE = new BigDecimal("31.0000").setScale(NUMBER_OF_DECIMALS);
+
+ protected static final Logger log = LoggerFactory.getLogger(TestIntegration.class);
+ protected static long AT_LEAST_ONE_MONTH_MS = 31L * 24L * 3600L * 1000L;
+
+ protected static final long DELAY = 5000;
+
+ @Inject
+ protected IDBI dbi;
+
+ @Inject
+ protected ClockMock clock;
+
+ protected CallContext context;
+
+ @Inject
+ protected Lifecycle lifecycle;
+
+ @Inject
+ protected BusService busService;
+
+ @Inject
+ protected EntitlementService entitlementService;
+
+ @Inject
+ protected InvoiceService invoiceService;
+
+ @Inject
+ protected AccountService accountService;
+
+ @Inject
+ protected MysqlTestingHelper helper;
+
+ protected EntitlementUserApi entitlementUserApi;
+
+ protected EntitlementRepairApi repairApi;
+
+ protected InvoiceUserApi invoiceUserApi;
+
+ protected AccountUserApi accountUserApi;
+
+ protected TestBusHandler busHandler;
+
+ protected void setupMySQL() throws IOException
+ {
+ final String accountDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+ final String entitlementDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+ final String invoiceDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+ final String paymentDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+ final String utilDdl = IOUtils.toString(TestIntegration.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+ helper.startMysql();
+
+ helper.initDb(accountDdl);
+ helper.initDb(entitlementDdl);
+ helper.initDb(invoiceDdl);
+ helper.initDb(paymentDdl);
+ helper.initDb(utilDdl);
+ }
+
+ @BeforeClass(groups = "slow")
+ public void setup() throws Exception{
+
+ setupMySQL();
+
+ context = new DefaultCallContextFactory(clock).createCallContext("Integration Test", CallOrigin.TEST, UserType.TEST);
+
+ /**
+ * Initialize lifecyle for subset of services
+ */
+ busHandler = new TestBusHandler();
+ lifecycle.fireStartupSequencePriorEventRegistration();
+ busService.getBus().register(busHandler);
+ lifecycle.fireStartupSequencePostEventRegistration();
+
+
+
+ /**
+ * Retrieve APIs
+ */
+ entitlementUserApi = entitlementService.getUserApi();
+ repairApi = entitlementService.getRepairApi();
+ invoiceUserApi = invoiceService.getUserApi();
+ accountUserApi = accountService.getAccountUserApi();
+ }
+
+ @AfterClass(groups = "slow")
+ public void tearDown() throws Exception {
+ lifecycle.fireShutdownSequencePriorEventUnRegistration();
+ busService.getBus().unregister(busHandler);
+ lifecycle.fireShutdownSequencePostEventUnRegistration();
+ helper.stopMysql();
+ }
+
+
+ @BeforeMethod(groups = "slow")
+ public void setupTest() {
+
+ log.warn("\n");
+ log.warn("RESET TEST FRAMEWORK\n\n");
+ busHandler.reset();
+ clock.resetDeltaFromReality();
+ cleanupData();
+ }
+
+ @AfterMethod(groups = "slow")
+ public void cleanupTest() {
+ log.warn("DONE WITH TEST\n");
+ }
+
+ protected void cleanupData() {
+ dbi.inTransaction(new TransactionCallback<Void>() {
+ @Override
+ public Void inTransaction(Handle h, TransactionStatus status)
+ throws Exception {
+ h.execute("truncate table accounts");
+ h.execute("truncate table entitlement_events");
+ h.execute("truncate table subscriptions");
+ h.execute("truncate table bundles");
+ h.execute("truncate table notifications");
+ h.execute("truncate table claimed_notifications");
+ h.execute("truncate table invoices");
+ h.execute("truncate table fixed_invoice_items");
+ h.execute("truncate table recurring_invoice_items");
+ h.execute("truncate table tag_definitions");
+ h.execute("truncate table tags");
+ h.execute("truncate table custom_fields");
+ h.execute("truncate table invoice_payments");
+ h.execute("truncate table payment_attempts");
+ h.execute("truncate table payments");
+ return null;
+ }
+ });
+ }
+
+ protected void verifyTestResult(UUID accountId, UUID subscriptionId,
+ DateTime startDate, DateTime endDate,
+ BigDecimal amount, DateTime chargeThroughDate,
+ int totalInvoiceItemCount) {
+ SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
+
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId);
+ List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
+ for (Invoice invoice : invoices) {
+ invoiceItems.addAll(invoice.getInvoiceItems());
+ }
+ assertEquals(invoiceItems.size(), totalInvoiceItemCount);
+
+ boolean wasFound = false;
+
+ for (InvoiceItem item : invoiceItems) {
+ if (item.getStartDate().compareTo(startDate) == 0) {
+ if (item.getEndDate().compareTo(endDate) == 0) {
+ if (item.getAmount().compareTo(amount) == 0) {
+ wasFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!wasFound) {
+ fail();
+ }
+
+ DateTime ctd = subscription.getChargedThroughDate();
+ assertNotNull(ctd);
+ log.info("Checking CTD: " + ctd.toString() + "; clock is " + clock.getUTCNow().toString());
+ assertTrue(clock.getUTCNow().isBefore(ctd));
+ assertTrue(ctd.compareTo(chargeThroughDate) == 0);
+ }
+
+
+ protected AccountData getAccountData(final int billingDay) {
+
+ final String someRandomKey = RandomStringUtils.randomAlphanumeric(10);
+ return new AccountData() {
+ @Override
+ public String getName() {
+ return "firstName lastName";
+ }
+ @Override
+ public int getFirstNameLength() {
+ return "firstName".length();
+ }
+ @Override
+ public String getEmail() {
+ return someRandomKey + "@laposte.fr";
+ }
+ @Override
+ public String getPhone() {
+ return "4152876341";
+ }
+ @Override
+ public String getExternalKey() {
+ return someRandomKey;
+ }
+ @Override
+ public int getBillCycleDay() {
+ return billingDay;
+ }
+ @Override
+ public Currency getCurrency() {
+ return Currency.USD;
+ }
+ @Override
+ public String getPaymentProviderName() {
+ return MockModule.PLUGIN_NAME;
+ }
+
+ @Override
+ public DateTimeZone getTimeZone() {
+ return null;
+ }
+
+ @Override
+ public String getLocale() {
+ return null;
+ }
+
+ @Override
+ public String getAddress1() {
+ return null;
+ }
+
+ @Override
+ public String getAddress2() {
+ return null;
+ }
+
+ @Override
+ public String getCompanyName() {
+ return null;
+ }
+
+ @Override
+ public String getCity() {
+ return null;
+ }
+
+ @Override
+ public String getStateOrProvince() {
+ return null;
+ }
+
+ @Override
+ public String getPostalCode() {
+ return null;
+ }
+
+ @Override
+ public String getCountry() {
+ return null;
+ }
+ };
+ }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
new file mode 100644
index 0000000..f58482a
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestRepairIntegration.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.ning.billing.beatrix.integration;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.Interval;
+import org.testng.Assert;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.beatrix.integration.TestBusHandler.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.SubscriptionTransitionType;
+import com.ning.billing.entitlement.api.repair.BundleRepair;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.DeletedEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.ExistingEvent;
+import com.ning.billing.entitlement.api.repair.SubscriptionRepair.NewEvent;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionEvents;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+
+@Test(groups = "slow")
+@Guice(modules = {MockModule.class})
+public class TestRepairIntegration extends TestIntegrationBase {
+
+ @Test(groups={"slow"}, enabled=true)
+ public void testRepairChangeBPWithAddonIncluded() throws Exception {
+
+ DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+ clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+ Account account = accountUserApi.createAccount(getAccountData(25), null, null, context);
+ assertNotNull(account);
+
+ SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever", context);
+
+ String productName = "Shotgun";
+ BillingPeriod term = BillingPeriod.MONTHLY;
+ String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+ busHandler.pushExpectedEvent(NextEvent.CREATE);
+ busHandler.pushExpectedEvent(NextEvent.INVOICE);
+ SubscriptionData baseSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null, context);
+ assertNotNull(baseSubscription);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ // MOVE CLOCK A LITTLE BIT-- STILL IN TRIAL
+ Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+
+ SubscriptionData aoSubscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Telescopic-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+
+ SubscriptionData aoSubscription2 = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+ new PlanPhaseSpecifier("Laser-Scope", ProductCategory.ADD_ON, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null), null, context);
+
+ // MOVE CLOCK A LITTLE BIT MORE -- STILL IN TRIAL
+ it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(3));
+ clock.addDeltaFromReality(it.toDurationMillis());
+
+
+ BundleRepair bundleRepair = repairApi.getBundleRepair(bundle.getId());
+ sortEventsOnBundle(bundleRepair);
+
+ // Quick check
+ SubscriptionRepair bpRepair = getSubscriptionRepair(baseSubscription.getId(), bundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair = getSubscriptionRepair(aoSubscription.getId(), bundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ SubscriptionRepair aoRepair2 = getSubscriptionRepair(aoSubscription2.getId(), bundleRepair);
+ assertEquals(aoRepair2.getExistingEvents().size(), 2);
+
+ DateTime bpChangeDate = clock.getUTCNow().minusDays(1);
+
+ List<DeletedEvent> des = new LinkedList<SubscriptionRepair.DeletedEvent>();
+ des.add(createDeletedEvent(bpRepair.getExistingEvents().get(1).getEventId()));
+
+ PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Assault-Rifle", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, PhaseType.TRIAL);
+ NewEvent ne = createNewEvent(SubscriptionTransitionType.CHANGE, bpChangeDate, spec);
+
+ bpRepair = createSubscriptionReapir(baseSubscription.getId(), des, Collections.singletonList(ne));
+
+ bundleRepair = createBundleRepair(bundle.getId(), bundleRepair.getViewId(), Collections.singletonList(bpRepair));
+
+ // TIME TO REPAIR
+ busHandler.pushExpectedEvent(NextEvent.REPAIR_BUNDLE);
+ BundleRepair realRunBundleRepair = repairApi.repairBundle(bundleRepair, false, context);
+ assertTrue(busHandler.isCompleted(DELAY));
+
+ aoRepair = getSubscriptionRepair(aoSubscription.getId(), realRunBundleRepair);
+ assertEquals(aoRepair.getExistingEvents().size(), 2);
+
+ bpRepair = getSubscriptionRepair(baseSubscription.getId(), realRunBundleRepair);
+ assertEquals(bpRepair.getExistingEvents().size(), 3);
+
+ // Check expected for AO
+ List<ExistingEvent> expectedAO = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription.getStartDate()));
+ expectedAO.add(createExistingEventForAssertion(SubscriptionTransitionType.CANCEL, "Telescopic-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, bpChangeDate));
+ int index = 0;
+ for (ExistingEvent e : expectedAO) {
+ validateExistingEventForAssertion(e, aoRepair.getExistingEvents().get(index++));
+ }
+
+ List<ExistingEvent> expectedAO2 = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Laser-Scope", PhaseType.DISCOUNT,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate()));
+ expectedAO2.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Laser-Scope", PhaseType.EVERGREEN,
+ ProductCategory.ADD_ON, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, aoSubscription2.getStartDate().plusMonths(1)));
+ index = 0;
+ for (ExistingEvent e : expectedAO2) {
+ validateExistingEventForAssertion(e, aoRepair2.getExistingEvents().get(index++));
+ }
+
+ // Check expected for BP
+ List<ExistingEvent> expectedBP = new LinkedList<SubscriptionRepair.ExistingEvent>();
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CREATE, "Shotgun", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, baseSubscription.getStartDate()));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.CHANGE, "Assault-Rifle", PhaseType.TRIAL,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.NO_BILLING_PERIOD, bpChangeDate));
+ expectedBP.add(createExistingEventForAssertion(SubscriptionTransitionType.PHASE, "Assault-Rifle", PhaseType.EVERGREEN,
+ ProductCategory.BASE, PriceListSet.DEFAULT_PRICELIST_NAME, BillingPeriod.MONTHLY, baseSubscription.getStartDate().plusDays(30)));
+ index = 0;
+ for (ExistingEvent e : expectedBP) {
+ validateExistingEventForAssertion(e, bpRepair.getExistingEvents().get(index++));
+ }
+
+ SubscriptionData newAoSubscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(aoSubscription.getId());
+ assertEquals(newAoSubscription.getState(), SubscriptionState.CANCELLED);
+ assertEquals(newAoSubscription.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+ SubscriptionData newAoSubscription2 = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(aoSubscription2.getId());
+ assertEquals(newAoSubscription2.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newAoSubscription2.getAllTransitions().size(), 2);
+ assertEquals(newAoSubscription2.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+
+ SubscriptionData newBaseSubscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(baseSubscription.getId());
+ assertEquals(newBaseSubscription.getState(), SubscriptionState.ACTIVE);
+ assertEquals(newBaseSubscription.getAllTransitions().size(), 3);
+ assertEquals(newBaseSubscription.getActiveVersion(), SubscriptionEvents.INITIAL_VERSION + 1);
+
+
+
+ }
+
+ protected SubscriptionRepair createSubscriptionReapir(final UUID id, final List<DeletedEvent> deletedEvents, final List<NewEvent> newEvents) {
+ return new SubscriptionRepair() {
+ @Override
+ public UUID getId() {
+ return id;
+ }
+ @Override
+ public List<NewEvent> getNewEvents() {
+ return newEvents;
+ }
+ @Override
+ public List<ExistingEvent> getExistingEvents() {
+ return null;
+ }
+ @Override
+ public List<DeletedEvent> getDeletedEvents() {
+ return deletedEvents;
+ }
+ };
+ }
+
+
+ protected BundleRepair createBundleRepair(final UUID bundleId, final String viewId, final List<SubscriptionRepair> subscriptionRepair) {
+ return new BundleRepair() {
+ @Override
+ public String getViewId() {
+ return viewId;
+ }
+ @Override
+ public List<SubscriptionRepair> getSubscriptions() {
+ return subscriptionRepair;
+ }
+ @Override
+ public UUID getBundleId() {
+ return bundleId;
+ }
+ };
+ }
+
+ protected ExistingEvent createExistingEventForAssertion(final SubscriptionTransitionType type,
+ final String productName, final PhaseType phaseType, final ProductCategory category, final String priceListName, final BillingPeriod billingPeriod,
+ final DateTime effectiveDateTime) {
+
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, category, billingPeriod, priceListName, phaseType);
+ ExistingEvent ev = new ExistingEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return null;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ @Override
+ public UUID getEventId() {
+ return null;
+ }
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDateTime;
+ }
+ };
+ return ev;
+ }
+
+ protected SubscriptionRepair getSubscriptionRepair(final UUID id, final BundleRepair bundleRepair) {
+ for (SubscriptionRepair cur : bundleRepair.getSubscriptions()) {
+ if (cur.getId().equals(id)) {
+ return cur;
+ }
+ }
+ Assert.fail("Failed to find SubscriptionReapir " + id);
+ return null;
+ }
+ protected void validateExistingEventForAssertion(final ExistingEvent expected, final ExistingEvent input) {
+
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductName(), expected.getPlanPhaseSpecifier().getProductName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType()));
+ assertEquals(input.getPlanPhaseSpecifier().getPhaseType(), expected.getPlanPhaseSpecifier().getPhaseType());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory()));
+ assertEquals(input.getPlanPhaseSpecifier().getProductCategory(), expected.getPlanPhaseSpecifier().getProductCategory());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName()));
+ assertEquals(input.getPlanPhaseSpecifier().getPriceListName(), expected.getPlanPhaseSpecifier().getPriceListName());
+ log.info(String.format("Got %s -> Expected %s", input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod()));
+ assertEquals(input.getPlanPhaseSpecifier().getBillingPeriod(), expected.getPlanPhaseSpecifier().getBillingPeriod());
+ log.info(String.format("Got %s -> Expected %s", input.getEffectiveDate(), expected.getEffectiveDate()));
+ assertEquals(input.getEffectiveDate(), expected.getEffectiveDate());
+ }
+
+ protected DeletedEvent createDeletedEvent(final UUID eventId) {
+ return new DeletedEvent() {
+ @Override
+ public UUID getEventId() {
+ return eventId;
+ }
+ };
+ }
+
+ protected NewEvent createNewEvent(final SubscriptionTransitionType type, final DateTime requestedDate, final PlanPhaseSpecifier spec) {
+
+ return new NewEvent() {
+ @Override
+ public SubscriptionTransitionType getSubscriptionTransitionType() {
+ return type;
+ }
+ @Override
+ public DateTime getRequestedDate() {
+ return requestedDate;
+ }
+ @Override
+ public PlanPhaseSpecifier getPlanPhaseSpecifier() {
+ return spec;
+ }
+ };
+ }
+
+ protected void sortEventsOnBundle(final BundleRepair bundle) {
+ if (bundle.getSubscriptions() == null) {
+ return;
+ }
+ for (SubscriptionRepair cur : bundle.getSubscriptions()) {
+ if (cur.getExistingEvents() != null) {
+ sortExistingEvent(cur.getExistingEvents());
+ }
+ if (cur.getNewEvents() != null) {
+ sortNewEvent(cur.getNewEvents());
+ }
+ }
+ }
+
+ protected void sortExistingEvent(final List<ExistingEvent> events) {
+ Collections.sort(events, new Comparator<ExistingEvent>() {
+ @Override
+ public int compare(ExistingEvent arg0, ExistingEvent arg1) {
+ return arg0.getEffectiveDate().compareTo(arg1.getEffectiveDate());
+ }
+ });
+ }
+ protected void sortNewEvent(final List<NewEvent> events) {
+ Collections.sort(events, new Comparator<NewEvent>() {
+ @Override
+ public int compare(NewEvent arg0, NewEvent arg1) {
+ return arg0.getRequestedDate().compareTo(arg1.getRequestedDate());
+ }
+ });
+ }
+
+
+}
beatrix/src/test/resources/catalogSample.xml 585(+395 -190)
diff --git a/beatrix/src/test/resources/catalogSample.xml b/beatrix/src/test/resources/catalogSample.xml
index 5b7aeaf..8c13707 100644
--- a/beatrix/src/test/resources/catalogSample.xml
+++ b/beatrix/src/test/resources/catalogSample.xml
@@ -1,43 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!--
- ~ Copyright 2010-2011 Ning, Inc.
- ~
- ~ Ning licenses this file to you under the Apache License, version 2.0
- ~ (the "License"); you may not use this file except in compliance with the
- ~ License. You may obtain a copy of the License at:
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- ~ License for the specific language governing permissions and limitations
- ~ under the License.
- -->
+<!-- ~ Copyright 2010-2011 Ning, Inc. ~ ~ Ning licenses this file to you
+ under the Apache License, version 2.0 ~ (the "License"); you may not use
+ this file except in compliance with the ~ License. You may obtain a copy
+ of the License at: ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless
+ required by applicable law or agreed to in writing, software ~ distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT ~ WARRANTIES
+ OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for
+ the specific language governing permissions and limitations ~ under the License. -->
-<!--
-Use cases covered so far:
- Tiered Product (Pistol/Shotgun/Assault-Rifle)
- Multiple changeEvent plan policies
- Multiple PlanAlignment (see below, trial add-on alignments and rescue discount package)
- Product transition rules
- Add on (Scopes, Holster)
- Multi-pack addon (Extra-Ammo)
- Addon Trial aligned to base plan (holster-monthly-regular)
- Addon Trial aligned to creation (holster-monthly-special)
- Rescue discount package (assault-rifle-annual-rescue)
- Plan phase with a recurring and a one off (refurbish-maintenance)
- Plan with more than 2 phase (gunclub discount plans)
-
-Use Cases to do:
- Tiered Add On
- Riskfree period
-
-
-
- -->
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle)
+ Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial
+ add-on alignments and rescue discount package) Product transition rules Add
+ on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to
+ base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special)
+ Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring
+ and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub
+ discount plans) Use Cases to do: Tiered Add On Riskfree period -->
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="CatalogSchema.xsd">
+ xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
<catalogName>Firearms</catalogName>
@@ -47,24 +27,24 @@ Use Cases to do:
<currency>EUR</currency>
<currency>GBP</currency>
</currencies>
-
+
<products>
+ <product name="Blowdart">
+ <category>BASE</category>
+ </product>
<product name="Pistol">
<category>BASE</category>
+ </product>
+ <product name="Shotgun">
+ <category>BASE</category>
<available>
<addonProduct>Telescopic-Scope</addonProduct>
<addonProduct>Laser-Scope</addonProduct>
</available>
</product>
- <product name="Blowdart">
- <category>BASE</category>
- </product>
- <product name="Shotgun">
- <category>BASE</category>
- </product>
<product name="Assault-Rifle">
<category>BASE</category>
- <included>
+ <included>
<addonProduct>Telescopic-Scope</addonProduct>
</included>
<available>
@@ -87,60 +67,42 @@ Use Cases to do:
<category>ADD_ON</category>
</product>
</products>
-
+
<rules>
<changePolicy>
- <changePolicyCase>
+ <changePolicyCase>
<phaseType>TRIAL</phaseType>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <toProduct>Pistol</toProduct>
- <policy>END_OF_TERM</policy>
+ <changePolicyCase>
+ <toProduct>Assault-Rifle</toProduct>
+ <policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <toPriceList>rescue</toPriceList>
- <policy>END_OF_TERM</policy>
- </changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromProduct>Pistol</fromProduct>
<toProduct>Shotgun</toProduct>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
- <fromProduct>Assault-Rifle</fromProduct>
- <toProduct>Shotgun</toProduct>
- <policy>END_OF_TERM</policy>
- </changePolicyCase>
- <changePolicyCase>
- <fromBillingPeriod>MONTHLY</fromBillingPeriod>
- <toProduct>Assault-Rifle</toProduct>
- <toBillingPeriod>MONTHLY</toBillingPeriod>
+ <changePolicyCase>
+ <toPriceList>rescue</toPriceList>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
- <toProduct>Assault-Rifle</toProduct>
- <policy>IMMEDIATE</policy>
- </changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>MONTHLY</fromBillingPeriod>
<toBillingPeriod>ANNUAL</toBillingPeriod>
<policy>IMMEDIATE</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<fromBillingPeriod>ANNUAL</fromBillingPeriod>
<toBillingPeriod>MONTHLY</toBillingPeriod>
<policy>END_OF_TERM</policy>
</changePolicyCase>
- <changePolicyCase>
+ <changePolicyCase>
<policy>END_OF_TERM</policy>
</changePolicyCase>
</changePolicy>
<changeAlignment>
<changeAlignmentCase>
- <alignment>START_OF_SUBSCRIPTION</alignment>
- </changeAlignmentCase>
- <changeAlignmentCase>
<toPriceList>rescue</toPriceList>
<alignment>CHANGE_OF_PLAN</alignment>
</changeAlignmentCase>
@@ -149,18 +111,25 @@ Use Cases to do:
<toPriceList>rescue</toPriceList>
<alignment>CHANGE_OF_PRICELIST</alignment>
</changeAlignmentCase>
+ <changeAlignmentCase>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </changeAlignmentCase>
</changeAlignment>
<cancelPolicy>
<cancelPolicyCase>
- <policy>END_OF_TERM</policy>
- </cancelPolicyCase>
- <cancelPolicyCase>
<phaseType>TRIAL</phaseType>
<policy>IMMEDIATE</policy>
</cancelPolicyCase>
+ <cancelPolicyCase>
+ <policy>END_OF_TERM</policy>
+ </cancelPolicyCase>
</cancelPolicy>
<createAlignment>
<createAlignmentCase>
+ <product>Laser-Scope</product>
+ <alignment>START_OF_SUBSCRIPTION</alignment>
+ </createAlignmentCase>
+ <createAlignmentCase>
<alignment>START_OF_BUNDLE</alignment>
</createAlignmentCase>
</createAlignment>
@@ -186,21 +155,7 @@ Use Cases to do:
</rules>
<plans>
- <plan name="pistol-monthly-no-trial">
- <product>Pistol</product>
- <finalPhase type="EVERGREEN">
- <duration>
- <unit>UNLIMITED</unit>
- </duration>
- <billingPeriod>MONTHLY</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>
- </finalPhase>
- </plan>
- <plan name="blowdart-monthly">
+ <plan name="blowdart-monthly">
<product>Blowdart</product>
<initialPhases>
<phase type="TRIAL">
@@ -219,9 +174,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>9.95</value></price>
- <price><currency>EUR</currency><value>9.95</value></price>
- <price><currency>GBP</currency><value>9.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>9.95</value>
+ </price>
</recurringPrice>
</phase>
</initialPhases>
@@ -231,25 +195,34 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>29.95</value></price>
- <price><currency>EUR</currency><value>29.95</value></price>
- <price><currency>GBP</currency><value>29.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>29.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
+
<plan name="pistol-monthly">
<product>Pistol</product>
<initialPhases>
- <phase type="TRIAL">
- <duration>
- <unit>DAYS</unit>
- <number>30</number>
- </duration>
- <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
- <fixedPrice>
- </fixedPrice>
- <!-- no price implies $0 -->
- </phase>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice> <!-- empty price implies $0 -->
+ </fixedPrice>
+ </phase>
</initialPhases>
<finalPhase type="EVERGREEN">
<duration>
@@ -257,9 +230,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</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>
+ <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>
</finalPhase>
</plan>
@@ -271,10 +253,9 @@ Use Cases to do:
<unit>DAYS</unit>
<number>30</number>
</duration>
- <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
- <fixedPrice>
- </fixedPrice>
- <!-- no price implies $0 -->
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice></fixedPrice>
+ <!-- no price implies $0 -->
</phase>
</initialPhases>
<finalPhase type="EVERGREEN">
@@ -284,25 +265,33 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>249.95</value></price>
- <price><currency>EUR</currency><value>149.95</value></price>
- <price><currency>GBP</currency><value>169.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>249.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>149.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>169.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
<plan name="assault-rifle-monthly">
<product>Assault-Rifle</product>
<initialPhases>
- <phase type="TRIAL">
- <duration>
- <unit>DAYS</unit>
- <number>30</number>
- </duration>
- <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
- <fixedPrice>
- </fixedPrice>
- <!-- no price implies $0 -->
- </phase>
+ <phase type="TRIAL">
+ <duration>
+ <unit>DAYS</unit>
+ <number>30</number>
+ </duration>
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <fixedPrice>
+ </fixedPrice>
+ </phase>
</initialPhases>
<finalPhase type="EVERGREEN">
<duration>
@@ -310,9 +299,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>599.95</value></price>
- <price><currency>EUR</currency><value>349.95</value></price>
- <price><currency>GBP</currency><value>399.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>349.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>399.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -335,9 +333,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>199.95</value></price>
- <price><currency>EUR</currency><value>199.95</value></price>
- <price><currency>GBP</currency><value>199.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -360,9 +367,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>2399.95</value></price>
- <price><currency>EUR</currency><value>1499.95</value></price>
- <price><currency>GBP</currency><value>1699.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>2399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1699.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -385,9 +401,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>5999.95</value></price>
- <price><currency>EUR</currency><value>3499.95</value></price>
- <price><currency>GBP</currency><value>3999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -410,9 +435,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>9.95</value></price>
- <price><currency>EUR</currency><value>9.95</value></price>
- <price><currency>GBP</currency><value>9.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>9.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>9.95</value>
+ </price>
</recurringPrice>
</phase>
</initialPhases>
@@ -422,9 +456,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>199.95</value></price>
- <price><currency>EUR</currency><value>199.95</value></price>
- <price><currency>GBP</currency><value>199.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -447,9 +490,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>19.95</value></price>
- <price><currency>EUR</currency><value>49.95</value></price>
- <price><currency>GBP</currency><value>69.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>19.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>49.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>69.95</value>
+ </price>
</recurringPrice>
</phase>
</initialPhases>
@@ -459,9 +511,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>2399.95</value></price>
- <price><currency>EUR</currency><value>1499.95</value></price>
- <price><currency>GBP</currency><value>1699.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>2399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1699.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -484,10 +545,19 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>99.95</value></price>
- <price><currency>EUR</currency><value>99.95</value></price>
- <price><currency>GBP</currency><value>99.95</value></price>
- </recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>99.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>99.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>99.95</value>
+ </price>
+ </recurringPrice>
</phase>
</initialPhases>
<finalPhase type="EVERGREEN">
@@ -496,37 +566,110 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>5999.95</value></price>
- <price><currency>EUR</currency><value>3499.95</value></price>
- <price><currency>GBP</currency><value>3999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
<plan name="laser-scope-monthly">
- <product>Laser-Scope</product>
+ <product>Laser-Scope</product>
+ <initialPhases>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>1</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
<finalPhase type="EVERGREEN">
<duration>
<unit>UNLIMITED</unit>
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>1999.95</value></price>
- <price><currency>EUR</currency><value>1499.95</value></price>
- <price><currency>GBP</currency><value>1999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>1999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>1499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>1999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
<plan name="telescopic-scope-monthly">
<product>Telescopic-Scope</product>
+ <initialPhases>
+ <phase type="DISCOUNT">
+ <duration>
+ <unit>MONTHS</unit>
+ <number>1</number>
+ </duration>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>399.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>299.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>399.95</value>
+ </price>
+ </recurringPrice>
+ </phase>
+ </initialPhases>
<finalPhase type="EVERGREEN">
<duration>
<unit>UNLIMITED</unit>
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>999.95</value></price>
- <price><currency>EUR</currency><value>499.95</value></price>
- <price><currency>GBP</currency><value>999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -538,9 +681,18 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>999.95</value></price>
- <price><currency>EUR</currency><value>499.95</value></price>
- <price><currency>GBP</currency><value>999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
<plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
@@ -564,9 +716,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>199.95</value></price>
- <price><currency>EUR</currency><value>199.95</value></price>
- <price><currency>GBP</currency><value>199.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -589,9 +750,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>199.95</value></price>
- <price><currency>EUR</currency><value>199.95</value></price>
- <price><currency>GBP</currency><value>199.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -605,9 +775,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>5999.95</value></price>
- <price><currency>EUR</currency><value>3499.95</value></price>
- <price><currency>GBP</currency><value>3999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
</recurringPrice>
</phase>
</initialPhases>
@@ -617,9 +796,18 @@ Use Cases to do:
</duration>
<billingPeriod>ANNUAL</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>5999.95</value></price>
- <price><currency>EUR</currency><value>3499.95</value></price>
- <price><currency>GBP</currency><value>3999.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>5999.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>3499.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>3999.95</value>
+ </price>
</recurringPrice>
</finalPhase>
</plan>
@@ -632,23 +820,40 @@ Use Cases to do:
</duration>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
- <price><currency>USD</currency><value>199.95</value></price>
- <price><currency>EUR</currency><value>199.95</value></price>
- <price><currency>GBP</currency><value>199.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>199.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>199.95</value>
+ </price>
</recurringPrice>
<fixedPrice>
- <price><currency>USD</currency><value>599.95</value></price>
- <price><currency>EUR</currency><value>599.95</value></price>
- <price><currency>GBP</currency><value>599.95</value></price>
+ <price>
+ <currency>USD</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>599.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>599.95</value>
+ </price>
</fixedPrice>
</finalPhase>
</plan>
</plans>
-
<priceLists>
- <defaultPriceList name="DEFAULT">
+ <defaultPriceList name="DEFAULT">
<plans>
- <plan>blowdart-monthly</plan>
+ <plan>blowdart-monthly</plan>
<plan>pistol-monthly</plan>
<plan>shotgun-monthly</plan>
<plan>assault-rifle-monthly</plan>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index e7f8a78..237b927 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -49,6 +49,7 @@ import com.ning.billing.catalog.api.Plan;
import com.ning.billing.catalog.api.PlanPhase;
import com.ning.billing.catalog.api.PlanPhaseSpecifier;
import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.ProductCategory;
import com.ning.billing.entitlement.api.SubscriptionFactory;
import com.ning.billing.entitlement.api.SubscriptionTransitionType;
import com.ning.billing.entitlement.api.user.Subscription;
@@ -88,13 +89,17 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
List<SubscriptionBundle> bundles = entitlementDao.getSubscriptionBundleForAccount(accountId);
SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
+
for (final SubscriptionBundle bundle: bundles) {
List<Subscription> subscriptions = entitlementDao.getSubscriptions(subscriptionFactory, bundle.getId());
+ DateTime bundleStartDate = bundle.getStartDate();
for (final Subscription subscription: subscriptions) {
- for (final SubscriptionEventTransition transition : ((SubscriptionData) subscription).getBillingTransitions()) {
+ // STEPH hack -- see RI-1169
+ bundleStartDate = (subscription.getCategory() == ProductCategory.BASE && bundleStartDate == null) ? subscription.getStartDate() : bundleStartDate;
+ for (final SubscriptionEventTransition transition : ((SubscriptionData) subscription).getBillingTransitions()) {
try {
- BillingEvent event = new DefaultBillingEvent(transition, subscription, calculateBcd(bundle, subscription, transition, accountId), currency);
+ BillingEvent event = new DefaultBillingEvent(transition, subscription, calculateBcd(bundle, subscription, transition, accountId, bundleStartDate), currency);
result.add(event);
} catch (CatalogApiException e) {
log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
@@ -114,7 +119,7 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
}
private int calculateBcd(final SubscriptionBundle bundle, final Subscription subscription,
- final SubscriptionEventTransition transition, final UUID accountId) throws CatalogApiException, AccountApiException {
+ final SubscriptionEventTransition transition, final UUID accountId, DateTime bundleStartDate) throws CatalogApiException, AccountApiException {
Catalog catalog = catalogService.getFullCatalog();
Plan plan = (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ?
transition.getNextPlan() : transition.getPreviousPlan();
@@ -143,7 +148,7 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
}
break;
case BUNDLE :
- result = bundle.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
+ result = bundleStartDate.toDateTime(account.getTimeZone()).getDayOfMonth();
break;
case SUBSCRIPTION :
result = subscription.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
@@ -214,6 +219,4 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
}
}
}
-
-
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
index 9813448..a3efd7a 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultEntitlementRepairApi.java
@@ -112,6 +112,7 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
if (bundle == null) {
throw new EntitlementRepairException(ErrorCode.ENT_REPAIR_UNKNOWN_BUNDLE, input.getBundleId());
}
+
// Subscriptions are ordered with BASE subscription first-- if exists
final List<Subscription> subscriptions = dao.getSubscriptions(factory, input.getBundleId());
@@ -233,7 +234,7 @@ public class DefaultEntitlementRepairApi implements EntitlementRepairApi {
final List<SubscriptionRepair> repairs = createGetSubscriptionRepairList(subscriptions, convertDataRepair(inRepair));
return createGetBundleRepair(input.getBundleId(), input.getViewId(), repairs);
} else {
- dao.repair(input.getBundleId(), inRepair, context);
+ dao.repair(bundle.getAccountId(), input.getBundleId(), inRepair, context);
return getBundleRepair(input.getBundleId());
}
} catch (CatalogApiException e) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java
index f7be710..47a475c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/repair/DefaultRepairEntitlementEvent.java
@@ -23,12 +23,14 @@ public class DefaultRepairEntitlementEvent implements RepairEntitlementEvent {
private final UUID userToken;
private final UUID bundleId;
+ private final UUID accountId;
private final DateTime efectiveDate;
- public DefaultRepairEntitlementEvent(final UUID userToken, final UUID bundleId, final DateTime efectiveDate) {
+ public DefaultRepairEntitlementEvent(final UUID userToken, final UUID acountId, final UUID bundleId, final DateTime efectiveDate) {
this.userToken = userToken;
this.bundleId = bundleId;
+ this.accountId = acountId;
this.efectiveDate = efectiveDate;
}
@@ -48,6 +50,11 @@ public class DefaultRepairEntitlementEvent implements RepairEntitlementEvent {
}
@Override
+ public UUID getAccountId() {
+ return accountId;
+ }
+
+ @Override
public DateTime getEffectiveDate() {
return efectiveDate;
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index 453020c..031d9c7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -51,6 +51,7 @@ import com.ning.billing.entitlement.api.billing.DefaultEntitlementBillingApi;
import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
import com.ning.billing.entitlement.api.migration.DefaultEntitlementMigrationApi;
import com.ning.billing.entitlement.api.migration.EntitlementMigrationApi;
+import com.ning.billing.entitlement.api.repair.EntitlementRepairApi;
import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
import com.ning.billing.entitlement.api.user.EntitlementUserApi;
import com.ning.billing.entitlement.api.user.Subscription;
@@ -91,6 +92,7 @@ public class Engine implements EventListener, EntitlementService {
private final EntitlementUserApi userApi;
private final EntitlementBillingApi billingApi;
private final EntitlementMigrationApi migrationApi;
+ private final EntitlementRepairApi repairApi;
private final AddonUtils addonUtils;
private final Bus eventBus;
@@ -104,6 +106,7 @@ public class Engine implements EventListener, EntitlementService {
public Engine(Clock clock, EntitlementDao dao, PlanAligner planAligner,
EntitlementConfig config, DefaultEntitlementUserApi userApi,
DefaultEntitlementBillingApi billingApi,
+ EntitlementRepairApi repairApi,
DefaultEntitlementMigrationApi migrationApi, AddonUtils addonUtils, Bus eventBus,
NotificationQueueService notificationQueueService,
SubscriptionFactory subscriptionFactory,
@@ -113,6 +116,7 @@ public class Engine implements EventListener, EntitlementService {
this.dao = dao;
this.planAligner = planAligner;
this.userApi = userApi;
+ this.repairApi = repairApi;
this.billingApi = billingApi;
this.migrationApi = migrationApi;
this.addonUtils = addonUtils;
@@ -200,6 +204,10 @@ public class Engine implements EventListener, EntitlementService {
return migrationApi;
}
+ @Override
+ public EntitlementRepairApi getRepairApi() {
+ return repairApi;
+ }
@Override
public void processEventReady(final EntitlementEvent event, final int seqId, final CallContext context) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index 5136a72..ac397b6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -81,7 +81,7 @@ public interface EntitlementDao {
public void migrate(final UUID accountId, final AccountMigrationData data, final CallContext context);
// Repair
- public void repair(final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context);
+ public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context);
// Custom Fields
public void saveCustomFields(final SubscriptionData subscription, final CallContext context);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 0256883..14e0dfd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -601,7 +601,7 @@ public class EntitlementSqlDao implements EntitlementDao {
});
}
- public void repair(final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context) {
+ public void repair(final UUID accountId, final UUID bundleId, final List<SubscriptionDataRepair> inRepair, final CallContext context) {
subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
@Override
@@ -624,7 +624,7 @@ public class EntitlementSqlDao implements EntitlementDao {
}
}
try {
- RepairEntitlementEvent busEvent = new DefaultRepairEntitlementEvent(context.getUserToken(), bundleId, clock.getUTCNow());
+ RepairEntitlementEvent busEvent = new DefaultRepairEntitlementEvent(context.getUserToken(), accountId, bundleId, clock.getUTCNow());
eventBus.postFromTransaction(busEvent, transactional);
} catch (EventBusException e) {
log.warn("Failed to post repair entitlement event for bundle " + bundleId);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
index 098dc4b..178ea2d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/RepairEntitlementDao.java
@@ -242,7 +242,7 @@ public class RepairEntitlementDao implements EntitlementDao, RepairEntitlementLi
}
@Override
- public void repair(UUID bundleId, List<SubscriptionDataRepair> inRepair,
+ public void repair(UUID accountId, UUID bundleId, List<SubscriptionDataRepair> inRepair,
CallContext context) {
throw new EntitlementError("Not implemented");
}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 57b59b9..e905008 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -456,7 +456,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
}
@Override
- public void repair(UUID bundleId, List<SubscriptionDataRepair> inRepair,
+ public void repair(UUID accountId, UUID bundleId, List<SubscriptionDataRepair> inRepair,
CallContext context) {
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index b02c3e8..49d175f 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
+import com.ning.billing.entitlement.api.repair.RepairEntitlementEvent;
import com.ning.billing.entitlement.api.user.SubscriptionEventTransition;
import com.ning.billing.invoice.api.InvoiceApiException;
@@ -43,6 +44,19 @@ public class InvoiceListener {
}
@Subscribe
+ public void handleRepairEntitlementEvent(final RepairEntitlementEvent repairEvent) {
+ // STEPH
+ /*
+ try {
+ CallContext context = factory.createCallContext("RepairBundle", CallOrigin.INTERNAL, UserType.SYSTEM, repairEvent.getUserToken());
+ dispatcher.processAccount(repairEvent.getAccountId(), repairEvent.getEffectiveDate(), false, context);
+ } catch (InvoiceApiException e) {
+ log.error(e.getMessage());
+ }
+ */
+ }
+
+ @Subscribe
public void handleSubscriptionTransition(final SubscriptionEventTransition transition) {
try {
if (transition.getRemainingEventsForUserOperation() > 0) {