Search in sources :

Example 66 with SubscriptionBaseApiException

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

the class DefaultSubscriptionInternalApi method getSubscriptionFromId.

@Override
public SubscriptionBase getSubscriptionFromId(final UUID id, final InternalTenantContext context) throws SubscriptionBaseApiException {
    try {
        final SubscriptionCatalog catalog = subscriptionCatalogApi.getFullCatalog(context);
        final SubscriptionBase result = dao.getSubscriptionFromId(id, catalog, context);
        if (result == null) {
            throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_SUBSCRIPTION_ID, id);
        }
        return createSubscriptionForApiUse(result);
    } catch (final CatalogApiException e) {
        throw new SubscriptionBaseApiException(e);
    }
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) SubscriptionCatalog(org.killbill.billing.subscription.catalog.SubscriptionCatalog) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 67 with SubscriptionBaseApiException

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

the class DefaultSubscriptionInternalApi method getSubscriptionsForAccount.

@Override
public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final VersionedCatalog publicCatalog, @Nullable final LocalDate cutoffDt, final InternalTenantContext context) throws SubscriptionBaseApiException {
    try {
        final SubscriptionCatalog catalog = DefaultSubscriptionCatalogApi.wrapCatalog(publicCatalog, clock);
        final Map<UUID, List<DefaultSubscriptionBase>> internalSubscriptions = dao.getSubscriptionsForAccount(catalog, cutoffDt, context);
        final Map<UUID, List<SubscriptionBase>> result = new HashMap<UUID, List<SubscriptionBase>>();
        for (final UUID bundleId : internalSubscriptions.keySet()) {
            final List<DefaultSubscriptionBase> subscriptionsForApiUse = createSubscriptionsForApiUse(internalSubscriptions.get(bundleId));
            result.put(bundleId, new ArrayList<SubscriptionBase>(subscriptionsForApiUse));
        }
        return result;
    } catch (final CatalogApiException e) {
        throw new SubscriptionBaseApiException(e);
    }
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) HashMap(java.util.HashMap) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) List(java.util.List) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) UUID(java.util.UUID) SubscriptionCatalog(org.killbill.billing.subscription.catalog.SubscriptionCatalog) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 68 with SubscriptionBaseApiException

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

the class DefaultSubscriptionInternalApi method getDryRunChangePlanStatus.

@Override
public List<EntitlementAOStatusDryRun> getDryRunChangePlanStatus(final UUID subscriptionId, @Nullable final String baseProductName, final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionBaseApiException {
    try {
        final SubscriptionCatalog catalog = subscriptionCatalogApi.getFullCatalog(context);
        final SubscriptionBase subscription = dao.getSubscriptionFromId(subscriptionId, catalog, context);
        if (subscription == null) {
            throw new SubscriptionBaseApiException(ErrorCode.SUB_INVALID_SUBSCRIPTION_ID, subscriptionId);
        }
        if (subscription.getCategory() != ProductCategory.BASE) {
            throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_DRY_RUN_NOT_BP);
        }
        final List<EntitlementAOStatusDryRun> result = new LinkedList<EntitlementAOStatusDryRun>();
        final List<DefaultSubscriptionBase> bundleSubscriptions = dao.getSubscriptions(subscription.getBundleId(), ImmutableList.<SubscriptionBaseEvent>of(), catalog, context);
        for (final SubscriptionBase cur : bundleSubscriptions) {
            if (cur.getId().equals(subscriptionId)) {
                continue;
            }
            // If ADDON is cancelled, skip
            if (cur.getState() == EntitlementState.CANCELLED) {
                continue;
            }
            final StaticCatalog catalogVersion = catalog.versionForDate(requestedDate);
            final Product baseProduct = baseProductName != null ? catalogVersion.findProduct(baseProductName) : null;
            final DryRunChangeReason reason;
            // If baseProductName is null, it's a cancellation dry-run. In this case, return all addons, so they are cancelled
            if (baseProduct != null && addonUtils.isAddonIncluded(baseProduct, cur.getCurrentPlan())) {
                reason = DryRunChangeReason.AO_INCLUDED_IN_NEW_PLAN;
            } else if (baseProduct != null && addonUtils.isAddonAvailable(baseProduct, cur.getCurrentPlan())) {
                reason = DryRunChangeReason.AO_AVAILABLE_IN_NEW_PLAN;
            } else {
                reason = DryRunChangeReason.AO_NOT_AVAILABLE_IN_NEW_PLAN;
            }
            final EntitlementAOStatusDryRun status = new DefaultSubscriptionStatusDryRun(cur.getId(), cur.getCurrentPlan().getProduct().getName(), cur.getCurrentPhase().getPhaseType(), cur.getCurrentPlan().getRecurringBillingPeriod(), cur.getCurrentPriceList().getName(), reason);
            result.add(status);
        }
        return result;
    } catch (final CatalogApiException e) {
        throw new SubscriptionBaseApiException(e);
    }
}
Also used : Product(org.killbill.billing.catalog.api.Product) SubscriptionCatalog(org.killbill.billing.subscription.catalog.SubscriptionCatalog) StaticCatalog(org.killbill.billing.catalog.api.StaticCatalog) LinkedList(java.util.LinkedList) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) DryRunChangeReason(org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun.DryRunChangeReason) EntitlementAOStatusDryRun(org.killbill.billing.entitlement.api.EntitlementAOStatusDryRun) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) DefaultSubscriptionStatusDryRun(org.killbill.billing.subscription.api.user.DefaultSubscriptionStatusDryRun) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 69 with SubscriptionBaseApiException

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

the class DefaultSubscriptionDao method createSubscriptionBundle.

@Override
public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final SubscriptionCatalog catalog, final boolean renameCancelledBundleIfExist, final InternalCallContext context) throws SubscriptionBaseApiException {
    return transactionalSqlDao.execute(false, SubscriptionBaseApiException.class, new EntitySqlDaoTransactionWrapper<SubscriptionBaseBundle>() {

        // 
        // Because the creation of the SubscriptionBundle is not atomic (with creation of Subscription/SubscriptionEvent), we verify if we were left
        // with an empty SubscriptionBaseBundle form a past failing operation (See #684). We only allow reuse if such SubscriptionBaseBundle is fully
        // empty (and don't allow use case where all Subscription are cancelled, which is the condition for that key to be re-used)
        // Such condition should have been checked upstream (to decide whether that key is valid or not)
        // 
        private SubscriptionBaseBundle findExistingUnusedBundleForExternalKeyAndAccount(final List<SubscriptionBundleModelDao> existingBundles, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) {
            final SubscriptionBundleModelDao existingBundleForAccount = Iterables.tryFind(existingBundles, new Predicate<SubscriptionBundleModelDao>() {

                @Override
                public boolean apply(final SubscriptionBundleModelDao input) {
                    return input.getAccountId().equals(bundle.getAccountId()) && // We look for strict equality ignoring tsf items with keys 'kbtsf-343453:'
                    bundle.getExternalKey().equals(input.getExternalKey());
                }
            }).orNull();
            // If Bundle already exists, and there is 0 Subscription associated with this bundle, we reuse
            if (existingBundleForAccount != null) {
                final List<SubscriptionModelDao> accountSubscriptions = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getByAccountRecordId(context);
                if (accountSubscriptions == null || !Iterables.any(accountSubscriptions, new Predicate<SubscriptionModelDao>() {

                    @Override
                    public boolean apply(final SubscriptionModelDao input) {
                        return input.getBundleId().equals(existingBundleForAccount.getId());
                    }
                })) {
                    return SubscriptionBundleModelDao.toSubscriptionBundle(existingBundleForAccount);
                }
            }
            return null;
        }

        @Override
        public SubscriptionBaseBundle inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
            final List<SubscriptionBundleModelDao> existingBundles = bundle.getExternalKey() == null ? ImmutableList.<SubscriptionBundleModelDao>of() : entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesForLikeKey(bundle.getExternalKey(), context);
            final SubscriptionBaseBundle unusedBundle = findExistingUnusedBundleForExternalKeyAndAccount(existingBundles, entitySqlDaoWrapperFactory);
            if (unusedBundle != null) {
                log.info("Found unused bundle for externalKey='{}': bundleId='{}'", bundle.getExternalKey(), unusedBundle.getId());
                return unusedBundle;
            }
            final BundleSqlDao bundleSqlDao = entitySqlDaoWrapperFactory.become(BundleSqlDao.class);
            for (SubscriptionBundleModelDao cur : existingBundles) {
                final List<SubscriptionModelDao> subscriptions = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class).getSubscriptionsFromBundleId(cur.getId().toString(), context);
                final Iterable<SubscriptionModelDao> filtered = subscriptions != null ? Iterables.filter(subscriptions, new Predicate<SubscriptionModelDao>() {

                    @Override
                    public boolean apply(final SubscriptionModelDao input) {
                        return input.getCategory() != ProductCategory.ADD_ON;
                    }
                }) : ImmutableList.<SubscriptionModelDao>of();
                for (SubscriptionModelDao f : filtered) {
                    try {
                        final SubscriptionBase s = buildSubscription(SubscriptionModelDao.toSubscription(f, cur.getExternalKey()), catalog, context);
                        if (s.getState() != EntitlementState.CANCELLED) {
                            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_ACTIVE_BUNDLE_KEY_EXISTS, bundle.getExternalKey());
                        } else if (renameCancelledBundleIfExist) {
                            log.info("Renaming bundles with externalKey='{}', prefix='cncl'", bundle.getExternalKey());
                            renameBundleExternalKey(bundleSqlDao, bundle.getExternalKey(), "cncl", context);
                        }
                    /* else {
                                Code will throw SQLIntegrityConstraintViolationException because of unique constraint on externalKey; might be worth having an ErrorCode just for that
                            } */
                    } catch (CatalogApiException e) {
                        throw new SubscriptionBaseApiException(e);
                    }
                }
            }
            final SubscriptionBundleModelDao model = new SubscriptionBundleModelDao(bundle);
            // Preserve Original created date
            if (!existingBundles.isEmpty()) {
                model.setOriginalCreatedDate(existingBundles.get(0).getCreatedDate());
            }
            final SubscriptionBundleModelDao result = createAndRefresh(bundleSqlDao, model, context);
            return SubscriptionBundleModelDao.toSubscriptionBundle(result);
        }
    });
}
Also used : SubscriptionModelDao(org.killbill.billing.subscription.engine.dao.model.SubscriptionModelDao) SubscriptionBundleModelDao(org.killbill.billing.subscription.engine.dao.model.SubscriptionBundleModelDao) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) SubscriptionApiException(org.killbill.billing.entitlement.api.SubscriptionApiException) IOException(java.io.IOException) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException) EventBusException(org.killbill.bus.api.PersistentBus.EventBusException) EntityPersistenceException(org.killbill.billing.entity.EntityPersistenceException) Predicate(com.google.common.base.Predicate) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) DefaultSubscriptionBaseBundle(org.killbill.billing.subscription.api.user.DefaultSubscriptionBaseBundle) EntitySqlDaoWrapperFactory(org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory) ArrayList(java.util.ArrayList) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 70 with SubscriptionBaseApiException

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

the class DefaultSubscriptionBaseCreateApi method verifyAndBuildSubscriptionSpecifiers.

private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final SubscriptionBaseBundle bundle, final boolean hasBaseOrStandalonePlanSpecifier, final List<EntitlementSpecifier> entitlements, final List<Plan> entitlementsPlans, final boolean isMigrated, final DateTime effectiveDate, final SubscriptionCatalog catalog, final AddonUtils addonUtils, final TenantContext callContext, final InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException {
    final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
    for (int i = 0; i < entitlements.size(); i++) {
        final EntitlementSpecifier entitlement = entitlements.get(i);
        final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
        if (spec == null) {
            // BP already exists
            continue;
        }
        final Plan plan = entitlementsPlans.get(i);
        final PlanPhase phase = plan.getAllPhases()[0];
        if (phase == null) {
            throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog", spec.getProductName(), spec.getBillingPeriod().toString(), plan.getPriceList()));
        }
        // verify the number of subscriptions (of the same kind) allowed per bundle and the existing ones
        if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
            if (plan.getPlansAllowedInBundle() != -1 && plan.getPlansAllowedInBundle() > 0) {
                // TODO We should also look to the specifiers being created for validation
                final List<DefaultSubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundle.getId(), null, catalog, addonUtils, callContext, context);
                final int existingAddOnsWithSamePlanName = addonUtils.countExistingAddOnsWithSamePlanName(subscriptionsForBundle, plan.getName());
                final int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlementsPlans, plan);
                if ((existingAddOnsWithSamePlanName + currentAddOnsWithSamePlanName) > plan.getPlansAllowedInBundle()) {
                    // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
                }
            }
        }
        final DateTime bundleStartDate;
        if (hasBaseOrStandalonePlanSpecifier) {
            bundleStartDate = effectiveDate;
        } else {
            final SubscriptionBase baseSubscription = dao.getBaseSubscription(bundle.getId(), catalog, context);
            bundleStartDate = getBundleStartDateWithSanity(bundle.getId(), baseSubscription, plan, effectiveDate, addonUtils, context);
        }
        final SubscriptionSpecifier subscription = new SubscriptionSpecifier();
        subscription.setRealPriceList(plan.getPriceList().getName());
        subscription.setEffectiveDate(effectiveDate);
        subscription.setProcessedDate(context.getCreatedDate());
        subscription.setPlan(plan);
        subscription.setInitialPhase(spec.getPhaseType());
        subscription.setBuilder(new SubscriptionBuilder().setId(UUIDs.randomUUID()).setBundleId(bundle.getId()).setExternalKey(entitlement.getExternalKey()).setBundleExternalKey(bundle.getExternalKey()).setCategory(plan.getProduct().getCategory()).setBundleStartDate(bundleStartDate).setAlignStartDate(effectiveDate).setMigrated(isMigrated).setSubscriptionBCD(entitlement.getBillCycleDay()));
        subscriptions.add(subscription);
    }
    return subscriptions;
}
Also used : SubscriptionSpecifier(org.killbill.billing.subscription.api.user.SubscriptionSpecifier) PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) ArrayList(java.util.ArrayList) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) Plan(org.killbill.billing.catalog.api.Plan) DateTime(org.joda.time.DateTime) EntitlementSpecifier(org.killbill.billing.entitlement.api.EntitlementSpecifier) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Aggregations

SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)72 ArrayList (java.util.ArrayList)29 DateTime (org.joda.time.DateTime)29 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)28 UUID (java.util.UUID)27 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)24 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)24 WithEntitlementPlugin (org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin)19 EntitlementContext (org.killbill.billing.entitlement.plugin.api.EntitlementContext)19 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)17 SubscriptionBaseBundle (org.killbill.billing.subscription.api.user.SubscriptionBaseBundle)16 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)14 DefaultBlockingState (org.killbill.billing.junction.DefaultBlockingState)14 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)11 Plan (org.killbill.billing.catalog.api.Plan)11 LinkedList (java.util.LinkedList)10 SubscriptionCatalog (org.killbill.billing.subscription.catalog.SubscriptionCatalog)10 AccountApiException (org.killbill.billing.account.api.AccountApiException)8 PlanPhasePriceOverridesWithCallContext (org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext)8 EntitlementApiException (org.killbill.billing.entitlement.api.EntitlementApiException)8