Search in sources :

Example 11 with SubscriptionBaseBundle

use of org.killbill.billing.subscription.api.user.SubscriptionBaseBundle in project killbill by killbill.

the class DefaultSubscriptionApi method getSubscriptionBundlesForAccount.

private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
    final InternalTenantContext internalTenantContextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
    // Retrieve entitlements
    final AccountEntitlements accountEntitlements;
    try {
        accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccount(internalTenantContextWithValidAccountRecordId);
    } catch (final EntitlementApiException e) {
        throw new SubscriptionApiException(e);
    }
    // Build subscriptions
    final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
    // Build subscription bundles
    final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
    for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
        final List<Subscription> subscriptionsForBundle = subscriptionsPerBundle.get(bundleId);
        final String externalKey = subscriptionsForBundle.get(0).getExternalKey();
        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountId, bundleId, externalKey, accountEntitlements.getEntitlements().get(bundleId), internalTenantContextWithValidAccountRecordId);
        final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
        final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId, accountId, externalKey, subscriptionsForBundle, timeline, baseBundle.getOriginalCreatedDate(), baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
        bundles.add(subscriptionBundle);
    }
    // Sort the results for predictability
    return Ordering.<SubscriptionBundle>from(SUBSCRIPTION_BUNDLE_COMPARATOR).sortedCopy(bundles);
}
Also used : LinkedList(java.util.LinkedList) AccountEntitlements(org.killbill.billing.entitlement.AccountEntitlements) InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) UUID(java.util.UUID)

Example 12 with SubscriptionBaseBundle

use of org.killbill.billing.subscription.api.user.SubscriptionBaseBundle in project killbill by killbill.

the class DefaultSubscriptionApi method getSubscriptionBundlesForExternalKey.

@Override
public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
    final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContextWithoutAccountRecordId(context);
    final List<SubscriptionBaseBundle> baseBundles = subscriptionBaseInternalApi.getBundlesForKey(externalKey, internalContext);
    final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
    for (final SubscriptionBaseBundle cur : baseBundles) {
        final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
        result.add(bundle);
    }
    // Sorting by createdDate will likely place the active bundle last, but this is the same ordering we already use for getSubscriptionBundlesForAccount
    return Ordering.from(SUBSCRIPTION_BUNDLE_COMPARATOR).sortedCopy(result);
}
Also used : InternalTenantContext(org.killbill.billing.callcontext.InternalTenantContext) ArrayList(java.util.ArrayList) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle)

Example 13 with SubscriptionBaseBundle

use of org.killbill.billing.subscription.api.user.SubscriptionBaseBundle in project killbill by killbill.

the class EventsStreamBuilder method buildForAccount.

// Special signature for ProxyBlockingStateDao to save a DAO call
public AccountEventsStreams buildForAccount(final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
    // Retrieve the account
    final ImmutableAccountData account;
    try {
        account = accountInternalApi.getImmutableAccountDataByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
    } catch (AccountApiException e) {
        throw new EntitlementApiException(e);
    }
    if (subscriptions.isEmpty()) {
        // Bail early
        return new DefaultAccountEventsStreams(account);
    }
    // Retrieve the bundles
    final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(account.getId(), internalTenantContext);
    // Map bundle id -> bundles
    final Map<UUID, SubscriptionBaseBundle> bundlesPerId = new HashMap<UUID, SubscriptionBaseBundle>();
    for (final SubscriptionBaseBundle bundle : bundles) {
        bundlesPerId.put(bundle.getId(), bundle);
    }
    // Retrieve the blocking states
    final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext);
    // Optimization: build lookup tables for blocking states states
    final Collection<BlockingState> accountBlockingStates = new LinkedList<BlockingState>();
    final Map<UUID, List<BlockingState>> blockingStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
    final Map<UUID, List<BlockingState>> blockingStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
    for (final BlockingState blockingState : blockingStatesForAccount) {
        if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
            if (blockingStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
                blockingStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
            }
            blockingStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
        } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
            if (blockingStatesPerBundle.get(blockingState.getBlockedId()) == null) {
                blockingStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
            }
            blockingStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
        } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) && account.getId().equals(blockingState.getBlockedId())) {
            accountBlockingStates.add(blockingState);
        }
    }
    // Build the EventsStream objects
    final Map<UUID, Integer> bcdCache = new HashMap<UUID, Integer>();
    final Map<UUID, Collection<EventsStream>> entitlementsPerBundle = new HashMap<UUID, Collection<EventsStream>>();
    for (final UUID bundleId : subscriptions.keySet()) {
        final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
        final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
        final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
        final List<BlockingState> bundleBlockingStates = MoreObjects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
        if (entitlementsPerBundle.get(bundleId) == null) {
            entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
        }
        for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
            final List<BlockingState> subscriptionBlockingStatesOnDisk = MoreObjects.firstNonNull(blockingStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
            // We cannot always use blockingStatesForAccount here: we need subscriptionBlockingStates to contain the events not on disk when building an EventsStream
            // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
            // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
            // needed, i.e. if this EventStream is for a standalone or a base subscription
            final List<BlockingState> subscriptionBlockingStates;
            if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
                subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
            } else {
                subscriptionBlockingStates = blockingStateDao.getBlockingHistory(subscriptionBlockingStatesOnDisk, blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
            }
            // Merge the BlockingStates
            final Collection<BlockingState> blockingStateSet = new LinkedHashSet<BlockingState>(accountBlockingStates);
            blockingStateSet.addAll(bundleBlockingStates);
            blockingStateSet.addAll(subscriptionBlockingStates);
            final List<BlockingState> blockingStates = ProxyBlockingStateDao.sortedCopy(blockingStateSet);
            final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, blockingStates, bcdCache, internalTenantContext);
            entitlementsPerBundle.get(bundleId).add(eventStream);
        }
    }
    return new DefaultAccountEventsStreams(account, bundles, entitlementsPerBundle);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) HashMap(java.util.HashMap) EventsStream(org.killbill.billing.entitlement.EventsStream) BlockingState(org.killbill.billing.entitlement.api.BlockingState) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) AccountApiException(org.killbill.billing.account.api.AccountApiException) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) DefaultAccountEventsStreams(org.killbill.billing.entitlement.api.svcs.DefaultAccountEventsStreams) UUID(java.util.UUID) EntitlementApiException(org.killbill.billing.entitlement.api.EntitlementApiException) LinkedList(java.util.LinkedList) Collection(java.util.Collection)

Example 14 with SubscriptionBaseBundle

use of org.killbill.billing.subscription.api.user.SubscriptionBaseBundle in project killbill by killbill.

the class DefaultInternalBillingApi method getBillingEventsForAccountAndUpdateAccountBCD.

@Override
public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
    final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(true, true, context);
    // Check to see if billing is off for the account
    final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
    final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
    final Set<UUID> skippedSubscriptions = new HashSet<UUID>();
    final DefaultBillingEventSet result;
    if (found_AUTO_INVOICING_OFF) {
        // billing is off, we are done
        result = new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode());
    } else {
        final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
        final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
        result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode());
        addBillingEventsForBundles(bundles, account, dryRunArguments, context, result, skippedSubscriptions);
    }
    if (result.isEmpty()) {
        log.info("No billing event for accountId='{}'", accountId);
        return result;
    }
    // Pretty-print the events, before and after the blocking calculator does its magic
    final StringBuilder logStringBuilder = new StringBuilder("Computed billing events for accountId='").append(accountId).append("'");
    eventsToString(logStringBuilder, result);
    if (blockCalculator.insertBlockingEvents(result, skippedSubscriptions, context)) {
        logStringBuilder.append("\nBilling Events After Blocking");
        eventsToString(logStringBuilder, result);
    }
    log.info(logStringBuilder.toString());
    return result;
}
Also used : ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) Tag(org.killbill.billing.util.tag.Tag) UUID(java.util.UUID) StaticCatalog(org.killbill.billing.catalog.api.StaticCatalog) HashSet(java.util.HashSet)

Example 15 with SubscriptionBaseBundle

use of org.killbill.billing.subscription.api.user.SubscriptionBaseBundle 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 DateTime effectiveTransferDate = transferDate == null ? clock.getUTCNow() : transferDate;
        if (effectiveTransferDate.isAfter(clock.getUTCNow())) {
            // (subscription always expects the first event to be in the past)
            throw new SubscriptionBaseTransferApiException(ErrorCode.SUB_TRANSFER_INVALID_EFF_DATE, effectiveTransferDate);
        }
        final List<SubscriptionBaseBundle> bundlesForAccountAndKey = dao.getSubscriptionBundlesForAccountAndKey(sourceAccountId, bundleKey, fromInternalCallContext);
        final SubscriptionBaseBundle bundle = DefaultSubscriptionInternalApi.getActiveBundleForKeyNotException(bundlesForAccountAndKey, dao, clock, 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(), clock.getUTCNow(), clock.getUTCNow());
        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(), 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(), fromInternalCallContext);
            final List<SubscriptionBaseEvent> events = toEvents(existingEvents, defaultSubscriptionBase, effectiveTransferDate, fromInternalCallContext);
            final SubscriptionTransferData curData = new SubscriptionTransferData(defaultSubscriptionBase, events, null);
            subscriptionTransferDataList.add(curData);
        }
        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, 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) 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)

Aggregations

SubscriptionBaseBundle (org.killbill.billing.subscription.api.user.SubscriptionBaseBundle)26 DateTime (org.joda.time.DateTime)17 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)16 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)10 UUID (java.util.UUID)9 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)9 ArrayList (java.util.ArrayList)8 Test (org.testng.annotations.Test)8 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)7 BillingPeriod (org.killbill.billing.catalog.api.BillingPeriod)7 Plan (org.killbill.billing.catalog.api.Plan)7 ImmutableAccountData (org.killbill.billing.account.api.ImmutableAccountData)6 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)6 LinkedList (java.util.LinkedList)4 AccountApiException (org.killbill.billing.account.api.AccountApiException)4 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)4 WithEntitlementPlugin (org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin)4 EntitlementContext (org.killbill.billing.entitlement.plugin.api.EntitlementContext)4 DefaultSubscriptionBaseBundle (org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle)4 ImmutableList (com.google.common.collect.ImmutableList)3