killbill-aplcache
Changes
account/pom.xml 2(+1 -1)
analytics/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
catalog/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java 1078(+540 -538)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg 138(+52 -86)
entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg 12(+11 -1)
invoice/pom.xml 10(+9 -1)
jaxrs/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
pom.xml 5(+2 -3)
server/pom.xml 2(+1 -1)
util/pom.xml 5(+2 -3)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index c48b8b2..de028b7 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
analytics/pom.xml 2(+1 -1)
diff --git a/analytics/pom.xml b/analytics/pom.xml
index d5fff4c..a92d088 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-analytics</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index ac7f6c9..a9cec84 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-api</artifactId>
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 28f478f..64726c8 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
index 0b509e3..e334966 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/TestIntegration.java
@@ -19,6 +19,7 @@ package com.ning.billing.beatrix.integration;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
import java.io.IOException;
import java.math.BigDecimal;
@@ -33,6 +34,7 @@ import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.InvoicingConfiguration;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallOrigin;
import com.ning.billing.util.callcontext.UserType;
@@ -82,8 +84,8 @@ import com.ning.billing.util.bus.BusService;
@Test(groups = "slow")
@Guice(modules = {MockModule.class})
public class TestIntegration {
- private static final int NUMBER_OF_DECIMALS = 4;
- private static final int ROUNDING_METHOD = BigDecimal.ROUND_HALF_EVEN;
+ private static final int NUMBER_OF_DECIMALS = InvoicingConfiguration.getNumberOfDecimals();
+ private static final int ROUNDING_METHOD = InvoicingConfiguration.getRoundingMode();
private static final BigDecimal ONE = new BigDecimal("1.0000").setScale(NUMBER_OF_DECIMALS);
private static final BigDecimal TWENTY_NINE = new BigDecimal("29.0000").setScale(NUMBER_OF_DECIMALS);
@@ -244,7 +246,9 @@ public class TestIntegration {
}
}
- assertTrue(wasFound);
+ if (!wasFound) {
+ fail();
+ }
DateTime ctd = subscription.getChargedThroughDate();
assertNotNull(ctd);
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index dee138d..489aba0 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index cccf568..426d9f8 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
index d9d9381..bccd559 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/BundleSqlDao.java
@@ -18,6 +18,8 @@ package com.ning.billing.entitlement.engine.dao;
import com.ning.billing.entitlement.api.user.SubscriptionBundle;
import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
import com.ning.billing.util.dao.BinderBase;
import com.ning.billing.util.dao.MapperBase;
import org.joda.time.DateTime;
@@ -43,7 +45,8 @@ import java.util.UUID;
public interface BundleSqlDao extends Transactional<BundleSqlDao>, CloseMe, Transmogrifier {
@SqlUpdate
- public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle);
+ public void insertBundle(@Bind(binder = SubscriptionBundleBinder.class) SubscriptionBundleData bundle,
+ @CallContextBinder final CallContext context);
@SqlQuery
@Mapper(ISubscriptionBundleSqlMapper.class)
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index 2dd8a8b..55238f5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -73,542 +73,544 @@ import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotifi
import javax.annotation.Nullable;
public class EntitlementSqlDao implements EntitlementDao {
- private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
- public static final String ENTITLEMENT_EVENTS_TABLE_NAME = "entitlement_events";
- public static final String BUNDLES_TABLE_NAME = "bundles";
- public static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
-
- private final Clock clock;
- private final SubscriptionSqlDao subscriptionsDao;
- private final BundleSqlDao bundlesDao;
- private final EventSqlDao eventsDao;
- private final NotificationQueueService notificationQueueService;
- private final AddonUtils addonUtils;
- private final CustomFieldDao customFieldDao;
-
- //
- // We are not injecting SubscriptionFactory since that creates circular dependencies--
- // Guice would still work, but this is playing with fire.
- //
- // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
- //
- @Inject
- public EntitlementSqlDao(final IDBI dbi, final Clock clock,
- final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
- final CustomFieldDao customFieldDao) {
- this.clock = clock;
- this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
- this.eventsDao = dbi.onDemand(EventSqlDao.class);
- this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
- this.notificationQueueService = notificationQueueService;
- this.addonUtils = addonUtils;
- this.customFieldDao = customFieldDao;
- }
-
- @Override
- public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
- return bundlesDao.getBundleFromKey(bundleKey);
- }
-
- @Override
- public List<SubscriptionBundle> getSubscriptionBundleForAccount(
- final UUID accountId) {
- return bundlesDao.getBundleFromAccount(accountId.toString());
- }
-
- @Override
- public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
- return bundlesDao.getBundleFromId(bundleId.toString());
- }
-
- @Override
- public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
- return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
- @Override
- public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
- bundlesDao.insertBundle(bundle);
-
- AuditSqlDao auditSqlDao = bundlesDao.become(AuditSqlDao.class);
- String bundleId = bundle.getId().toString();
- auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleId, ChangeType.INSERT, context);
-
- return bundle;
- }
- });
- }
-
- @Override
- public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
- Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
- if (subscription == null) {
- log.error(String.format(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
- return null;
- }
-
- UUID bundleId = subscription.getBundleId();
- if (bundleId == null) {
- log.error(String.format(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
- return null;
- }
-
- SubscriptionBundle bundle = bundlesDao.getBundleFromId(bundleId.toString());
- if (bundle == null) {
- log.error(String.format(ErrorCode.ENT_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
- return null;
- }
-
- return bundle.getAccountId();
- }
-
- @Override
- public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
- return getBaseSubscription(factory, bundleId, true);
- }
-
- @Override
- public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
- return buildSubscription(factory, subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
- }
-
- @Override
- public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
- return buildBundleSubscriptions(factory, subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
- }
-
- @Override
- public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
- SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
- if (bundle == null) {
- return Collections.emptyList();
- }
- return getSubscriptions(factory, bundle.getId());
- }
-
- @Override
- public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
-
- final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
- final Date ptd = (subscription.getPaidThroughDate() != null) ? subscription.getPaidThroughDate().toDate() : null;
-
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
- @Override
- public Void inTransaction(SubscriptionSqlDao transactionalDao,
- TransactionStatus status) throws Exception {
- transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
-
- AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
- String subscriptionId = subscription.getId().toString();
- auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
- return null;
- }
- });
- }
-
- @Override
- public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
- dao.insertEvent(nextPhase);
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, nextPhase.getId().toString(), ChangeType.INSERT, context);
-
- recordFutureNotificationFromTransaction(dao,
- nextPhase.getEffectiveDate(),
- new EntitlementNotificationKey(nextPhase.getId()));
- return null;
- }
- });
- }
-
- @Override
- public EntitlementEvent getEventById(UUID eventId) {
- return eventsDao.getEventById(eventId.toString());
- }
-
- @Override
- public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
- return eventsDao.getEventsForSubscription(subscriptionId.toString());
- }
-
- @Override
- public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
- Date now = clock.getUTCNow().toDate();
- return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
- }
-
- @Override
- public void createSubscription(final SubscriptionData subscription,
- final List<EntitlementEvent> initialEvents, final CallContext context) {
-
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
-
- @Override
- public Void inTransaction(SubscriptionSqlDao dao,
- TransactionStatus status) throws Exception {
-
- dao.insertSubscription(subscription, context);
- // STEPH batch as well
- EventSqlDao eventsDaoFromSameTransaction = dao.become(EventSqlDao.class);
- List<String> eventIds = new ArrayList<String>();
-
- for (final EntitlementEvent cur : initialEvents) {
- eventsDaoFromSameTransaction.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // collect ids for batch audit log insert
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new EntitlementNotificationKey(cur.getId()));
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- @Override
- public void recreateSubscription(final UUID subscriptionId,
- final List<EntitlementEvent> recreateEvents, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : recreateEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch audit insert
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new EntitlementNotificationKey(cur.getId()));
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- @Override
- public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context, final int seqId) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
- cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
- cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
- dao.insertEvent(cancelEvent);
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- String cancelEventId = cancelEvent.getId().toString();
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, cancelEventId, ChangeType.INSERT, context);
-
- recordFutureNotificationFromTransaction(dao,
- cancelEvent.getEffectiveDate(),
- new EntitlementNotificationKey(cancelEvent.getId(), seqId));
- return null;
- }
- });
- }
-
- @Override
- public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
- @Override
- public Void inTransaction(EventSqlDao dao,
- TransactionStatus status) throws Exception {
-
- UUID existingCancelId = null;
- Date now = clock.getUTCNow().toDate();
- List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
-
- for (EntitlementEvent cur : events) {
- if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
- if (existingCancelId != null) {
- throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
- }
- existingCancelId = cur.getId();
- }
- }
-
- if (existingCancelId != null) {
- dao.unactiveEvent(existingCancelId.toString(), now);
- String deactivatedEventId = existingCancelId.toString();
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : uncancelEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch insert into audit log
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new EntitlementNotificationKey(cur.getId()));
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, deactivatedEventId, ChangeType.UPDATE, context);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- }
- return null;
- }
- });
- }
-
- @Override
- public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
- @Override
- public Void inTransaction(EventSqlDao dao, TransactionStatus status) throws Exception {
- cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
- cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
-
- List<String> eventIds = new ArrayList<String>();
- for (final EntitlementEvent cur : changeEvents) {
- dao.insertEvent(cur);
- eventIds.add(cur.getId().toString()); // gather event ids for batch audit log insert
-
- recordFutureNotificationFromTransaction(dao,
- cur.getEffectiveDate(),
- new EntitlementNotificationKey(cur.getId()));
- }
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
- return null;
- }
- });
- }
-
- private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
- }
-
- private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
- }
-
- private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
- cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
- }
-
- private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
- final EventType type, @Nullable final ApiEventType apiType,
- final CallContext context) {
-
- UUID futureEventId = null;
- Date now = clock.getUTCNow().toDate();
- List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
- for (EntitlementEvent cur : events) {
- if (cur.getType() == type &&
- (apiType == null || apiType == ((ApiEvent) cur).getEventType() )) {
- if (futureEventId != null) {
- throw new EntitlementError(
- String.format("Found multiple future events for type %s for subscriptions %s",
- type, subscriptionId.toString()));
- }
- futureEventId = cur.getId();
- }
- }
-
- if (futureEventId != null) {
- dao.unactiveEvent(futureEventId.toString(), now);
-
- AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, futureEventId.toString(), ChangeType.UPDATE, context);
- }
- }
-
- private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
- final SubscriptionData subscription,
- final CallContext context) {
- customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
- }
-
- private Subscription buildSubscription(final SubscriptionFactory factory, final Subscription input) {
- if (input == null) {
- return null;
- }
- List<Subscription> bundleInput = new ArrayList<Subscription>();
- if (input.getCategory() == ProductCategory.ADD_ON) {
- Subscription baseSubscription = getBaseSubscription(factory, input.getBundleId(), false);
- bundleInput.add(baseSubscription);
- bundleInput.add(input);
- } else {
- bundleInput.add(input);
- }
- List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(factory, bundleInput);
- for (Subscription cur : reloadedSubscriptions) {
- if (cur.getId().equals(input.getId())) {
- return cur;
- }
- }
- throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
- }
-
- private List<Subscription> buildBundleSubscriptions(final SubscriptionFactory factory, final List<Subscription> input) {
- // Make sure BasePlan -- if exists-- is first
- Collections.sort(input, new Comparator<Subscription>() {
- @Override
- public int compare(Subscription o1, Subscription o2) {
- if (o1.getCategory() == ProductCategory.BASE) {
- return -1;
- } else if (o2.getCategory() == ProductCategory.BASE) {
- return 1;
- } else {
- return o1.getStartDate().compareTo(o2.getStartDate());
- }
- }
- });
-
- EntitlementEvent futureBaseEvent = null;
- List<Subscription> result = new ArrayList<Subscription>(input.size());
- for (Subscription cur : input) {
-
- List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
- Subscription reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
-
- switch (cur.getCategory()) {
- case BASE:
- Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
- @Override
- public boolean apply(EntitlementEvent input) {
- return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
- ((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
- }
- });
- futureBaseEvent = (futureApiEvents.size() == 0) ? null : futureApiEvents.iterator().next();
- break;
-
- case ADD_ON:
- Plan targetAddOnPlan = reloaded.getCurrentPlan();
- String baseProductName = (futureBaseEvent instanceof ApiEventChange) ?
- ((ApiEventChange) futureBaseEvent).getEventPlan() : null;
-
- boolean createCancelEvent = (futureBaseEvent != null) &&
- ((futureBaseEvent instanceof ApiEventCancel) ||
- ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
- (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
-
- if (createCancelEvent) {
- DateTime now = clock.getUTCNow();
- EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
- .setSubscriptionId(reloaded.getId())
- .setActiveVersion(((SubscriptionData) reloaded).getActiveVersion())
- .setProcessedDate(now)
- .setEffectiveDate(futureBaseEvent.getEffectiveDate())
- .setRequestedDate(now)
- // This event is only there to indicate the ADD_ON is future canceled, but it is not there
- // on disk until the base plan cancellation becomes effective
- .setFromDisk(false));
-
- events.add(addOnCancelEvent);
- // Finally reload subscription with full set of events
- reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
- }
- break;
- default:
- break;
- }
- loadCustomFields((SubscriptionData) reloaded);
- result.add(reloaded);
- }
- return result;
- }
-
- @Override
- public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
-
- eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
- @Override
- public Void inTransaction(EventSqlDao transEventDao,
- TransactionStatus status) throws Exception {
-
- SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
- BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
-
- List<String> bundleIds = new ArrayList<String>();
- List<String> subscriptionIds = new ArrayList<String>();
- List<String> eventIds = new ArrayList<String>();
-
- for (BundleMigrationData curBundle : accountData.getData()) {
- SubscriptionBundleData bundleData = curBundle.getData();
-
- for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
-
- SubscriptionData subData = curSubscription.getData();
- for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
- transEventDao.insertEvent(curEvent);
- eventIds.add(curEvent.getId().toString()); // gather event ids for batch audit
-
- recordFutureNotificationFromTransaction(transEventDao,
- curEvent.getEffectiveDate(),
- new EntitlementNotificationKey(curEvent.getId()));
- }
- transSubDao.insertSubscription(subData, context);
- subscriptionIds.add(subData.getId().toString()); // gather subscription ids for batch audit
- }
- transBundleDao.insertBundle(bundleData);
- bundleIds.add(bundleData.getId().toString()); // gather bundle ids for batch audit
- }
-
- // add audit records for bundles, subscriptions, and events
- AuditSqlDao auditSqlDao = transBundleDao.become(AuditSqlDao.class);
- auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionIds, ChangeType.INSERT, context);
- auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleIds, ChangeType.INSERT, context);
- auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
-
- return null;
- }
- });
- }
-
-
- private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
- List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
- for (Subscription cur : subscriptions) {
- if (cur.getCategory() == ProductCategory.BASE) {
- return rebuildSubscription ? buildSubscription(factory, cur) : cur;
- }
- }
- return null;
- }
-
- private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
- try {
- NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
- Engine.NOTIFICATION_QUEUE_NAME);
- subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
- } catch (NoSuchNotificationQueue e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
- subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
- @Override
- public Void inTransaction(SubscriptionSqlDao transactionalDao,
- TransactionStatus status) throws Exception {
- updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
- return null;
- }
- });
- }
-
- private void loadCustomFields(final SubscriptionData subscription) {
- CustomFieldSqlDao customFieldSqlDao = subscriptionsDao.become(CustomFieldSqlDao.class);
- List<CustomField> fields = customFieldSqlDao.load(subscription.getId().toString(), subscription.getObjectName());
- subscription.clearFields();
- if (fields != null) {
- subscription.setFields(fields);
- }
- }
+
+ private final static Logger log = LoggerFactory.getLogger(EntitlementSqlDao.class);
+ public static final String ENTITLEMENT_EVENTS_TABLE_NAME = "entitlement_events";
+ public static final String BUNDLES_TABLE_NAME = "bundles";
+ public static final String SUBSCRIPTIONS_TABLE_NAME = "subscriptions";
+
+ private final Clock clock;
+ private final SubscriptionSqlDao subscriptionsDao;
+ private final BundleSqlDao bundlesDao;
+ private final EventSqlDao eventsDao;
+ private final NotificationQueueService notificationQueueService;
+ private final AddonUtils addonUtils;
+ private final CustomFieldDao customFieldDao;
+
+ //
+ // We are not injecting SubscriptionFactory since that creates circular dependencies--
+ // Guice would still work, but this is playing with fire.
+ //
+ // Instead that factory passed through API top to bottom for the call where is it needed-- where we returned fully rehydrated Subscriptions
+ //
+ @Inject
+ public EntitlementSqlDao(final IDBI dbi, final Clock clock,
+ final AddonUtils addonUtils, final NotificationQueueService notificationQueueService,
+ final CustomFieldDao customFieldDao) {
+ this.clock = clock;
+ this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
+ this.eventsDao = dbi.onDemand(EventSqlDao.class);
+ this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
+ this.notificationQueueService = notificationQueueService;
+ this.addonUtils = addonUtils;
+ this.customFieldDao = customFieldDao;
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromKey(final String bundleKey) {
+ return bundlesDao.getBundleFromKey(bundleKey);
+ }
+
+ @Override
+ public List<SubscriptionBundle> getSubscriptionBundleForAccount(
+ final UUID accountId) {
+ return bundlesDao.getBundleFromAccount(accountId.toString());
+ }
+
+ @Override
+ public SubscriptionBundle getSubscriptionBundleFromId(final UUID bundleId) {
+ return bundlesDao.getBundleFromId(bundleId.toString());
+ }
+
+ @Override
+ public SubscriptionBundle createSubscriptionBundle(final SubscriptionBundleData bundle, final CallContext context) {
+ return bundlesDao.inTransaction(new Transaction<SubscriptionBundle, BundleSqlDao>() {
+ @Override
+ public SubscriptionBundle inTransaction(BundleSqlDao bundlesDao, TransactionStatus status) {
+ bundlesDao.insertBundle(bundle, context);
+
+ AuditSqlDao auditSqlDao = bundlesDao.become(AuditSqlDao.class);
+ String bundleId = bundle.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleId, ChangeType.INSERT, context);
+
+ return bundle;
+ }
+ });
+ }
+
+ @Override
+ public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
+ Subscription subscription = subscriptionsDao.getSubscriptionFromId(subscriptionId.toString());
+ if (subscription == null) {
+ log.error(String.format(ErrorCode.ENT_INVALID_SUBSCRIPTION_ID.getFormat(), subscriptionId.toString()));
+ return null;
+ }
+
+ UUID bundleId = subscription.getBundleId();
+ if (bundleId == null) {
+ log.error(String.format(ErrorCode.ENT_GET_NO_BUNDLE_FOR_SUBSCRIPTION.getFormat(), subscriptionId.toString()));
+ return null;
+ }
+
+ SubscriptionBundle bundle = bundlesDao.getBundleFromId(bundleId.toString());
+ if (bundle == null) {
+ log.error(String.format(ErrorCode.ENT_GET_INVALID_BUNDLE_ID.getFormat(), bundleId.toString()));
+ return null;
+ }
+
+ return bundle.getAccountId();
+ }
+
+ @Override
+ public Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId) {
+ return getBaseSubscription(factory, bundleId, true);
+ }
+
+ @Override
+ public Subscription getSubscriptionFromId(final SubscriptionFactory factory, final UUID subscriptionId) {
+ return buildSubscription(factory, subscriptionsDao.getSubscriptionFromId(subscriptionId.toString()));
+ }
+
+ @Override
+ public List<Subscription> getSubscriptions(final SubscriptionFactory factory, final UUID bundleId) {
+ return buildBundleSubscriptions(factory, subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString()));
+ }
+
+ @Override
+ public List<Subscription> getSubscriptionsForKey(final SubscriptionFactory factory, final String bundleKey) {
+ SubscriptionBundle bundle = bundlesDao.getBundleFromKey(bundleKey);
+ if (bundle == null) {
+ return Collections.emptyList();
+ }
+ return getSubscriptions(factory, bundle.getId());
+ }
+
+ @Override
+ public void updateSubscription(final SubscriptionData subscription, final CallContext context) {
+
+ final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
+ final Date ptd = (subscription.getPaidThroughDate() != null) ? subscription.getPaidThroughDate().toDate() : null;
+
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+ @Override
+ public Void inTransaction(SubscriptionSqlDao transactionalDao,
+ TransactionStatus status) throws Exception {
+ transactionalDao.updateSubscription(subscription.getId().toString(), subscription.getActiveVersion(), ctd, ptd, context);
+
+ AuditSqlDao auditSqlDao = transactionalDao.become(AuditSqlDao.class);
+ String subscriptionId = subscription.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionId, ChangeType.UPDATE, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void createNextPhaseEvent(final UUID subscriptionId, final EntitlementEvent nextPhase, final CallContext context) {
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+ dao.insertEvent(nextPhase, context);
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, nextPhase.getId().toString(), ChangeType.INSERT, context);
+
+ recordFutureNotificationFromTransaction(dao,
+ nextPhase.getEffectiveDate(),
+ new EntitlementNotificationKey(nextPhase.getId()));
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public EntitlementEvent getEventById(UUID eventId) {
+ return eventsDao.getEventById(eventId.toString());
+ }
+
+ @Override
+ public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
+ return eventsDao.getEventsForSubscription(subscriptionId.toString());
+ }
+
+ @Override
+ public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId) {
+ Date now = clock.getUTCNow().toDate();
+ return eventsDao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+ }
+
+ @Override
+ public void createSubscription(final SubscriptionData subscription,
+ final List<EntitlementEvent> initialEvents, final CallContext context) {
+
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+
+ @Override
+ public Void inTransaction(SubscriptionSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ dao.insertSubscription(subscription, context);
+ // STEPH batch as well
+ EventSqlDao eventsDaoFromSameTransaction = dao.become(EventSqlDao.class);
+ List<String> eventIds = new ArrayList<String>();
+
+ for (final EntitlementEvent cur : initialEvents) {
+ eventsDaoFromSameTransaction.insertEvent(cur, context);
+ eventIds.add(cur.getId().toString()); // collect ids for batch audit log insert
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void recreateSubscription(final UUID subscriptionId,
+ final List<EntitlementEvent> recreateEvents, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : recreateEvents) {
+ dao.insertEvent(cur, context);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch audit insert
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void cancelSubscription(final UUID subscriptionId, final EntitlementEvent cancelEvent, final CallContext context, final int seqId) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+ cancelNextCancelEventFromTransaction(subscriptionId, dao, context);
+ cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+ dao.insertEvent(cancelEvent, context);
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ String cancelEventId = cancelEvent.getId().toString();
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, cancelEventId, ChangeType.INSERT, context);
+
+ recordFutureNotificationFromTransaction(dao,
+ cancelEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(cancelEvent.getId(), seqId));
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void uncancelSubscription(final UUID subscriptionId, final List<EntitlementEvent> uncancelEvents, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao dao,
+ TransactionStatus status) throws Exception {
+
+ UUID existingCancelId = null;
+ Date now = clock.getUTCNow().toDate();
+ List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+
+ for (EntitlementEvent cur : events) {
+ if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
+ if (existingCancelId != null) {
+ throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
+ }
+ existingCancelId = cur.getId();
+ }
+ }
+
+ if (existingCancelId != null) {
+ dao.unactiveEvent(existingCancelId.toString(), context);
+ String deactivatedEventId = existingCancelId.toString();
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : uncancelEvents) {
+ dao.insertEvent(cur, context);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch insert into audit log
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, deactivatedEventId, ChangeType.UPDATE, context);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ }
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public void changePlan(final UUID subscriptionId, final List<EntitlementEvent> changeEvents, final CallContext context) {
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+ @Override
+ public Void inTransaction(EventSqlDao dao, TransactionStatus status) throws Exception {
+ cancelNextChangeEventFromTransaction(subscriptionId, dao, context);
+ cancelNextPhaseEventFromTransaction(subscriptionId, dao, context);
+
+ List<String> eventIds = new ArrayList<String>();
+ for (final EntitlementEvent cur : changeEvents) {
+ dao.insertEvent(cur, context);
+ eventIds.add(cur.getId().toString()); // gather event ids for batch audit log insert
+
+ recordFutureNotificationFromTransaction(dao,
+ cur.getEffectiveDate(),
+ new EntitlementNotificationKey(cur.getId()));
+ }
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+ return null;
+ }
+ });
+ }
+
+ private void cancelNextPhaseEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.PHASE, null, context);
+ }
+
+ private void cancelNextChangeEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CHANGE, context);
+ }
+
+ private void cancelNextCancelEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao, final CallContext context) {
+ cancelFutureEventFromTransaction(subscriptionId, dao, EventType.API_USER, ApiEventType.CANCEL, context);
+ }
+
+ private void cancelFutureEventFromTransaction(final UUID subscriptionId, final EventSqlDao dao,
+ final EventType type, @Nullable final ApiEventType apiType,
+ final CallContext context) {
+
+ UUID futureEventId = null;
+ Date now = clock.getUTCNow().toDate();
+ List<EntitlementEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
+ for (EntitlementEvent cur : events) {
+ if (cur.getType() == type &&
+ (apiType == null || apiType == ((ApiEvent) cur).getEventType() )) {
+ if (futureEventId != null) {
+ throw new EntitlementError(
+ String.format("Found multiple future events for type %s for subscriptions %s",
+ type, subscriptionId.toString()));
+ }
+ futureEventId = cur.getId();
+ }
+ }
+
+ if (futureEventId != null) {
+ dao.unactiveEvent(futureEventId.toString(), context);
+
+ AuditSqlDao auditSqlDao = dao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, futureEventId.toString(), ChangeType.UPDATE, context);
+ }
+ }
+
+ private void updateCustomFieldsFromTransaction(final SubscriptionSqlDao transactionalDao,
+ final SubscriptionData subscription,
+ final CallContext context) {
+ customFieldDao.saveFields(transactionalDao, subscription.getId(), subscription.getObjectName(), subscription.getFieldList(), context);
+ }
+
+ private Subscription buildSubscription(final SubscriptionFactory factory, final Subscription input) {
+ if (input == null) {
+ return null;
+ }
+ List<Subscription> bundleInput = new ArrayList<Subscription>();
+ if (input.getCategory() == ProductCategory.ADD_ON) {
+ Subscription baseSubscription = getBaseSubscription(factory, input.getBundleId(), false);
+ bundleInput.add(baseSubscription);
+ bundleInput.add(input);
+ } else {
+ bundleInput.add(input);
+ }
+ List<Subscription> reloadedSubscriptions = buildBundleSubscriptions(factory, bundleInput);
+ for (Subscription cur : reloadedSubscriptions) {
+ if (cur.getId().equals(input.getId())) {
+ return cur;
+ }
+ }
+ throw new EntitlementError(String.format("Unexpected code path in buildSubscription"));
+ }
+
+ private List<Subscription> buildBundleSubscriptions(final SubscriptionFactory factory, final List<Subscription> input) {
+ // Make sure BasePlan -- if exists-- is first
+ Collections.sort(input, new Comparator<Subscription>() {
+ @Override
+ public int compare(Subscription o1, Subscription o2) {
+ if (o1.getCategory() == ProductCategory.BASE) {
+ return -1;
+ } else if (o2.getCategory() == ProductCategory.BASE) {
+ return 1;
+ } else {
+ return o1.getStartDate().compareTo(o2.getStartDate());
+ }
+ }
+ });
+
+ EntitlementEvent futureBaseEvent = null;
+ List<Subscription> result = new ArrayList<Subscription>(input.size());
+ for (Subscription cur : input) {
+
+ List<EntitlementEvent> events = eventsDao.getEventsForSubscription(cur.getId().toString());
+ Subscription reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+
+ switch (cur.getCategory()) {
+ case BASE:
+ Collection<EntitlementEvent> futureApiEvents = Collections2.filter(events, new Predicate<EntitlementEvent>() {
+ @Override
+ public boolean apply(EntitlementEvent input) {
+ return (input.getEffectiveDate().isAfter(clock.getUTCNow()) &&
+ ((input instanceof ApiEventCancel) || (input instanceof ApiEventChange)));
+ }
+ });
+ futureBaseEvent = (futureApiEvents.size() == 0) ? null : futureApiEvents.iterator().next();
+ break;
+
+ case ADD_ON:
+ Plan targetAddOnPlan = reloaded.getCurrentPlan();
+ String baseProductName = (futureBaseEvent instanceof ApiEventChange) ?
+ ((ApiEventChange) futureBaseEvent).getEventPlan() : null;
+
+ boolean createCancelEvent = (futureBaseEvent != null) &&
+ ((futureBaseEvent instanceof ApiEventCancel) ||
+ ((! addonUtils.isAddonAvailable(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan)) ||
+ (addonUtils.isAddonIncluded(baseProductName, futureBaseEvent.getEffectiveDate(), targetAddOnPlan))));
+
+ if (createCancelEvent) {
+ DateTime now = clock.getUTCNow();
+ EntitlementEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
+ .setSubscriptionId(reloaded.getId())
+ .setActiveVersion(((SubscriptionData) reloaded).getActiveVersion())
+ .setProcessedDate(now)
+ .setEffectiveDate(futureBaseEvent.getEffectiveDate())
+ .setRequestedDate(now)
+ // This event is only there to indicate the ADD_ON is future canceled, but it is not there
+ // on disk until the base plan cancellation becomes effective
+ .setFromDisk(false));
+
+ events.add(addOnCancelEvent);
+ // Finally reload subscription with full set of events
+ reloaded = factory.createSubscription(new SubscriptionBuilder((SubscriptionData) cur), events);
+ }
+ break;
+ default:
+ break;
+ }
+ loadCustomFields((SubscriptionData) reloaded);
+ result.add(reloaded);
+ }
+ return result;
+ }
+
+ @Override
+ public void migrate(final UUID accountId, final AccountMigrationData accountData, final CallContext context) {
+
+ eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
+
+ @Override
+ public Void inTransaction(EventSqlDao transEventDao,
+ TransactionStatus status) throws Exception {
+
+ SubscriptionSqlDao transSubDao = transEventDao.become(SubscriptionSqlDao.class);
+ BundleSqlDao transBundleDao = transEventDao.become(BundleSqlDao.class);
+
+ List<String> bundleIds = new ArrayList<String>();
+ List<String> subscriptionIds = new ArrayList<String>();
+ List<String> eventIds = new ArrayList<String>();
+
+ for (BundleMigrationData curBundle : accountData.getData()) {
+ SubscriptionBundleData bundleData = curBundle.getData();
+
+ for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
+
+ SubscriptionData subData = curSubscription.getData();
+ for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+ transEventDao.insertEvent(curEvent, context);
+ eventIds.add(curEvent.getId().toString()); // gather event ids for batch audit
+
+ recordFutureNotificationFromTransaction(transEventDao,
+ curEvent.getEffectiveDate(),
+ new EntitlementNotificationKey(curEvent.getId()));
+ }
+ transSubDao.insertSubscription(subData, context);
+ subscriptionIds.add(subData.getId().toString()); // gather subscription ids for batch audit
+ }
+ transBundleDao.insertBundle(bundleData, context);
+ bundleIds.add(bundleData.getId().toString()); // gather bundle ids for batch audit
+ }
+
+ // add audit records for bundles, subscriptions, and events
+ AuditSqlDao auditSqlDao = transBundleDao.become(AuditSqlDao.class);
+ auditSqlDao.insertAuditFromTransaction(SUBSCRIPTIONS_TABLE_NAME, subscriptionIds, ChangeType.INSERT, context);
+ auditSqlDao.insertAuditFromTransaction(BUNDLES_TABLE_NAME, bundleIds, ChangeType.INSERT, context);
+ auditSqlDao.insertAuditFromTransaction(ENTITLEMENT_EVENTS_TABLE_NAME, eventIds, ChangeType.INSERT, context);
+
+ return null;
+ }
+ });
+ }
+
+
+ private Subscription getBaseSubscription(final SubscriptionFactory factory, final UUID bundleId, boolean rebuildSubscription) {
+ List<Subscription> subscriptions = subscriptionsDao.getSubscriptionsFromBundleId(bundleId.toString());
+ for (Subscription cur : subscriptions) {
+ if (cur.getCategory() == ProductCategory.BASE) {
+ return rebuildSubscription ? buildSubscription(factory, cur) : cur;
+ }
+ }
+ return null;
+ }
+
+ private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
+ try {
+ NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+ Engine.NOTIFICATION_QUEUE_NAME);
+ subscriptionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
+ } catch (NoSuchNotificationQueue e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void saveCustomFields(final SubscriptionData subscription, final CallContext context) {
+ subscriptionsDao.inTransaction(new Transaction<Void, SubscriptionSqlDao>() {
+ @Override
+ public Void inTransaction(SubscriptionSqlDao transactionalDao,
+ TransactionStatus status) throws Exception {
+ updateCustomFieldsFromTransaction(transactionalDao, subscription, context);
+ return null;
+ }
+ });
+ }
+
+ private void loadCustomFields(final SubscriptionData subscription) {
+ CustomFieldSqlDao customFieldSqlDao = subscriptionsDao.become(CustomFieldSqlDao.class);
+ List<CustomField> fields = customFieldSqlDao.load(subscription.getId().toString(), subscription.getObjectName());
+ subscription.clearFields();
+ if (fields != null) {
+ subscription.setFields(fields);
+ }
+ }
}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index 5254517..89baed8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -24,6 +24,8 @@ import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
import com.ning.billing.entitlement.events.phase.PhaseEventData;
import com.ning.billing.entitlement.events.user.*;
import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
import com.ning.billing.util.dao.BinderBase;
import com.ning.billing.util.dao.MapperBase;
import org.joda.time.DateTime;
@@ -53,13 +55,16 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
public EntitlementEvent getEventById(@Bind("event_id") String eventId);
@SqlUpdate
- public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt);
+ public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt,
+ @CallContextBinder final CallContext context);
@SqlUpdate
- public void unactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
+ public void unactiveEvent(@Bind("event_id")String eventId,
+ @CallContextBinder final CallContext context);
@SqlUpdate
- public void reactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
+ public void reactiveEvent(@Bind("event_id")String eventId,
+ @CallContextBinder final CallContext context);
@SqlQuery
@Mapper(EventSqlMapper.class)
@@ -77,22 +82,18 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
String phaseName = null;
String priceListName = null;
String userType = null;
- String userToken = null;
if (evt.getType() == EventType.API_USER) {
ApiEvent userEvent = (ApiEvent) evt;
planName = userEvent.getEventPlan();
phaseName = userEvent.getEventPlanPhase();
priceListName = userEvent.getPriceList();
userType = userEvent.getEventType().toString();
- userToken = (userEvent.getUserToken() != null) ? userEvent.getUserToken().toString() : null;
} else {
phaseName = ((PhaseEvent) evt).getPhase();
}
stmt.bind("event_id", evt.getId().toString());
stmt.bind("event_type", evt.getType().toString());
stmt.bind("user_type", userType);
- stmt.bind("created_dt", getDate(evt.getProcessedDate()));
- stmt.bind("updated_dt", getDate(evt.getProcessedDate()));
stmt.bind("requested_dt", getDate(evt.getRequestedDate()));
stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
stmt.bind("subscription_id", evt.getSubscriptionId().toString());
@@ -101,7 +102,6 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
stmt.bind("plist_name", priceListName);
stmt.bind("current_version", evt.getActiveVersion());
stmt.bind("is_active", evt.isActive());
- stmt.bind("user_token", userToken);
}
}
@@ -114,7 +114,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
UUID id = UUID.fromString(r.getString("event_id"));
EventType eventType = EventType.valueOf(r.getString("event_type"));
ApiEventType userType = (eventType == EventType.API_USER) ? ApiEventType.valueOf(r.getString("user_type")) : null;
- DateTime createdDate = getDate(r, "created_dt");
+ DateTime createdDate = getDate(r, "created_date");
DateTime requestedDate = getDate(r, "requested_dt");
DateTime effectiveDate = getDate(r, "effective_dt");
UUID subscriptionId = UUID.fromString(r.getString("subscription_id"));
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index 0a64176..d2bb5b7 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -5,8 +5,6 @@ CREATE TABLE entitlement_events (
event_id char(36) NOT NULL,
event_type varchar(9) NOT NULL,
user_type varchar(25) DEFAULT NULL,
- created_dt datetime NOT NULL,
- updated_dt datetime NOT NULL,
requested_dt datetime NOT NULL,
effective_dt datetime NOT NULL,
subscription_id char(36) NOT NULL,
@@ -16,11 +14,14 @@ CREATE TABLE entitlement_events (
user_token char(36),
current_version int(11) DEFAULT 1,
is_active bool DEFAULT 1,
+ created_by varchar(50) NOT NULL,
+ created_date datetime NOT NULL,
+ updated_by varchar(50) NOT NULL,
+ updated_date datetime NOT NULL,
PRIMARY KEY(id)
) ENGINE=innodb;
CREATE INDEX idx_ent_1 ON entitlement_events(subscription_id,is_active,effective_dt);
-CREATE INDEX idx_ent_2 ON entitlement_events(subscription_id,effective_dt,created_dt,requested_dt,id);
-
+CREATE INDEX idx_ent_2 ON entitlement_events(subscription_id,effective_dt,created_date,requested_dt,id);
DROP TABLE IF EXISTS subscriptions;
CREATE TABLE subscriptions (
@@ -32,6 +33,10 @@ CREATE TABLE subscriptions (
active_version int(11) DEFAULT 1,
ctd_dt datetime DEFAULT NULL,
ptd_dt datetime DEFAULT NULL,
+ created_by varchar(50) NOT NULL,
+ created_date datetime NOT NULL,
+ updated_by varchar(50) NOT NULL,
+ updated_date datetime NOT NULL,
PRIMARY KEY(id)
) ENGINE=innodb;
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index a1aae54..cd5afbf 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -1,22 +1,27 @@
group EventSqlDao;
-getEventById(event_id) ::= <<
- select
- id
- , event_id
- , event_type
- , user_type
- , created_dt
- , updated_dt
- , requested_dt
- , effective_dt
- , subscription_id
- , plan_name
- , phase_name
- , plist_name
- , current_version
- , user_token
- , is_active
+fields(prefix) ::= <<
+ <prefix>event_id,
+ <prefix>event_type,
+ <prefix>user_type,
+ <prefix>requested_dt,
+ <prefix>effective_dt,
+ <prefix>subscription_id,
+ <prefix>plan_name,
+ <prefix>phase_name,
+ <prefix>plist_name,
+ <prefix>user_token,
+ <prefix>current_version,
+ <prefix>is_active,
+ <prefix>created_by,
+ <prefix>created_date,
+ <prefix>updated_by,
+ <prefix>updated_date
+>>
+
+
+getEventById() ::= <<
+ select id, <fields()>
from entitlement_events
where
event_id = :event_id
@@ -24,76 +29,52 @@ getEventById(event_id) ::= <<
>>
insertEvent() ::= <<
- insert into entitlement_events (
- event_id
- , event_type
- , user_type
- , created_dt
- , updated_dt
- , requested_dt
- , effective_dt
- , subscription_id
- , plan_name
- , phase_name
- , plist_name
- , current_version
- , user_token
- , is_active
- ) values (
- :event_id
- , :event_type
- , :user_type
- , :created_dt
- , :updated_dt
- , :requested_dt
- , :effective_dt
- , :subscription_id
- , :plan_name
- , :phase_name
- , :plist_name
- , :current_version
- , :user_token
- , :is_active
+ insert into entitlement_events (<fields()>)
+ values (
+ :event_id
+ , :event_type
+ , :user_type
+ , :requested_dt
+ , :effective_dt
+ , :subscription_id
+ , :plan_name
+ , :phase_name
+ , :plist_name
+ , :userToken
+ , :current_version
+ , :is_active
+ , :userName
+ , :createdDate
+ , :userName
+ , :updatedDate
);
>>
-unactiveEvent(event_id, now) ::= <<
+unactiveEvent() ::= <<
update entitlement_events
set
is_active = 0
- , updated_dt = :now
+ , updated_by = :userName
+ , updated_date = :updatedDate
where
event_id = :event_id
;
>>
-reactiveEvent(event_id, now) ::= <<
+reactiveEvent() ::= <<
update entitlement_events
set
is_active = 1
- , updated_dt = :now
+ , updated_by = :userName
+ , updated_date = :updatedDate
where
event_id = :event_id
;
>>
-getFutureActiveEventForSubscription(subscription_id, now) ::= <<
- select
- id
- , event_id
- , event_type
- , user_type
- , created_dt
- , updated_dt
- , requested_dt
- , effective_dt
- , subscription_id
- , plan_name
- , phase_name
- , plist_name
- , current_version
- , user_token
- , is_active
+
+getFutureActiveEventForSubscription() ::= <<
+ select id, <fields()>
from entitlement_events
where
subscription_id = :subscription_id
@@ -101,35 +82,20 @@ getFutureActiveEventForSubscription(subscription_id, now) ::= <<
and effective_dt > :now
order by
effective_dt asc
- , created_dt asc
+ , created_date asc
, requested_dt asc
, id asc
;
>>
getEventsForSubscription(subscription_id) ::= <<
- select
- id
- , event_id
- , event_type
- , user_type
- , created_dt
- , updated_dt
- , requested_dt
- , effective_dt
- , subscription_id
- , plan_name
- , phase_name
- , plist_name
- , current_version
- , user_token
- , is_active
+ select id, <fields()>
from entitlement_events
where
subscription_id = :subscription_id
order by
effective_dt asc
- , created_dt asc
+ , created_date asc
, requested_dt asc
, id asc
;
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
index 094bdf1..780c06a 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.sql.stg
@@ -10,6 +10,10 @@ insertSubscription() ::= <<
, active_version
, ctd_dt
, ptd_dt
+ , created_by
+ , created_date
+ , updated_by
+ , updated_date
) values (
:id
, :bundle_id
@@ -18,7 +22,11 @@ insertSubscription() ::= <<
, :bundle_start_dt
, :active_version
, :ctd_dt
- , :ptd_dt
+ , :ptd_dt
+ , :userName
+ , :createdDate
+ , :userName
+ , :updatedDate
);
>>
@@ -58,6 +66,8 @@ updateSubscription(id, active_version, ctd_dt, ptd_dt) ::= <<
active_version = :active_version
, ctd_dt = :ctd_dt
, ptd_dt = :ptd_dt
+ , updated_by = :userName
+ , updated_date = :updatedDate
where id = :id
;
>>
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index 808e457..0a65693 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -189,10 +189,13 @@ public abstract class TestApiBase {
log.warn("RESET TEST FRAMEWORK\n\n");
- testListener.reset();
+ if (testListener != null) {
+ testListener.reset();
+ }
clock.resetDeltaFromReality();
((MockEntitlementDao) dao).reset();
+
try {
busService.getBus().register(testListener);
UUID accountId = UUID.randomUUID();
invoice/pom.xml 10(+9 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index ccef93e..8db3258 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
@@ -39,13 +39,21 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.ning.billing</groupId>
+ <artifactId>killbill-entitlement</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.ning.billing</groupId>
<artifactId>killbill-entitlement</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
<artifactId>killbill-catalog</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>com.ning.billing</groupId>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
index 554dc62..7741f00 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoicingConfiguration.java
@@ -20,7 +20,7 @@ import java.math.BigDecimal;
public class InvoicingConfiguration {
private final static int roundingMethod = BigDecimal.ROUND_HALF_UP;
- private final static int numberOfDecimals = 4;
+ private final static int numberOfDecimals = 2;
public static int getRoundingMode() {
return roundingMethod;
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 0701aa2..f8472a9 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -14,7 +14,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index fc660f7..d55d8d9 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index e565ccc..0d9c19a 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -23,7 +23,6 @@ import java.util.UUID;
import javax.annotation.Nullable;
-import com.ning.billing.util.callcontext.CallContext;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -40,6 +39,7 @@ import com.ning.billing.payment.RetryService;
import com.ning.billing.payment.dao.PaymentDao;
import com.ning.billing.payment.provider.PaymentProviderPlugin;
import com.ning.billing.payment.provider.PaymentProviderPluginRegistry;
+import com.ning.billing.util.callcontext.CallContext;
public class DefaultPaymentApi implements PaymentApi {
private final PaymentProviderPluginRegistry pluginRegistry;
@@ -184,14 +184,7 @@ public class DefaultPaymentApi implements PaymentApi {
Invoice invoice = invoicePaymentApi.getInvoice(UUID.fromString(invoiceId));
if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0 ) {
- // TODO: send a notification that invoice was ignored?
- log.info("Received invoice for payment with balance of 0 {} ", invoice);
- Either<PaymentErrorEvent, PaymentInfoEvent> result = Either.left((PaymentErrorEvent) new DefaultPaymentError("invoice_balance_0",
- "Invoice balance was 0 or less",
- account.getId(),
- UUID.fromString(invoiceId),
- context.getUserToken()));
- processedPaymentsOrErrors.add(result);
+ log.debug("Received invoice for payment with balance of 0 {} ", invoice);
}
else if (invoice.isMigrationInvoice()) {
log.info("Received invoice for payment that is a migration invoice - don't know how to handle those yet: {}", invoice);
@@ -312,9 +305,11 @@ public class DefaultPaymentApi implements PaymentApi {
}
@Override
- public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account, List<String> invoiceIds, CallContext context) {
- //TODO
- throw new UnsupportedOperationException();
+ public List<Either<PaymentErrorEvent, PaymentInfoEvent>> createRefund(Account account,
+ List<String> invoiceIds,
+ CallContext context) {
+ final PaymentProviderPlugin plugin = getPaymentProviderPlugin(account);
+ return plugin.processRefund(account);
}
@Override
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index 23dfe51..8e28b80 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -110,4 +110,10 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
return Either.right(null);
}
+ @Override
+ public List<Either<PaymentErrorEvent, PaymentInfoEvent>> processRefund(Account account) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
index c46d24d..2daa160 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/PaymentProviderPlugin.java
@@ -40,7 +40,8 @@ public interface PaymentProviderPlugin {
Either<PaymentErrorEvent, PaymentMethodInfo> updatePaymentMethod(String accountKey, PaymentMethodInfo paymentMethodInfo);
Either<PaymentErrorEvent, Void> deletePaymentMethod(String accountKey, String paymentMethodId);
+
Either<PaymentErrorEvent, Void> updatePaymentProviderAccountExistingContact(Account account);
Either<PaymentErrorEvent, Void> updatePaymentProviderAccountWithNewContact(Account account);
-
+ List<Either<PaymentErrorEvent, PaymentInfoEvent>> processRefund(Account account);
}
diff --git a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
index c4fd34a..ac2b9f3 100644
--- a/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
+++ b/payment/src/test/java/com/ning/billing/payment/api/TestPaymentApi.java
@@ -22,6 +22,7 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@@ -80,7 +81,7 @@ public abstract class TestPaymentApi {
final DateTime now = new DateTime(DateTimeZone.UTC);
final Account account = testHelper.createTestCreditCardAccount();
final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
- final BigDecimal amount = new BigDecimal("10.00");
+ final BigDecimal amount = new BigDecimal("10.0011");
final UUID subscriptionId = UUID.randomUUID();
invoice.addInvoiceItem(new RecurringInvoiceItem(invoice.getId(), account.getId(),
@@ -100,7 +101,7 @@ public abstract class TestPaymentApi {
PaymentInfoEvent paymentInfo = results.get(0).getRight();
assertNotNull(paymentInfo.getPaymentId());
- assertTrue(paymentInfo.getAmount().compareTo(amount) == 0);
+ assertTrue(paymentInfo.getAmount().compareTo(amount.setScale(2, RoundingMode.HALF_EVEN)) == 0);
assertNotNull(paymentInfo.getPaymentNumber());
assertFalse(paymentInfo.getStatus().equals("Error"));
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 074676b..c027985 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -24,13 +24,11 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
-import com.google.inject.Inject;
-import com.ning.billing.util.clock.Clock;
import org.apache.commons.lang.RandomStringUtils;
-import org.joda.time.DateTime;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
+import com.google.inject.Inject;
import com.ning.billing.account.api.Account;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.payment.api.CreditCardPaymentMethodInfo;
@@ -42,6 +40,7 @@ import com.ning.billing.payment.api.PaymentInfoEvent;
import com.ning.billing.payment.api.PaymentMethodInfo;
import com.ning.billing.payment.api.PaymentProviderAccount;
import com.ning.billing.payment.api.PaypalPaymentMethodInfo;
+import com.ning.billing.util.clock.Clock;
public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
private final AtomicBoolean makeNextInvoiceFail = new AtomicBoolean(false);
@@ -268,8 +267,11 @@ public class MockPaymentProviderPlugin implements PaymentProviderPlugin {
@Override
public Either<PaymentErrorEvent, Void> updatePaymentProviderAccountWithNewContact(Account account) {
- // nothing to do here
return Either.right(null);
}
+ @Override
+ public List<Either<PaymentErrorEvent, PaymentInfoEvent>> processRefund(Account account) {
+ return null;
+ }
}
pom.xml 5(+2 -3)
diff --git a/pom.xml b/pom.xml
index b50bc5f..e0b602f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,8 +7,7 @@
OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for
the specific language governing permissions and limitations ~ under the License. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
@@ -18,7 +17,7 @@
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
<packaging>pom</packaging>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
<url>http://github.com/ning/killbill</url>
server/pom.xml 2(+1 -1)
diff --git a/server/pom.xml b/server/pom.xml
index 9cf0a9b..ea39345 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -14,7 +14,7 @@
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-server</artifactId>
util/pom.xml 5(+2 -3)
diff --git a/util/pom.xml b/util/pom.xml
index dc5c251..e0d256c 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -8,13 +8,12 @@
OR CONDITIONS OF ANY KIND, either express or implied. See the ~ License for
the specific language governing permissions and limitations ~ under the License. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ning.billing</groupId>
<artifactId>killbill</artifactId>
- <version>0.1.8-SNAPSHOT</version>
+ <version>0.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>