use of org.killbill.billing.catalog.api.UsagePriceOverride in project killbill by killbill.
the class TestCatalogOverrideDao method testGetOverriddenPlanPhasesWithUsageOverrides.
@Test(groups = "slow")
public void testGetOverriddenPlanPhasesWithUsageOverrides() throws Exception {
final StandaloneCatalog catalog = getCatalog("SpyCarAdvanced.xml");
final Plan plan = catalog.findPlan("gas-monthly");
final PlanPhasePriceOverride[] resolvedOverrides = new PlanPhasePriceOverride[plan.getAllPhases().length];
List<TieredBlockPriceOverride> tieredBlockPriceOverrides = new ArrayList<TieredBlockPriceOverride>();
DefaultTieredBlockPriceOverride tieredBlockPriceOverride = new DefaultTieredBlockPriceOverride("gallons", new Double("1"), new BigDecimal(4), Currency.USD, new Double("100"));
tieredBlockPriceOverrides.add(tieredBlockPriceOverride);
List<TierPriceOverride> tierPriceOverrides = new ArrayList<TierPriceOverride>();
DefaultTierPriceOverride tierPriceOverride = new DefaultTierPriceOverride(tieredBlockPriceOverrides);
tierPriceOverrides.add(tierPriceOverride);
List<UsagePriceOverride> usagePriceOverrides = new ArrayList<UsagePriceOverride>();
DefaultUsagePriceOverride usagePriceOverride = new DefaultUsagePriceOverride("gas-monthly-in-arrear", UsageType.CONSUMABLE, tierPriceOverrides);
usagePriceOverrides.add(usagePriceOverride);
// Override the gallons price from $3.95 to $4 and also the recurring price from $0 to $348.64
resolvedOverrides[0] = new DefaultPlanPhasePriceOverride(plan.getFinalPhase().getName(), Currency.USD, BigDecimal.ZERO, new BigDecimal("348.64"), usagePriceOverrides);
final CatalogOverridePlanDefinitionModelDao newPlan = catalogOverrideDao.getOrCreateOverridePlanDefinition(plan, new DateTime(catalog.getEffectiveDate()), resolvedOverrides, internalCallContext);
final List<CatalogOverridePhaseDefinitionModelDao> phases = catalogOverrideDao.getOverriddenPlanPhases(newPlan.getRecordId(), internalCallContext);
assertEquals(phases.size(), 1);
final CatalogOverridePhaseDefinitionModelDao curPhase = phases.get(0);
assertEquals(curPhase.getCurrency(), resolvedOverrides[0].getCurrency().name());
assertEquals(curPhase.getFixedPrice().compareTo(resolvedOverrides[0].getFixedPrice()), 0);
assertEquals(curPhase.getRecurringPrice().compareTo(resolvedOverrides[0].getRecurringPrice()), 0);
assertEquals(curPhase.getParentPhaseName(), resolvedOverrides[0].getPhaseName());
final List<CatalogOverrideUsageDefinitionModelDao> usages = catalogOverrideDao.getOverriddenPhaseUsages(curPhase.getRecordId(), internalCallContext);
assertEquals(usages.size(), 1);
final CatalogOverrideUsageDefinitionModelDao curUsage = usages.get(0);
assertEquals(curUsage.getParentUsageName(), usagePriceOverride.getName());
assertEquals(curUsage.getType(), usagePriceOverride.getUsageType().toString());
final List<CatalogOverrideTierDefinitionModelDao> tiers = catalogOverrideDao.getOverriddenUsageTiers(curUsage.getRecordId(), internalCallContext);
assertEquals(tiers.size(), 1);
final CatalogOverrideTierDefinitionModelDao curTier = tiers.get(0);
final List<CatalogOverrideBlockDefinitionModelDao> tierBlocks = catalogOverrideDao.getOverriddenTierBlocks(curTier.getRecordId(), internalCallContext);
assertEquals(tierBlocks.size(), 1);
final CatalogOverrideBlockDefinitionModelDao curTieredBlock = tierBlocks.get(0);
assertEquals(curTieredBlock.getParentUnitName(), tieredBlockPriceOverride.getUnitName());
assertEquals(curTieredBlock.getPrice().compareTo(tieredBlockPriceOverride.getPrice()), 0);
assertEquals(curTieredBlock.getSize(), (double) tieredBlockPriceOverride.getSize());
assertEquals(curTieredBlock.getMax(), (double) tieredBlockPriceOverride.getMax());
}
use of org.killbill.billing.catalog.api.UsagePriceOverride in project killbill by killbill.
the class DefaultPriceOverride method getOrCreateOverriddenPlan.
@Override
public DefaultPlan getOrCreateOverriddenPlan(final StandaloneCatalog standaloneCatalog, final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, @Nullable final InternalCallContext context) throws CatalogApiException {
final PlanPhasePriceOverride[] resolvedOverride = new PlanPhasePriceOverride[parentPlan.getAllPhases().length];
int index = 0;
for (final PlanPhase curPhase : parentPlan.getAllPhases()) {
final PlanPhasePriceOverride curOverride = Iterables.tryFind(overrides, new Predicate<PlanPhasePriceOverride>() {
@Override
public boolean apply(final PlanPhasePriceOverride input) {
if (input.getPhaseName() != null) {
return input.getPhaseName().equals(curPhase.getName());
}
// If the phaseName was not passed, we infer by matching the phaseType. This obviously would not work in a case where
// a plan is defined with multiple phases of the same type.
final PlanPhaseSpecifier curPlanPhaseSpecifier = input.getPlanPhaseSpecifier();
if (curPlanPhaseSpecifier.getPhaseType().equals(curPhase.getPhaseType())) {
return true;
}
return false;
}
}).orNull();
if (curOverride != null) {
List<UsagePriceOverride> resolvedUsageOverrides = getResolvedUsageOverrides(curPhase.getUsages(), curOverride.getUsagePriceOverrides());
resolvedOverride[index++] = new DefaultPlanPhasePriceOverride(curPhase.getName(), curOverride.getCurrency(), curOverride.getFixedPrice(), curOverride.getRecurringPrice(), resolvedUsageOverrides);
} else {
resolvedOverride[index++] = null;
}
}
for (int i = 0; i < resolvedOverride.length; i++) {
final PlanPhasePriceOverride curOverride = resolvedOverride[i];
if (curOverride != null) {
final DefaultPlanPhase curPhase = (DefaultPlanPhase) parentPlan.getAllPhases()[i];
if (curPhase.getFixed() == null && curOverride.getFixedPrice() != null) {
final String error = String.format("There is no existing fixed price for the phase %s", curPhase.getName());
throw new CatalogApiException(ErrorCode.CAT_INVALID_INVALID_PRICE_OVERRIDE, parentPlan.getName(), error);
}
if (curPhase.getRecurring() == null && curOverride.getRecurringPrice() != null) {
final String error = String.format("There is no existing recurring price for the phase %s", curPhase.getName());
throw new CatalogApiException(ErrorCode.CAT_INVALID_INVALID_PRICE_OVERRIDE, parentPlan.getName(), error);
}
}
}
final String planName;
if (context != null) {
final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan, catalogEffectiveDate, resolvedOverride, context);
planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
} else {
planName = new StringBuffer(parentPlan.getName()).append("-dryrun-").append(DRY_RUN_PLAN_IDX.incrementAndGet()).toString();
}
final DefaultPlan result = new DefaultPlan(planName, (DefaultPlan) parentPlan, resolvedOverride);
result.initialize(standaloneCatalog);
if (context == null) {
overriddenPlanCache.addDryRunPlan(planName, result);
}
return result;
}
use of org.killbill.billing.catalog.api.UsagePriceOverride in project killbill by killbill.
the class TestWithBCDUpdate method testBCDChangeFromFreePlanToPayingPlanWithTrialAndCHANGE_OF_PLANPolicy30DaysMonth.
@Test(groups = "slow")
public void testBCDChangeFromFreePlanToPayingPlanWithTrialAndCHANGE_OF_PLANPolicy30DaysMonth() throws Exception {
final DateTime initialDate = new DateTime(2016, 4, 1, 0, 13, 42, 0, testTimeZone);
clock.setTime(initialDate);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(0));
assertNotNull(account);
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
// Price override of $0
final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
overrides.add(new DefaultPlanPhasePriceOverride("blowdart-monthly-notrial-evergreen", account.getCurrency(), null, BigDecimal.ZERO, ImmutableList.<UsagePriceOverride>of()));
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
// BP creation : Will set Account BCD to the first (DateOfFirstRecurringNonZeroCharge is the subscription start date in this case)
final UUID baseEntitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec, null, null, overrides), "bundleExternalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Entitlement baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlementId, callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.RECURRING, BigDecimal.ZERO));
// 2016-4-15
clock.addDays(14);
// Set next BCD to be the 15
busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE);
subscriptionBaseInternalApi.updateBCD(baseEntitlement.getId(), 15, null, internalCallContext);
assertListenerStatus();
// Re-alignment invoice
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 4, 15), InvoiceItemType.RECURRING, BigDecimal.ZERO), new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 15), new LocalDate(2016, 5, 15), InvoiceItemType.RECURRING, BigDecimal.ZERO));
// Change to the paying plan (alignment is CHANGE_OF_PLAN: we end up in TRIAL)
final PlanPhaseSpecifier specWithTrial = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "trial", null);
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE);
baseEntitlement.changePlanOverrideBillingPolicy(new DefaultEntitlementSpecifier(specWithTrial), clock.getUTCToday(), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// Trial invoice
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 15), null, InvoiceItemType.FIXED, BigDecimal.ZERO));
// Verify next month (extra null invoice because of the original notification set on the 1st)
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
// First paying invoice
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 15), new LocalDate(2016, 6, 15), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
// Verify next month
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 5, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 15), new LocalDate(2016, 7, 15), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
}
use of org.killbill.billing.catalog.api.UsagePriceOverride in project killbill by killbill.
the class TestIntegrationInvoice method testIntegrationWithRecurringFreePlan.
@Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/783")
public void testIntegrationWithRecurringFreePlan() throws Exception {
final DateTime initialCreationDate = new DateTime(2017, 1, 1, 0, 0, 0, 0, testTimeZone);
// set clock to the initial start date
clock.setTime(initialCreationDate);
final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(1));
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", BillingPeriod.MONTHLY, "notrial", null);
// Price override of $0
final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
overrides.add(new DefaultPlanPhasePriceOverride("blowdart-monthly-notrial-evergreen", account.getCurrency(), null, BigDecimal.ZERO, ImmutableList.<UsagePriceOverride>of()));
busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec, null, null, overrides), "bundleExternalKey", null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, callContext);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 1, 1), new LocalDate(2017, 2, 1), InvoiceItemType.RECURRING, BigDecimal.ZERO));
// 2017-02-01
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 2, 1), new LocalDate(2017, 3, 1), InvoiceItemType.RECURRING, BigDecimal.ZERO));
// Do the change mid-month so the repair triggers the bug in https://github.com/killbill/killbill/issues/783
entitlement.changePlanWithDate(new DefaultEntitlementSpecifier(spec), new LocalDate("2017-02-15"), ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
// 2017-02-15
busHandler.pushExpectedEvents(NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(15);
assertListenerStatus();
// Note: no repair
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 2, 1), new LocalDate(2017, 3, 1), InvoiceItemType.RECURRING, BigDecimal.ZERO));
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 2, 1), new LocalDate(2017, 2, 15), InvoiceItemType.RECURRING, BigDecimal.ZERO), new ExpectedInvoiceItemCheck(new LocalDate(2017, 2, 15), new LocalDate(2017, 3, 1), InvoiceItemType.RECURRING, new BigDecimal("14.98")));
// 2017-03-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(15);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 3, 1), new LocalDate(2017, 4, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
// 2017-04-01
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 5, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2017, 4, 1), new LocalDate(2017, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
}
use of org.killbill.billing.catalog.api.UsagePriceOverride in project killbill by killbill.
the class SubscriptionResourceHelpers method buildUsagePrices.
private static void buildUsagePrices(final Currency currency, final PhasePriceJson input, final Collection<UsagePriceOverride> usagePrices) {
for (final UsagePriceJson usageOverrideJson : input.getUsagePrices()) {
final List<TierPriceOverride> tierPriceOverrides = new LinkedList<TierPriceOverride>();
for (final TierPriceJson tierPriceJson : usageOverrideJson.getTierPrices()) {
final List<TieredBlockPriceOverride> blockPriceOverrides = new LinkedList<TieredBlockPriceOverride>();
for (final BlockPriceJson block : tierPriceJson.getBlockPrices()) {
blockPriceOverrides.add(new TieredBlockPriceOverride() {
@Override
public String getUnitName() {
return block.getUnitName();
}
@Override
public Double getSize() {
return block.getSize();
}
@Override
public BigDecimal getPrice() {
return block.getPrice();
}
@Override
public Currency getCurrency() {
return currency;
}
@Override
public Double getMax() {
return block.getMax();
}
});
}
tierPriceOverrides.add(new TierPriceOverride() {
@Override
public List<TieredBlockPriceOverride> getTieredBlockPriceOverrides() {
return blockPriceOverrides;
}
});
}
usagePrices.add(new UsagePriceOverride() {
@Override
public String getName() {
return usageOverrideJson.getUsageName();
}
@Override
public UsageType getUsageType() {
return usageOverrideJson.getUsageType();
}
@Override
public List<TierPriceOverride> getTierPriceOverrides() {
return tierPriceOverrides;
}
});
}
}
Aggregations