Skip to content

Commit

Permalink
subscription, entitlement, jaxrs: Modify entitlement apis to allow op…
Browse files Browse the repository at this point in the history
…erations using either the catalog planName or the undeterministic triplet {productName, billingPeriod, priceList}. See #303.

At the JAXRS level the code still allows operation to happen using the {productName, billingPeriod, priceList} triplet. If the catalog contains multiple entries with the same shape in a given pricelist,
such operations would not be deterministic. Missing is the logic to detect such cases and throw exceptions (along with tests).
  • Loading branch information
sbrossie committed Aug 22, 2016
1 parent 9bfe596 commit d433005
Show file tree
Hide file tree
Showing 31 changed files with 257 additions and 201 deletions.
Expand Up @@ -26,6 +26,7 @@
import org.killbill.billing.catalog.api.Plan; import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase; import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride; import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceList; import org.killbill.billing.catalog.api.PriceList;
import org.killbill.billing.catalog.api.Product; import org.killbill.billing.catalog.api.Product;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
Expand All @@ -52,15 +53,15 @@ public boolean uncancel(final CallContext context)
throws SubscriptionBaseApiException; throws SubscriptionBaseApiException;


// Return the effective date of the change // Return the effective date of the change
public DateTime changePlan(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final CallContext context) public DateTime changePlan(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final CallContext context)
throws SubscriptionBaseApiException; throws SubscriptionBaseApiException;


// Return the effective date of the change // Return the effective date of the change
public DateTime changePlanWithDate(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate, final CallContext context) public DateTime changePlanWithDate(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime requestedDate, final CallContext context)
throws SubscriptionBaseApiException; throws SubscriptionBaseApiException;


// Return the effective date of the change // Return the effective date of the change
public DateTime changePlanWithPolicy(final String productName, final BillingPeriod term, final String priceList, final List<PlanPhasePriceOverride> overrides, public DateTime changePlanWithPolicy(final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides,
final BillingActionPolicy policy, final CallContext context) final BillingActionPolicy policy, final CallContext context)
throws SubscriptionBaseApiException; throws SubscriptionBaseApiException;


Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.killbill.billing.catalog.api.CatalogApiException; import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride; import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier; import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun; import org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun;
import org.killbill.billing.entitlement.api.EntitlementSpecifier; import org.killbill.billing.entitlement.api.EntitlementSpecifier;
import org.killbill.billing.events.EffectiveSubscriptionInternalEvent; import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
Expand Down Expand Up @@ -88,8 +89,7 @@ public List<SubscriptionBase> getSubscriptionsForBundle(UUID bundleId, DryRunArg


public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(SubscriptionBase subscription, InternalTenantContext context); public List<EffectiveSubscriptionInternalEvent> getBillingTransitions(SubscriptionBase subscription, InternalTenantContext context);


public DateTime getDryRunChangePlanEffectiveDate(SubscriptionBase subscription, String productName, BillingPeriod term, public DateTime getDryRunChangePlanEffectiveDate(SubscriptionBase subscription, PlanSpecifier spec, DateTime requestedDate, BillingActionPolicy policy, InternalTenantContext context) throws SubscriptionBaseApiException;
String priceList, DateTime requestedDate, BillingActionPolicy policy, InternalTenantContext context) throws SubscriptionBaseApiException;


public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String baseProductName, public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(UUID subscriptionId, @Nullable String baseProductName,
DateTime requestedDate, InternalTenantContext context) throws SubscriptionBaseApiException; DateTime requestedDate, InternalTenantContext context) throws SubscriptionBaseApiException;
Expand Down
Expand Up @@ -20,6 +20,7 @@
import java.util.List; import java.util.List;


import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account; import org.killbill.billing.account.api.Account;
import org.killbill.billing.api.TestApiListener.NextEvent; import org.killbill.billing.api.TestApiListener.NextEvent;
import org.killbill.billing.catalog.api.BillingPeriod; import org.killbill.billing.catalog.api.BillingPeriod;
Expand Down Expand Up @@ -84,7 +85,7 @@ public void testRetirePlan() throws Exception {
entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext); entitlementApi.createBaseEntitlement(account.getId(), spec, "externalKey2", null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
fail(); // force to fail is there is not an exception fail(); // force to fail is there is not an exception
} catch (final EntitlementApiException e) { } catch (final EntitlementApiException e) {
assertTrue(e.getLocalizedMessage().startsWith("Could not find a plan matching: (product: 'Pistol', billing period: 'MONTHLY'")); assertEquals(e.getCode(), ErrorCode.CAT_PLAN_NOT_FOUND.getCode());
} }


// Move out a month and verify 'Pistol' plan continue working as expected. // Move out a month and verify 'Pistol' plan continue working as expected.
Expand Down
Expand Up @@ -53,6 +53,7 @@
import org.killbill.billing.catalog.api.PhaseType; import org.killbill.billing.catalog.api.PhaseType;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride; import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier; import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet; import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement; import org.killbill.billing.entitlement.api.DefaultEntitlement;
Expand Down Expand Up @@ -665,9 +666,9 @@ public Entitlement apply(@Nullable final Void dontcare) {
// Need to fetch again to get latest CTD updated from the system // Need to fetch again to get latest CTD updated from the system
Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext); Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
if (billingPolicy == null) { if (billingPolicy == null) {
refreshedEntitlement = refreshedEntitlement.changePlan(productName, billingPeriod, priceList, null, ImmutableList.<PluginProperty>of(), callContext); refreshedEntitlement = refreshedEntitlement.changePlan(new PlanSpecifier(productName, billingPeriod, priceList), null, ImmutableList.<PluginProperty>of(), callContext);
} else { } else {
refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(productName, billingPeriod, priceList, null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext); refreshedEntitlement = refreshedEntitlement.changePlanOverrideBillingPolicy(new PlanSpecifier(productName, billingPeriod, priceList), null, null, billingPolicy, ImmutableList.<PluginProperty>of(), callContext);
} }
return refreshedEntitlement; return refreshedEntitlement;
} catch (final EntitlementApiException e) { } catch (final EntitlementApiException e) {
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.killbill.billing.catalog.api.BillingActionPolicy; import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod; import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency; import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement; import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice; import org.killbill.billing.invoice.api.Invoice;
Expand Down Expand Up @@ -173,7 +174,7 @@ public void testParentInvoiceGenerationMultipleActionsSameDay() throws Exception


// upgrade plan // upgrade plan
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE); busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
baseEntitlementChild.changePlanOverrideBillingPolicy("Shotgun", BillingPeriod.MONTHLY, baseEntitlementChild.getLastActivePriceList().getName(), null, clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext); baseEntitlementChild.changePlanOverrideBillingPolicy(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, baseEntitlementChild.getLastActivePriceList().getName()), null, clock.getToday(childAccount.getTimeZone()), BillingActionPolicy.IMMEDIATE, null, callContext);
assertListenerStatus(); assertListenerStatus();


// check parent invoice. Expected to have the same invoice item with the amount updated // check parent invoice. Expected to have the same invoice item with the amount updated
Expand Down
Expand Up @@ -18,8 +18,6 @@
package org.killbill.billing.beatrix.integration; package org.killbill.billing.beatrix.integration;


import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID; import java.util.UUID;


import javax.inject.Inject; import javax.inject.Inject;
Expand All @@ -32,8 +30,10 @@
import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck; import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
import org.killbill.billing.callcontext.DefaultCallContext; import org.killbill.billing.callcontext.DefaultCallContext;
import org.killbill.billing.catalog.api.BillingPeriod; import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogUserApi; import org.killbill.billing.catalog.api.CatalogUserApi;
import org.killbill.billing.catalog.api.PlanPhaseSpecifier; import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet; import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.catalog.api.SimplePlanDescriptor; import org.killbill.billing.catalog.api.SimplePlanDescriptor;
Expand Down Expand Up @@ -94,7 +94,7 @@ public void testBasic() throws Exception {
StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext); StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
assertEquals(catalog.getCurrentPlans().length, 1); assertEquals(catalog.getCurrentPlans().length, 1);


final Entitlement baseEntitlement = createEntitlement("Foo", BillingPeriod.MONTHLY); final Entitlement baseEntitlement = createEntitlement("foo-monthly", true);


invoiceChecker.checkInvoice(account.getId(), 1, testCallContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, BigDecimal.TEN)); invoiceChecker.checkInvoice(account.getId(), 1, testCallContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.RECURRING, BigDecimal.TEN));


Expand All @@ -104,10 +104,9 @@ public void testBasic() throws Exception {
catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext); catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
assertEquals(catalog.getCurrentPlans().length, 2); assertEquals(catalog.getCurrentPlans().length, 2);



// Change Plan to the newly added Plan and verify correct default rules behavior (IMMEDIATE change) // Change Plan to the newly added Plan and verify correct default rules behavior (IMMEDIATE change)
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT); busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
baseEntitlement.changePlan("SuperFoo", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), testCallContext); baseEntitlement.changePlan(new PlanSpecifier("SuperFoo", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), testCallContext);
assertListenerStatus(); assertListenerStatus();


invoiceChecker.checkInvoice(account.getId(), 2, testCallContext, invoiceChecker.checkInvoice(account.getId(), 2, testCallContext,
Expand All @@ -116,17 +115,61 @@ public void testBasic() throws Exception {
assertListenerStatus(); assertListenerStatus();
} }


@Test(groups = "slow")
public void testWithMultiplePlansForOneProduct() throws CatalogApiException, EntitlementApiException {

// Create a per-tenant catalog with one plan
final SimplePlanDescriptor desc1 = new DefaultSimplePlanDescriptor("xxx-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 0, TimeUnit.UNLIMITED, ImmutableList.<String>of());
catalogUserApi.addSimplePlan(desc1, init, testCallContext);
StaticCatalog catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
assertEquals(catalog.getCurrentProducts().length, 1);
assertEquals(catalog.getCurrentPlans().length, 1);

final Entitlement baseEntitlement1 = createEntitlement("xxx-monthly", true);

// Add a second plan for same product but with a 14 days trial
final SimplePlanDescriptor desc2 = new DefaultSimplePlanDescriptor("xxx-14-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 14, TimeUnit.DAYS, ImmutableList.<String>of());
catalogUserApi.addSimplePlan(desc2, init, testCallContext);
catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
assertEquals(catalog.getCurrentProducts().length, 1);
assertEquals(catalog.getCurrentPlans().length, 2);


private Entitlement createEntitlement(String productName, BillingPeriod billingPeriod) throws EntitlementApiException { final Entitlement baseEntitlement2 = createEntitlement("xxx-14-monthly", false);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(productName, billingPeriod, PriceListSet.DEFAULT_PRICELIST_NAME, null);


busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
// Add a second plan for same product but with a 30 days trial
final SimplePlanDescriptor desc3 = new DefaultSimplePlanDescriptor("xxx-30-monthly", "XXX", ProductCategory.BASE, account.getCurrency(), BigDecimal.TEN, BillingPeriod.MONTHLY, 30, TimeUnit.DAYS, ImmutableList.<String>of());
catalogUserApi.addSimplePlan(desc3, init, testCallContext);
catalog = catalogUserApi.getCurrentCatalog("dummy", testCallContext);
assertEquals(catalog.getCurrentProducts().length, 1);
assertEquals(catalog.getCurrentPlans().length, 3);

final Entitlement baseEntitlement3 = createEntitlement("xxx-30-monthly", false);

// Move clock 14 days
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(14);
assertListenerStatus();

// Move clock 16 days
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(16);
assertListenerStatus();
}

private Entitlement createEntitlement(final String planName, final boolean expectPayment) throws EntitlementApiException {
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, null);

if (expectPayment) {
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
} else {
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
}
final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext); final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, null, null, false, ImmutableList.<PluginProperty>of(), testCallContext);
assertListenerStatus(); assertListenerStatus();
return entitlement; return entitlement;
} }



private void setupTenant() throws TenantApiException { private void setupTenant() throws TenantApiException {
final UUID uuid = UUID.randomUUID(); final UUID uuid = UUID.randomUUID();
final String externalKey = uuid.toString(); final String externalKey = uuid.toString();
Expand All @@ -136,7 +179,7 @@ private void setupTenant() throws TenantApiException {
tenant = tenantUserApi.createTenant(new DefaultTenant(uuid, init, init, externalKey, apiKey, apiSecret), callContext); tenant = tenantUserApi.createTenant(new DefaultTenant(uuid, init, init, externalKey, apiKey, apiSecret), callContext);


testCallContext = new DefaultCallContext(tenant.getId(), "tester", CallOrigin.EXTERNAL, UserType.TEST, testCallContext = new DefaultCallContext(tenant.getId(), "tester", CallOrigin.EXTERNAL, UserType.TEST,
"good reason", "trust me", uuid, clock); "good reason", "trust me", uuid, clock);
} }


private void setupAccount() throws Exception { private void setupAccount() throws Exception {
Expand All @@ -146,7 +189,7 @@ private void setupAccount() throws Exception {
assertNotNull(account); assertNotNull(account);


final PaymentMethodPlugin info = createPaymentMethodPlugin(); final PaymentMethodPlugin info = createPaymentMethodPlugin();
paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, info, PLUGIN_PROPERTIES, testCallContext); paymentApi.addPaymentMethod(account, UUID.randomUUID().toString(), BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, true, info, PLUGIN_PROPERTIES, testCallContext);
} }


} }
Expand Up @@ -32,6 +32,7 @@
import org.killbill.billing.catalog.api.BillingPeriod; import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency; import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PlanPhasePriceOverride; import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet; import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement; import org.killbill.billing.entitlement.api.DefaultEntitlement;
Expand Down Expand Up @@ -120,7 +121,7 @@ public void testChangePlanWithRecurringPriceOverride() throws Exception {
overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, new BigDecimal("279.95"))); overrides.add(new DefaultPlanPhasePriceOverride("shotgun-monthly-evergreen", account.getCurrency(), null, new BigDecimal("279.95")));


busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT); busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
bpSubscription.changePlanOverrideBillingPolicy("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, overrides, new LocalDate(2012, 5, 1), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext); bpSubscription.changePlanOverrideBillingPolicy(new PlanSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), overrides, new LocalDate(2012, 5, 1), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus(); assertListenerStatus();


invoiceChecker.checkInvoice(account.getId(), 3, callContext, invoiceChecker.checkInvoice(account.getId(), 3, callContext,
Expand Down Expand Up @@ -167,7 +168,7 @@ public void testChangePlanWithRecurringPriceOverrideAndSamePlan() throws Excepti
overrides.add(new DefaultPlanPhasePriceOverride("telescopic-scope-monthly-evergreen", account.getCurrency(), null, new BigDecimal("1200.00"))); overrides.add(new DefaultPlanPhasePriceOverride("telescopic-scope-monthly-evergreen", account.getCurrency(), null, new BigDecimal("1200.00")));


busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT); busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
aoEntitlement.changePlanOverrideBillingPolicy("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, overrides, new LocalDate(2012, 5, 5), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext); aoEntitlement.changePlanOverrideBillingPolicy(new PlanSpecifier("Telescopic-Scope", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME), overrides, new LocalDate(2012, 5, 5), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus(); assertListenerStatus();
} }
} }
Expand Up @@ -34,6 +34,7 @@
import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck; import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
import org.killbill.billing.catalog.api.BillingPeriod; import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Currency; import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.PlanSpecifier;
import org.killbill.billing.catalog.api.PriceListSet; import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.ProductCategory; import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.BlockingApiException; import org.killbill.billing.entitlement.api.BlockingApiException;
Expand Down Expand Up @@ -995,7 +996,7 @@ public int compare(final Invoice i1, final Invoice i2) {
private void checkChangePlanWithOverdueState(final Entitlement entitlement, final boolean shouldFail, final boolean expectedPayment) { private void checkChangePlanWithOverdueState(final Entitlement entitlement, final boolean shouldFail, final boolean expectedPayment) {
if (shouldFail) { if (shouldFail) {
try { try {
entitlement.changePlan("Pistol", term, PriceListSet.DEFAULT_PRICELIST_NAME, null, ImmutableList.<PluginProperty>of(), callContext); entitlement.changePlan(new PlanSpecifier("Pistol", term, PriceListSet.DEFAULT_PRICELIST_NAME), null, ImmutableList.<PluginProperty>of(), callContext);
} catch (EntitlementApiException e) { } catch (EntitlementApiException e) {
assertTrue(e.getCause() instanceof BlockingApiException || e.getCode() == ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode(), assertTrue(e.getCause() instanceof BlockingApiException || e.getCode() == ErrorCode.SUB_CHANGE_NON_ACTIVE.getCode(),
String.format("Cause is %s, message is %s", e.getCause(), e.getMessage())); String.format("Cause is %s, message is %s", e.getCause(), e.getMessage()));
Expand Down
Expand Up @@ -82,31 +82,9 @@ public DefaultPlan findPlan(final Product product, final BillingPeriod period) {


@Override @Override
public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) { public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
if (getPlans() != null) {
for (final DefaultPlan cur : getPlans()) {
final int numPlans = findNumberOfPlans(cur.getProduct(), cur.getRecurringBillingPeriod());
if (numPlans > 1) {
errors.add(new ValidationError(
String.format("There are %d plans in pricelist %s and have the same product/billingPeriod (%s, %s)",
numPlans, getName(), cur.getProduct().getName(), cur.getRecurringBillingPeriod()), catalog.getCatalogURI(),
DefaultPriceListSet.class, getName()));
}
}
}
return errors; return errors;
} }


private int findNumberOfPlans(final Product product, final BillingPeriod period) {
int count = 0;
for (final DefaultPlan cur : getPlans()) {
if (cur.getProduct().equals(product) &&
(cur.getRecurringBillingPeriod() == null || cur.getRecurringBillingPeriod().equals(period))) {
count++;
}
}
return count;
}

public DefaultPriceList setName(final String name) { public DefaultPriceList setName(final String name) {
this.name = name; this.name = name;
return this; return this;
Expand Down

1 comment on commit d433005

@pierre
Copy link
Member

@pierre pierre commented on d433005 Aug 23, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Please sign in to comment.