Search in sources :

Example 1 with ApiEventCancel

use of org.killbill.billing.subscription.events.user.ApiEventCancel in project killbill by killbill.

the class DefaultSubscriptionDao method buildBundleSubscriptions.

private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription, @Nullable final Collection<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) throws CatalogApiException {
    if (input == null || input.isEmpty()) {
        return Collections.emptyList();
    }
    // Make sure BasePlan -- if exists-- is first
    Collections.sort(input, DefaultSubscriptionInternalApi.SUBSCRIPTIONS_COMPARATOR);
    final Collection<ApiEventChange> baseChangeEvents = new LinkedList<ApiEventChange>();
    ApiEventCancel baseCancellationEvent = null;
    final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
    for (final SubscriptionBase cur : input) {
        final List<SubscriptionBaseEvent> events = eventsForSubscription != null ? (List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) : getEventsForSubscription(cur.getId(), context);
        mergeDryRunEvents(cur.getId(), events, dryRunEvents);
        SubscriptionBase reloaded = createSubscriptionForInternalUse(cur, events, context);
        switch(cur.getCategory()) {
            case BASE:
                for (final SubscriptionBaseEvent event : events) {
                    if (!event.isActive()) {
                        continue;
                    } else if (event instanceof ApiEventCancel) {
                        baseCancellationEvent = (ApiEventCancel) event;
                        break;
                    } else if (event instanceof ApiEventChange) {
                        // Need to track all changes, see https://github.com/killbill/killbill/issues/268
                        baseChangeEvents.add((ApiEventChange) event);
                    }
                }
                break;
            case ADD_ON:
                final Plan targetAddOnPlan = reloaded.getCurrentPlan();
                if (targetAddOnPlan == null || reloaded.getFutureEndDate() != null) {
                    // triggers another cancellation before?
                    break;
                }
                SubscriptionBaseEvent baseTriggerEventForAddOnCancellation = baseCancellationEvent;
                for (final ApiEventChange baseChangeEvent : baseChangeEvents) {
                    final String baseProductName = baseChangeEvent.getEventPlan();
                    if ((!addonUtils.isAddonAvailableFromPlanName(baseProductName, targetAddOnPlan, baseChangeEvent.getEffectiveDate(), context)) || (addonUtils.isAddonIncludedFromPlanName(baseProductName, targetAddOnPlan, baseChangeEvent.getEffectiveDate(), context))) {
                        if (baseTriggerEventForAddOnCancellation != null) {
                            if (baseTriggerEventForAddOnCancellation.getEffectiveDate().isAfter(baseChangeEvent.getEffectiveDate())) {
                                baseTriggerEventForAddOnCancellation = baseChangeEvent;
                            }
                        } else {
                            baseTriggerEventForAddOnCancellation = baseChangeEvent;
                        }
                    }
                }
                if (baseTriggerEventForAddOnCancellation != null) {
                    final DateTime now = clock.getUTCNow();
                    final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder().setSubscriptionId(reloaded.getId()).setEffectiveDate(baseTriggerEventForAddOnCancellation.getEffectiveDate()).setCreatedDate(baseTriggerEventForAddOnCancellation.getCreatedDate()).setFromDisk(false));
                    events.add(addOnCancelEvent);
                    // Finally reload subscription with full set of events
                    reloaded = createSubscriptionForInternalUse(cur, events, context);
                }
                break;
            default:
                break;
        }
        result.add(reloaded);
    }
    return result;
}
Also used : ArrayList(java.util.ArrayList) ApiEventCancel(org.killbill.billing.subscription.events.user.ApiEventCancel) Plan(org.killbill.billing.catalog.api.Plan) LinkedList(java.util.LinkedList) DateTime(org.joda.time.DateTime) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) ApiEventChange(org.killbill.billing.subscription.events.user.ApiEventChange)

Example 2 with ApiEventCancel

use of org.killbill.billing.subscription.events.user.ApiEventCancel in project killbill by killbill.

the class DefaultSubscriptionBaseTransferApi method transferBundle.

@Override
public SubscriptionBaseBundle transferBundle(final UUID sourceAccountId, final UUID destAccountId, final String bundleKey, final DateTime transferDate, final boolean transferAddOn, final boolean cancelImmediately, final CallContext context) throws SubscriptionBaseTransferApiException {
    final InternalCallContext fromInternalCallContext = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
    final InternalCallContext toInternalCallContext = internalCallContextFactory.createInternalCallContext(destAccountId, context);
    try {
        final SubscriptionCatalog catalog = subscriptionCatalogApi.getFullCatalog(fromInternalCallContext);
        final DateTime effectiveTransferDate = transferDate == null ? context.getCreatedDate() : transferDate;
        if (effectiveTransferDate.isAfter(context.getCreatedDate())) {
            // (subscription always expects the first event to be in the past)
            throw new SubscriptionBaseTransferApiException(ErrorCode.SUB_TRANSFER_INVALID_EFF_DATE, effectiveTransferDate);
        }
        final SubscriptionBaseBundle bundle = subscriptionBaseInternalApi.getActiveBundleForKey(catalog.getCatalog(), bundleKey, fromInternalCallContext);
        if (bundle == null) {
            throw new SubscriptionBaseTransferApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleKey);
        }
        // Get the bundle timeline for the old account
        final BundleBaseTimeline bundleBaseTimeline = timelineApi.getBundleTimeline(bundle, context);
        final DefaultSubscriptionBaseBundle subscriptionBundleData = new DefaultSubscriptionBaseBundle(bundleKey, destAccountId, effectiveTransferDate, bundle.getOriginalCreatedDate(), context.getCreatedDate(), context.getCreatedDate());
        final List<SubscriptionTransferData> subscriptionTransferDataList = new LinkedList<SubscriptionTransferData>();
        final List<TransferCancelData> transferCancelDataList = new LinkedList<TransferCancelData>();
        DateTime bundleStartdate = null;
        for (final SubscriptionBaseTimeline cur : bundleBaseTimeline.getSubscriptions()) {
            final DefaultSubscriptionBase oldSubscription = (DefaultSubscriptionBase) dao.getSubscriptionFromId(cur.getId(), catalog, fromInternalCallContext);
            // Skip already cancelled subscriptions
            if (oldSubscription.getState() == EntitlementState.CANCELLED) {
                continue;
            }
            final List<ExistingEvent> existingEvents = cur.getExistingEvents();
            final ProductCategory productCategory = existingEvents.get(0).getProductCategory();
            // on base plan cancellations, even though we don't support un-transfer today)
            if (productCategory != ProductCategory.ADD_ON || cancelImmediately) {
                // Create the cancelWithRequestedDate event on effectiveCancelDate
                final DateTime effectiveCancelDate = !cancelImmediately && oldSubscription.getChargedThroughDate() != null && effectiveTransferDate.isBefore(oldSubscription.getChargedThroughDate()) ? oldSubscription.getChargedThroughDate() : effectiveTransferDate;
                final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder().setSubscriptionId(cur.getId()).setEffectiveDate(effectiveCancelDate).setFromDisk(true));
                final TransferCancelData cancelData = new TransferCancelData(oldSubscription, cancelEvent);
                transferCancelDataList.add(cancelData);
            }
            if (productCategory == ProductCategory.ADD_ON && !transferAddOn) {
                continue;
            }
            // We Align with the original subscription
            final DateTime subscriptionAlignStartDate = oldSubscription.getAlignStartDate();
            if (bundleStartdate == null) {
                bundleStartdate = oldSubscription.getStartDate();
            }
            // Create the new subscription for the new bundle on the new account
            final DefaultSubscriptionBase defaultSubscriptionBase = createSubscriptionForApiUse(new SubscriptionBuilder().setId(UUIDs.randomUUID()).setBundleId(subscriptionBundleData.getId()).setBundleExternalKey(subscriptionBundleData.getExternalKey()).setCategory(productCategory).setBundleStartDate(effectiveTransferDate).setAlignStartDate(subscriptionAlignStartDate), ImmutableList.<SubscriptionBaseEvent>of(), catalog, fromInternalCallContext);
            final List<SubscriptionBaseEvent> events = toEvents(existingEvents, defaultSubscriptionBase, effectiveTransferDate, fromInternalCallContext);
            final SubscriptionTransferData curData = new SubscriptionTransferData(defaultSubscriptionBase, events, null);
            subscriptionTransferDataList.add(curData);
        }
        final BundleTransferData bundleTransferData = new BundleTransferData(subscriptionBundleData, subscriptionTransferDataList);
        // Atomically cancelWithRequestedDate all subscription on old account and create new bundle, subscriptions, events for new account
        dao.transfer(sourceAccountId, destAccountId, bundleTransferData, transferCancelDataList, catalog, fromInternalCallContext, toInternalCallContext);
        return bundleTransferData.getData();
    } catch (SubscriptionBaseRepairException e) {
        throw new SubscriptionBaseTransferApiException(e);
    } catch (CatalogApiException e) {
        throw new SubscriptionBaseTransferApiException(e);
    }
}
Also used : InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) SubscriptionBaseTimeline(org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimeline) SubscriptionCatalog(org.killbill.billing.subscription.catalog.SubscriptionCatalog) DateTime(org.joda.time.DateTime) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) DefaultSubscriptionBaseBundle(org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) ExistingEvent(org.killbill.billing.subscription.api.timeline.SubscriptionBaseTimeline.ExistingEvent) BundleBaseTimeline(org.killbill.billing.subscription.api.timeline.BundleBaseTimeline) SubscriptionBaseRepairException(org.killbill.billing.subscription.api.timeline.SubscriptionBaseRepairException) DefaultSubscriptionBaseBundle(org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle) ApiEventCancel(org.killbill.billing.subscription.events.user.ApiEventCancel) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) LinkedList(java.util.LinkedList) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) ProductCategory(org.killbill.billing.catalog.api.ProductCategory) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent)

Example 3 with ApiEventCancel

use of org.killbill.billing.subscription.events.user.ApiEventCancel in project killbill by killbill.

the class TestSubscriptionDao method createSubscription.

private List<SubscriptionBaseEvent> createSubscription(final SubscriptionBaseBundle bundle, final String externalKey, final DateTime startDate, final DateTime cancelDate) {
    final SubscriptionBuilder builder = new SubscriptionBuilder().setId(UUIDs.randomUUID()).setBundleId(bundle.getId()).setBundleExternalKey(bundle.getExternalKey()).setCategory(ProductCategory.BASE).setBundleStartDate(startDate).setAlignStartDate(startDate).setExternalKey(externalKey).setMigrated(false);
    final ApiEventBuilder createBuilder = new ApiEventBuilder().setSubscriptionId(builder.getId()).setEventPlan("shotgun-monthly").setEventPlanPhase("shotgun-monthly-trial").setEventPriceList(DefaultPriceListSet.DEFAULT_PRICELIST_NAME).setEffectiveDate(startDate).setFromDisk(true);
    final SubscriptionBaseEvent creationEvent = new ApiEventCreate(createBuilder);
    final ApiEventBuilder cancelBuilder = cancelDate != null ? new ApiEventBuilder().setSubscriptionId(builder.getId()).setEffectiveDate(cancelDate).setFromDisk(true) : null;
    final SubscriptionBaseEvent cancelEvent = cancelBuilder != null ? new ApiEventCancel(cancelBuilder) : null;
    final DefaultSubscriptionBase subscription = new DefaultSubscriptionBase(builder);
    final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = new DefaultSubscriptionBaseWithAddOns(bundle, ImmutableList.<SubscriptionBase>of(subscription));
    testListener.pushExpectedEvents(NextEvent.CREATE);
    final ImmutableList<SubscriptionBaseEvent> events = cancelEvent != null ? ImmutableList.<SubscriptionBaseEvent>of(creationEvent, cancelEvent) : ImmutableList.<SubscriptionBaseEvent>of(creationEvent);
    final List<SubscriptionBaseEvent> result = dao.createSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOns>of(subscriptionBaseWithAddOns), ImmutableMap.<UUID, List<SubscriptionBaseEvent>>of(subscription.getId(), events), catalog, internalCallContext);
    assertListenerStatus();
    return result;
}
Also used : ApiEventCreate(org.killbill.billing.subscription.events.user.ApiEventCreate) DefaultSubscriptionBaseWithAddOns(org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseWithAddOns) SubscriptionBaseWithAddOns(org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) ApiEventCancel(org.killbill.billing.subscription.events.user.ApiEventCancel) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DefaultSubscriptionBaseWithAddOns(org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseWithAddOns)

Example 4 with ApiEventCancel

use of org.killbill.billing.subscription.events.user.ApiEventCancel in project killbill by killbill.

the class TestDefaultSubscriptionBase method testCancelSOT.

@Test(groups = "fast")
public void testCancelSOT() throws Exception {
    final DateTime startDate = new DateTime(2012, 5, 1, 0, 0, DateTimeZone.UTC);
    final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(startDate));
    final UUID subscriptionId = UUID.randomUUID();
    final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
    inputEvents.add(new ApiEventCreate(new ApiEventBuilder().setApiEventType(CREATE).setEventPlan("laser-scope-monthly").setEventPlanPhase("laser-scope-monthly-discount").setEventPriceList("DEFAULT").setFromDisk(true).setUuid(UUID.randomUUID()).setSubscriptionId(subscriptionId).setCreatedDate(startDate).setUpdatedDate(startDate).setEffectiveDate(startDate).setTotalOrdering(3).setActive(true)));
    inputEvents.add(new ApiEventCancel(new ApiEventBuilder().setApiEventType(ApiEventType.CANCEL).setEventPlan(null).setEventPlanPhase(null).setEventPriceList(null).setFromDisk(false).setUuid(UUID.randomUUID()).setSubscriptionId(subscriptionId).setCreatedDate(startDate).setUpdatedDate(null).setEffectiveDate(startDate).setTotalOrdering(// In-memory event
    0).setActive(true)));
    subscriptionBase.rebuildTransitions(inputEvents, catalog);
    Assert.assertEquals(subscriptionBase.getAllTransitions().size(), 2);
    Assert.assertNull(subscriptionBase.getAllTransitions().get(0).getPreviousState());
    Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getNextState(), EntitlementState.ACTIVE);
    Assert.assertEquals(subscriptionBase.getAllTransitions().get(0).getEffectiveTransitionTime(), startDate);
    Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getPreviousState(), EntitlementState.ACTIVE);
    Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getNextState(), EntitlementState.CANCELLED);
    Assert.assertEquals(subscriptionBase.getAllTransitions().get(1).getEffectiveTransitionTime(), startDate);
}
Also used : ApiEventCreate(org.killbill.billing.subscription.events.user.ApiEventCreate) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) ApiEventCancel(org.killbill.billing.subscription.events.user.ApiEventCancel) UUID(java.util.UUID) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DateTime(org.joda.time.DateTime) LinkedList(java.util.LinkedList) Test(org.testng.annotations.Test)

Example 5 with ApiEventCancel

use of org.killbill.billing.subscription.events.user.ApiEventCancel in project killbill by killbill.

the class TestSubscriptionBillingEvents method testWithCancelation_After_EffSubDtV2.

@Test(groups = "fast")
public void testWithCancelation_After_EffSubDtV2() throws Exception {
    final DateTime createDate = new DateTime(2011, 1, 2, 0, 0, DateTimeZone.UTC);
    final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(new SubscriptionBuilder().setAlignStartDate(createDate));
    final UUID subscriptionId = UUID.randomUUID();
    final List<SubscriptionBaseEvent> inputEvents = new LinkedList<SubscriptionBaseEvent>();
    inputEvents.add(new ApiEventCreate(new ApiEventBuilder().setApiEventType(CREATE).setEventPlan("gold-monthly").setEventPlanPhase("gold-monthly-trial").setEventPriceList("DEFAULT").setFromDisk(true).setUuid(UUID.randomUUID()).setSubscriptionId(subscriptionId).setCreatedDate(createDate).setUpdatedDate(createDate).setEffectiveDate(createDate).setTotalOrdering(1).setActive(true)));
    final DateTime evergreenPhaseDate = createDate.plusDays(30);
    inputEvents.add(new PhaseEventData(new PhaseEventBuilder().setPhaseName("gold-monthly-evergreen").setUuid(UUID.randomUUID()).setSubscriptionId(subscriptionId).setCreatedDate(evergreenPhaseDate).setUpdatedDate(evergreenPhaseDate).setEffectiveDate(evergreenPhaseDate).setTotalOrdering(2).setActive(true)));
    final DateTime cancelDate = new DateTime(2011, 2, 15, 0, 0, DateTimeZone.UTC);
    inputEvents.add(new ApiEventCancel(new ApiEventBuilder().setApiEventType(ApiEventType.CANCEL).setEventPlan(null).setEventPlanPhase(null).setEventPriceList(null).setFromDisk(true).setUuid(UUID.randomUUID()).setSubscriptionId(subscriptionId).setCreatedDate(createDate).setUpdatedDate(null).setEffectiveDate(cancelDate).setTotalOrdering(3).setActive(true)));
    subscriptionBase.rebuildTransitions(inputEvents, catalog);
    final List<SubscriptionBillingEvent> result = subscriptionBase.getSubscriptionBillingEvents(catalog.getCatalog());
    Assert.assertEquals(result.size(), 5);
    Assert.assertEquals(result.get(0).getType(), SubscriptionBaseTransitionType.CREATE);
    Assert.assertEquals(result.get(0).getEffectiveDate().compareTo(createDate), 0);
    Assert.assertEquals(result.get(0).getPlan().getName().compareTo("gold-monthly"), 0);
    Assert.assertEquals(toDateTime(result.get(0).getPlan().getCatalog().getEffectiveDate()).compareTo(EFF_V1), 0);
    Assert.assertEquals(result.get(1).getType(), SubscriptionBaseTransitionType.PHASE);
    Assert.assertEquals(result.get(1).getEffectiveDate().compareTo(evergreenPhaseDate), 0);
    Assert.assertEquals(result.get(1).getPlan().getName().compareTo("gold-monthly"), 0);
    Assert.assertEquals(toDateTime(result.get(1).getPlan().getCatalog().getEffectiveDate()).compareTo(EFF_V1), 0);
    // Catalog change event for EFF_SUB_DT_V2
    Assert.assertEquals(result.get(2).getType(), SubscriptionBaseTransitionType.CHANGE);
    Assert.assertEquals(result.get(2).getEffectiveDate().compareTo(EFF_SUB_DT_V2), 0);
    Assert.assertEquals(result.get(2).getPlan().getName().compareTo("gold-monthly"), 0);
    Assert.assertEquals(toDateTime(result.get(2).getPlan().getCatalog().getEffectiveDate()).compareTo(EFF_V2), 0);
    // Catalog change event for EFF_SUB_DT_V3
    Assert.assertEquals(result.get(3).getType(), SubscriptionBaseTransitionType.CHANGE);
    Assert.assertEquals(result.get(3).getEffectiveDate().compareTo(EFF_SUB_DT_V3), 0);
    Assert.assertEquals(result.get(3).getPlan().getName().compareTo("gold-monthly"), 0);
    Assert.assertEquals(toDateTime(result.get(3).getPlan().getCatalog().getEffectiveDate()).compareTo(EFF_V3), 0);
    // Cancel event
    Assert.assertEquals(result.get(4).getType(), SubscriptionBaseTransitionType.CANCEL);
    Assert.assertEquals(result.get(4).getEffectiveDate().compareTo(cancelDate), 0);
    Assert.assertNull(result.get(4).getPlan());
// Nothing after cancel -> we correctly discarded subsequent catalog update events after the cancel
}
Also used : ApiEventCreate(org.killbill.billing.subscription.events.user.ApiEventCreate) ApiEventCancel(org.killbill.billing.subscription.events.user.ApiEventCancel) DateTime(org.joda.time.DateTime) LinkedList(java.util.LinkedList) PhaseEventData(org.killbill.billing.subscription.events.phase.PhaseEventData) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) UUID(java.util.UUID) PhaseEventBuilder(org.killbill.billing.subscription.events.phase.PhaseEventBuilder) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) Test(org.testng.annotations.Test)

Aggregations

SubscriptionBaseEvent (org.killbill.billing.subscription.events.SubscriptionBaseEvent)12 ApiEventBuilder (org.killbill.billing.subscription.events.user.ApiEventBuilder)12 ApiEventCancel (org.killbill.billing.subscription.events.user.ApiEventCancel)12 LinkedList (java.util.LinkedList)7 ArrayList (java.util.ArrayList)6 DateTime (org.joda.time.DateTime)6 ApiEventCreate (org.killbill.billing.subscription.events.user.ApiEventCreate)5 UUID (java.util.UUID)4 Plan (org.killbill.billing.catalog.api.Plan)4 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)4 Test (org.testng.annotations.Test)4 Product (org.killbill.billing.catalog.api.Product)3 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)3 PhaseEventBuilder (org.killbill.billing.subscription.events.phase.PhaseEventBuilder)3 PhaseEventData (org.killbill.billing.subscription.events.phase.PhaseEventData)3 SubscriptionBuilder (org.killbill.billing.subscription.api.user.SubscriptionBuilder)2 ApiEventChange (org.killbill.billing.subscription.events.user.ApiEventChange)2 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)1 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)1 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)1