Search in sources :

Example 76 with SubscriptionBase

use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.

the class DefaultEntitlementApi method transferEntitlementsOverrideBillingPolicy.

@Override
public UUID transferEntitlementsOverrideBillingPolicy(final UUID sourceAccountId, final UUID destAccountId, final String bundleExternalKey, @Nullable final LocalDate effectiveDate, final BillingActionPolicy billingPolicy, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
    logTransferEntitlement(log, sourceAccountId, destAccountId, bundleExternalKey, effectiveDate, billingPolicy);
    final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(null, bundleExternalKey, new ArrayList<EntitlementSpecifier>(), effectiveDate, effectiveDate, false);
    final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
    baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
    final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.TRANSFER_BUNDLE, sourceAccountId, destAccountId, baseEntitlementWithAddOnsSpecifierList, billingPolicy, properties, context);
    final WithEntitlementPlugin<UUID> transferWithPlugin = new WithEntitlementPlugin<UUID>() {

        @Override
        public UUID doCall(final EntitlementApi entitlementApi, final DefaultEntitlementContext updatedPluginContext) throws EntitlementApiException {
            final boolean cancelImm;
            switch(billingPolicy) {
                case IMMEDIATE:
                    cancelImm = true;
                    break;
                case END_OF_TERM:
                    cancelImm = false;
                    break;
                default:
                    throw new RuntimeException("Unexpected billing policy " + billingPolicy);
            }
            final InternalCallContext contextWithSourceAccountRecordId = internalCallContextFactory.createInternalCallContext(sourceAccountId, context);
            try {
                final UUID activeSubscriptionIdForKey = entitlementUtils.getFirstActiveSubscriptionIdForKeyOrNull(bundleExternalKey, contextWithSourceAccountRecordId);
                final UUID bundleId = activeSubscriptionIdForKey != null ? subscriptionBaseInternalApi.getBundleIdFromSubscriptionId(activeSubscriptionIdForKey, contextWithSourceAccountRecordId) : null;
                final UUID baseBundleAccountId = bundleId != null ? subscriptionBaseInternalApi.getAccountIdFromBundleId(bundleId, contextWithSourceAccountRecordId) : null;
                if (baseBundleAccountId == null || !baseBundleAccountId.equals(sourceAccountId)) {
                    throw new EntitlementApiException(new SubscriptionBaseApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, bundleExternalKey));
                }
                final DefaultBaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
                final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getBillingEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithSourceAccountRecordId);
                final SubscriptionBaseBundle newBundle = subscriptionBaseTransferApi.transferBundle(sourceAccountId, destAccountId, bundleExternalKey, requestedDate, true, cancelImm, context);
                // Update the context for plugins
                baseEntitlementWithAddOnsSpecifier.setBundleId(newBundle.getId());
                baseEntitlementWithAddOnsSpecifier.setBundleExternalKey(newBundle.getExternalKey());
                final Map<BlockingState, UUID> blockingStates = new HashMap<BlockingState, UUID>();
                // Note that there is no un-transfer at the moment, so we effectively add a blocking state on disk for all subscriptions
                for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(bundleId, null, contextWithSourceAccountRecordId)) {
                    final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, false, requestedDate);
                    blockingStates.put(blockingState, subscriptionBase.getBundleId());
                }
                entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithSourceAccountRecordId);
                // Add blocking events for transferred subscriptions..
                final InternalCallContext contextWithDestAccountRecordId = internalCallContextFactory.createInternalCallContext(destAccountId, context);
                blockingStates.clear();
                final DateTime entitlementRequestedDate = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithDestAccountRecordId);
                for (final SubscriptionBase subscriptionBase : subscriptionBaseInternalApi.getSubscriptionsForBundle(newBundle.getId(), null, contextWithDestAccountRecordId)) {
                    final BlockingState newBlockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, entitlementRequestedDate);
                    blockingStates.put(newBlockingState, subscriptionBase.getBundleId());
                }
                entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStates, contextWithDestAccountRecordId);
                return newBundle.getId();
            } catch (final SubscriptionBaseTransferApiException e) {
                throw new EntitlementApiException(e);
            } catch (final SubscriptionBaseApiException e) {
                throw new EntitlementApiException(e);
            }
        }
    };
    return pluginExecution.executeWithPlugin(transferWithPlugin, pluginContext);
}
Also used : WithEntitlementPlugin(org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) DateTime(org.joda.time.DateTime) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) UUID(java.util.UUID) EntitlementContext(org.killbill.billing.entitlement.plugin.api.EntitlementContext) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) SubscriptionBaseTransferApiException(org.killbill.billing.subscription.api.transfer.SubscriptionBaseTransferApiException)

Example 77 with SubscriptionBase

use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.

the class DefaultEntitlementApi method createEntitlementEvents.

private List<UUID> createEntitlementEvents(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins, final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns, final CallContext updatedPluginContext, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
    final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
    final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
    int i = 0;
    for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
        final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
        for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
            final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), false, false, false, dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
            blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
            createdSubscriptionIds.add(subscriptionBase.getId());
        }
    }
    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
    return createdSubscriptionIds;
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) HashMap(java.util.HashMap) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) UUID(java.util.UUID) LinkedList(java.util.LinkedList) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState)

Example 78 with SubscriptionBase

use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.

the class DefaultEventsStream method computeAddonsBlockingStatesForSubscriptionBaseEvent.

private Collection<BlockingState> computeAddonsBlockingStatesForSubscriptionBaseEvent(@Nullable final Product baseTransitionTriggerNextProduct, final DateTime blockingStateEffectiveDate) {
    if (baseSubscription == null || baseSubscription.getLastActivePlan() == null || !ProductCategory.BASE.equals(baseSubscription.getLastActivePlan().getProduct().getCategory())) {
        return ImmutableList.<BlockingState>of();
    }
    // Compute included and available addons for the new product
    final Collection<String> includedAddonsForProduct;
    final Collection<String> availableAddonsForProduct;
    if (baseTransitionTriggerNextProduct == null) {
        includedAddonsForProduct = ImmutableList.<String>of();
        availableAddonsForProduct = ImmutableList.<String>of();
    } else {
        includedAddonsForProduct = Collections2.<Product, String>transform(ImmutableSet.<Product>copyOf(baseTransitionTriggerNextProduct.getIncluded()), new Function<Product, String>() {

            @Override
            public String apply(final Product product) {
                return product.getName();
            }
        });
        availableAddonsForProduct = Collections2.<Product, String>transform(ImmutableSet.<Product>copyOf(baseTransitionTriggerNextProduct.getAvailable()), new Function<Product, String>() {

            @Override
            public String apply(final Product product) {
                return product.getName();
            }
        });
    }
    // Retrieve all add-ons to block for that base subscription
    final Collection<SubscriptionBase> futureBlockedAddons = Collections2.<SubscriptionBase>filter(allSubscriptionsForBundle, new Predicate<SubscriptionBase>() {

        @Override
        public boolean apply(final SubscriptionBase subscription) {
            final Plan lastActivePlan = subscription.getLastActivePlan();
            final boolean result = ProductCategory.ADD_ON.equals(subscription.getCategory()) && // Check the subscription started, if not we don't want it, and that way we avoid doing NPE a few lines below.
            lastActivePlan != null && // Check the entitlement for that add-on hasn't been cancelled yet
            getEntitlementCancellationEvent(subscription.getId()) == null && (// Base subscription cancelled
            baseTransitionTriggerNextProduct == null || (// Change plan - check which add-ons to cancel
            includedAddonsForProduct.contains(lastActivePlan.getProduct().getName()) || !availableAddonsForProduct.contains(subscription.getLastActivePlan().getProduct().getName())));
            return result;
        }
    });
    // Create the blocking states
    return Collections2.<SubscriptionBase, BlockingState>transform(futureBlockedAddons, new Function<SubscriptionBase, BlockingState>() {

        @Override
        public BlockingState apply(final SubscriptionBase input) {
            return new DefaultBlockingState(input.getId(), BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_CANCELLED, KILLBILL_SERVICES.ENTITLEMENT_SERVICE.getServiceName(), true, true, false, blockingStateEffectiveDate);
        }
    });
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) Function(com.google.common.base.Function) Product(org.killbill.billing.catalog.api.Product) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) BlockingState(org.killbill.billing.entitlement.api.BlockingState) Plan(org.killbill.billing.catalog.api.Plan) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState)

Example 79 with SubscriptionBase

use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.

the class EventsStreamBuilder method buildForEntitlement.

public EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle, final SubscriptionBase subscription, final Collection<SubscriptionBase> allSubscriptionsForBundle, final int accountBCD, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
    final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
    final ImmutableAccountData account;
    try {
        account = accountInternalApi.getImmutableAccountDataById(bundle.getAccountId(), internalTenantContext);
    } catch (final AccountApiException e) {
        throw new EntitlementApiException(e);
    }
    final VersionedCatalog catalog = getCatalog(internalTenantContext);
    // Retrieve the blocking states
    final ImmutableSet<UUID> blockingStateIds = baseSubscription != null ? ImmutableSet.of(account.getId(), bundle.getId(), baseSubscription.getId(), subscription.getId()) : ImmutableSet.of(account.getId(), bundle.getId(), subscription.getId());
    final List<BlockingState> blockingStatesForAccount = defaultBlockingStateDao.getByBlockingIds(blockingStateIds, internalTenantContext);
    final Map<UUID, Integer> bcdCache = new HashMap<UUID, Integer>();
    return buildForEntitlement(blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, accountBCD, bcdCache, catalog, internalTenantContext);
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) ImmutableAccountData(org.killbill.billing.account.api.ImmutableAccountData) VersionedCatalog(org.killbill.billing.catalog.api.VersionedCatalog) HashMap(java.util.HashMap) AccountApiException(org.killbill.billing.account.api.AccountApiException) EntitlementApiException(org.killbill.billing.entitlement.api.EntitlementApiException) BlockingState(org.killbill.billing.entitlement.api.BlockingState) UUID(java.util.UUID)

Example 80 with SubscriptionBase

use of org.killbill.billing.subscription.api.SubscriptionBase 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 VersionedCatalog catalog, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
    // Retrieve the account
    final ImmutableAccountData account;
    final int accountBCD;
    try {
        account = accountInternalApi.getImmutableAccountDataByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
        accountBCD = accountInternalApi.getBCD(internalTenantContext);
    } catch (final 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(catalog, 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>> eventsStreamPerBundle = new HashMap<UUID, Collection<EventsStream>>();
    final Map<UUID, Collection<SubscriptionBase>> subscriptionsPerBundle = new HashMap<UUID, Collection<SubscriptionBase>>();
    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 (eventsStreamPerBundle.get(bundleId) == null) {
            eventsStreamPerBundle.put(bundleId, new LinkedList<EventsStream>());
        }
        if (subscriptionsPerBundle.get(bundleId) == null) {
            subscriptionsPerBundle.put(bundleId, allSubscriptionsForBundle);
        }
        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 Collection<BlockingState> subscriptionBlockingStates;
            if (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) {
                subscriptionBlockingStates = subscriptionBlockingStatesOnDisk;
            } else {
                subscriptionBlockingStates = blockingStateDao.getBlockingHistory(subscriptionBlockingStatesOnDisk, blockingStatesForAccount, account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, accountBCD, catalog, 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, accountBCD, bcdCache, catalog, internalTenantContext);
            eventsStreamPerBundle.get(bundleId).add(eventStream);
        }
    }
    return new DefaultAccountEventsStreams(account, bundles, subscriptionsPerBundle, eventsStreamPerBundle);
}
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)

Aggregations

SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)120 Test (org.testng.annotations.Test)47 UUID (java.util.UUID)46 DateTime (org.joda.time.DateTime)40 Plan (org.killbill.billing.catalog.api.Plan)37 ArrayList (java.util.ArrayList)35 LocalDate (org.joda.time.LocalDate)33 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)32 BillingEvent (org.killbill.billing.junction.BillingEvent)30 PlanPhase (org.killbill.billing.catalog.api.PlanPhase)26 MockPlan (org.killbill.billing.catalog.MockPlan)24 MockPlanPhase (org.killbill.billing.catalog.MockPlanPhase)24 MockBillingEventSet (org.killbill.billing.invoice.MockBillingEventSet)23 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)23 BigDecimal (java.math.BigDecimal)22 LinkedList (java.util.LinkedList)21 Invoice (org.killbill.billing.invoice.api.Invoice)21 AccountInvoices (org.killbill.billing.invoice.optimizer.InvoiceOptimizerBase.AccountInvoices)21 BillingEventSet (org.killbill.billing.junction.BillingEventSet)21 DefaultInvoice (org.killbill.billing.invoice.model.DefaultInvoice)20