use of org.killbill.billing.subscription.api.SubscriptionBase 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);
}
}
use of org.killbill.billing.subscription.api.SubscriptionBase 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);
}
}
use of org.killbill.billing.subscription.api.SubscriptionBase 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);
}
});
}
use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.
the class DefaultSubscriptionBaseApiService method addCancellationAddOnForEventsIfRequired.
private List<DefaultSubscriptionBase> addCancellationAddOnForEventsIfRequired(final Collection<SubscriptionBaseEvent> events, final Product baseProduct, final UUID bundleId, final DateTime effectiveDate, final SubscriptionCatalog catalog, final InternalTenantContext internalTenantContext) throws CatalogApiException {
final List<DefaultSubscriptionBase> subscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
final List<DefaultSubscriptionBase> subscriptions = dao.getSubscriptions(bundleId, ImmutableList.<SubscriptionBaseEvent>of(), catalog, internalTenantContext);
for (final SubscriptionBase subscription : subscriptions) {
final DefaultSubscriptionBase cur = (DefaultSubscriptionBase) subscription;
if (cur.getState() == EntitlementState.CANCELLED || cur.getCategory() != ProductCategory.ADD_ON) {
continue;
}
final Plan addonCurrentPlan = cur.getCurrentPlan();
if (baseProduct == null || addonUtils.isAddonIncluded(baseProduct, addonCurrentPlan) || !addonUtils.isAddonAvailable(baseProduct, addonCurrentPlan)) {
//
// Perform AO cancellation using the effectiveDate of the BP
//
final SubscriptionBaseEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder().setSubscriptionId(cur.getId()).setEffectiveDate(effectiveDate).setFromDisk(true));
subscriptionsToBeCancelled.add(cur);
events.add(cancelEvent);
}
}
return subscriptionsToBeCancelled;
}
use of org.killbill.billing.subscription.api.SubscriptionBase in project killbill by killbill.
the class DefaultSubscriptionBaseCreateApi method createBaseSubscriptionsWithAddOns.
List<SubscriptionBaseWithAddOns> createBaseSubscriptionsWithAddOns(final Iterable<SubscriptionBaseWithAddOnsSpecifier> baseAndAddOnEntitlementsSpecifiers, final boolean renameCancelledBundleIfExist, final SubscriptionCatalog catalog, final AddonUtils addonUtils, final CacheController<UUID, UUID> accountIdCacheController, final CacheController<UUID, UUID> bundleIdCacheController, final CallContext callContext, final InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException {
// Prepare the subscription specifiers from the entitlement specifiers
final Collection<SubscriptionAndAddOnsSpecifier> baseAndAddOnSubscriptionsSpecifiers = new ArrayList<SubscriptionAndAddOnsSpecifier>();
for (final SubscriptionBaseWithAddOnsSpecifier baseAndAddOnEntitlementsSpecifier : baseAndAddOnEntitlementsSpecifiers) {
prepareSubscriptionAndAddOnsSpecifier(baseAndAddOnSubscriptionsSpecifiers, baseAndAddOnEntitlementsSpecifier, renameCancelledBundleIfExist, catalog, addonUtils, accountIdCacheController, callContext, context);
}
// Create the subscriptions
final List<SubscriptionBaseWithAddOns> subscriptionBaseWithAddOns = apiService.createPlansWithAddOns(callContext.getAccountId(), baseAndAddOnSubscriptionsSpecifiers, catalog, callContext);
// Populate the caches
for (final SubscriptionBaseWithAddOns subscriptionBaseWithAO : subscriptionBaseWithAddOns) {
for (final SubscriptionBase subscriptionBase : subscriptionBaseWithAO.getSubscriptionBaseList()) {
bundleIdCacheController.putIfAbsent(subscriptionBase.getId(), subscriptionBaseWithAO.getBundle().getId());
}
}
return subscriptionBaseWithAddOns;
}
Aggregations