diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 36a9afe..d629e61 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -259,16 +259,17 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
@Override
public SubscriptionBaseBundle createSubscriptionBundle(final DefaultSubscriptionBaseBundle bundle, final InternalCallContext context) throws SubscriptionBaseApiException {
+
return transactionalSqlDao.execute(SubscriptionBaseApiException.class, new EntitySqlDaoTransactionWrapper<SubscriptionBaseBundle>() {
- @Override
- public SubscriptionBaseBundle inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- final List<SubscriptionBundleModelDao> existingBundles = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesForKey(bundle.getExternalKey(), context);
- //
- // 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)
- //
+
+ //
+ // 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) {
@@ -276,19 +277,37 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
}
}).orNull();
- // If Bundle already exists, and there is 0 Subscription, we reuse
+ // 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 || accountSubscriptions.size() == 0) {
+ 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 = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesForKey(bundle.getExternalKey(), context);
+
+ final SubscriptionBaseBundle unusedBundle = findExistingUnusedBundleForExternalKeyAndAccount(existingBundles, entitySqlDaoWrapperFactory);
+ if (unusedBundle != null) {
+ return unusedBundle;
+ }
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(@Nullable final SubscriptionModelDao input) {
+ public boolean apply(final SubscriptionModelDao input) {
return input.getCategory() != ProductCategory.ADD_ON;
}
}) : ImmutableList.<SubscriptionModelDao>of();