killbill-memoizeit

closes #90

9/23/2013 8:35:37 PM

Details

diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index 6ccae6e..c61053e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -16,8 +16,12 @@
 
 package com.ning.billing.entitlement.api;
 
+import java.util.Collection;
+import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
@@ -42,6 +46,9 @@ import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.entity.EntityBase;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+
 public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     protected final EntitlementDateHelper dateHelper;
@@ -224,6 +231,37 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
     }
 
     @Override
+    public void uncancelEntitlement(final CallContext callContext) throws EntitlementApiException {
+        if (state == EntitlementState.CANCELLED || subscriptionBase.getState() == EntitlementState.CANCELLED) {
+            throw new EntitlementApiException(ErrorCode.SUB_CANCEL_BAD_STATE, getId(), EntitlementState.CANCELLED);
+        }
+        final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
+        final List<BlockingState> blockingStates = blockingStateDao.getBlockingHistoryForService(getId(), EntitlementService.ENTITLEMENT_SERVICE_NAME, contextWithValidAccountRecordId);
+        final Collection<BlockingState> filtered = Collections2.filter(blockingStates, new Predicate<BlockingState>() {
+            @Override
+            public boolean apply(final BlockingState input) {
+                return EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(input.getService()) && input.getEffectiveDate().isAfter(clock.getUTCNow());
+            }
+        });
+        final BlockingState futureCancellation = filtered.iterator().hasNext() ? filtered.iterator().next() : null;
+        if (futureCancellation == null) {
+            return;
+        }
+
+        // Reactivate entitlement
+        blockingStateDao.unactiveBlockingState(futureCancellation.getId(), contextWithValidAccountRecordId);
+
+        // If billing was previously cancelled, reactivate
+        if (subscriptionBase.getFutureEndDate() != null) {
+            try {
+                subscriptionBase.uncancel(callContext);
+            } catch (SubscriptionBaseApiException e) {
+                throw new EntitlementApiException(e);
+            }
+        }
+    }
+
+    @Override
     public Entitlement cancelEntitlementWithDateOverrideBillingPolicy(final LocalDate localCancelDate, final BillingActionPolicy billingPolicy, final CallContext callContext) throws EntitlementApiException {
 
         if (state == EntitlementState.CANCELLED) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
index ade0cfb..de0675f 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateDao.java
@@ -90,4 +90,13 @@ public interface BlockingStateDao {
      * @param context
      */
     public void setBlockingState(BlockingState state, Clock clock, InternalCallContext context);
+
+    /**
+     * Unactive the blocking state
+     *
+     * @param blockableId
+     * @param context
+     */
+    public void unactiveBlockingState(UUID blockableId, final InternalCallContext context);
+
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
index 0be4794..1bdf533 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateModelDao.java
@@ -38,9 +38,10 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
     private final Boolean blockEntitlement;
     private final Boolean blockBilling;
     private final DateTime effectiveDate;
+    private boolean isActive;
 
     public BlockingStateModelDao(final UUID id, final UUID blockableId, final BlockingStateType blockingStateType, final String state, final String service, final Boolean blockChange, final Boolean blockEntitlement,
-                                 final Boolean blockBilling, final DateTime effectiveDate, final DateTime createDate, final DateTime updateDate) {
+                                 final Boolean blockBilling, final DateTime effectiveDate, final boolean isActive, final DateTime createDate, final DateTime updateDate) {
         super(id, createDate, updateDate);
         this.blockableId = blockableId;
         this.effectiveDate = effectiveDate;
@@ -50,11 +51,12 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
         this.blockChange = blockChange;
         this.blockEntitlement = blockEntitlement;
         this.blockBilling = blockBilling;
+        this.isActive = isActive;
     }
 
     public BlockingStateModelDao(final BlockingState src, InternalCallContext context) {
         this(src.getId(), src.getBlockedId(), src.getType(), src.getStateName(), src.getService(), src.isBlockChange(),
-             src.isBlockEntitlement(), src.isBlockBilling(), src.getEffectiveDate(), context.getCreatedDate(), context.getUpdatedDate());
+             src.isBlockEntitlement(), src.isBlockBilling(), src.getEffectiveDate(), true, context.getCreatedDate(), context.getUpdatedDate());
     }
 
     public UUID getBlockableId() {
@@ -89,6 +91,15 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
         return effectiveDate;
     }
 
+    // TODO required for jdbi binder
+    public boolean getIsActive() {
+        return isActive;
+    }
+
+    public boolean isActive() {
+        return isActive;
+    }
+
     public static BlockingState toBlockingState(BlockingStateModelDao src) {
         if (src == null) {
             return null;
@@ -117,6 +128,7 @@ public class BlockingStateModelDao extends EntityBase implements EntityModelDao<
         sb.append(", blockChange=").append(blockChange);
         sb.append(", blockEntitlement=").append(blockEntitlement);
         sb.append(", blockBilling=").append(blockBilling);
+        sb.append(", isActive=").append(isActive);
         sb.append('}');
         return sb.toString();
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
index 6df54b4..d560cc3 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/BlockingStateSqlDao.java
@@ -27,13 +27,17 @@ import org.skife.jdbi.v2.StatementContext;
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
 import org.skife.jdbi.v2.tweak.ResultSetMapper;
 
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.dao.MapperBase;
+import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 import com.ning.billing.util.svcapi.junction.DefaultBlockingState;
@@ -71,6 +75,12 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
                                                                    @BindBean final InternalTenantContext context);
 
 
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    public void unactiveEvent(@Bind("id") String id,
+                              @BindBean final InternalCallContext context);
+
+
     public class BlockingHistorySqlMapper extends MapperBase implements ResultSetMapper<BlockingStateModelDao> {
 
         @Override
@@ -84,6 +94,7 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
             final boolean blockChange;
             final boolean blockEntitlement;
             final boolean blockBilling;
+            final boolean isActive;
             final DateTime effectiveDate;
             final DateTime createdDate;
             final BlockingStateType type;
@@ -96,9 +107,10 @@ public interface BlockingStateSqlDao extends EntitySqlDao<BlockingStateModelDao,
             blockChange = r.getBoolean("block_change");
             blockEntitlement = r.getBoolean("block_entitlement");
             blockBilling = r.getBoolean("block_billing");
+            isActive = r.getBoolean("is_active");
             effectiveDate = getDateTime(r, "effective_date");
             createdDate = getDateTime(r, "created_date");
-            return new BlockingStateModelDao(id, blockableId, type, stateName, service, blockChange, blockEntitlement, blockBilling, effectiveDate, createdDate, createdDate);
+            return new BlockingStateModelDao(id, blockableId, type, stateName, service, blockChange, blockEntitlement, blockBilling, effectiveDate, isActive, createdDate, createdDate);
         }
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
index e4d9587..5a45752 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -154,4 +154,16 @@ public class DefaultBlockingStateDao implements BlockingStateDao {
             }
         });
     }
+
+    @Override
+    public void unactiveBlockingState(final UUID id, final InternalCallContext context) {
+        transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
+            @Override
+            public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final BlockingStateSqlDao sqlDao = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class);
+                sqlDao.unactiveEvent(id.toString() , context);
+                return null;
+            }
+        });
+    }
 }
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 94ad70e..8cc83d5 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -3,6 +3,8 @@ group BlockingStateSqlDao: EntitySqlDao;
 
 tableName() ::= "blocking_states"
 
+andCheckSoftDeletionWithComma(prefix) ::= "and <prefix>is_active"
+
 tableFields(prefix) ::= <<
   <prefix>blockable_id
 , <prefix>type
@@ -12,6 +14,7 @@ tableFields(prefix) ::= <<
 , <prefix>block_entitlement
 , <prefix>block_billing
 , <prefix>effective_date
+, <prefix>is_active
 , <prefix>created_date
 >>
 
@@ -25,6 +28,7 @@ tableValues() ::= <<
 , :blockEntitlement
 , :blockBilling
 , :effectiveDate
+, :isActive
 , :createdDate
 >>
 
@@ -37,6 +41,7 @@ from
 where blockable_id = :blockableId
 and service = :service
 and effective_date \<= :effectiveDate
+and is_active
 <AND_CHECK_TENANT()>
 -- We want the current state, hence the order desc and limit 1
 order by record_id desc
@@ -55,6 +60,7 @@ getBlockingState() ::= <<
          from blocking_states
          where blockable_id = :blockableId
          and effective_date \<= :effectiveDate
+         and is_active
          <AND_CHECK_TENANT()>
          group by service
  ) tmp
@@ -70,6 +76,7 @@ from
 <tableName()>
 where blockable_id = :blockableId
 and service = :service
+and is_active
 <AND_CHECK_TENANT()>
 order by record_id asc
 ;
@@ -81,6 +88,7 @@ select
 from
 <tableName()>
 where blockable_id = :blockableId
+and is_active
 <AND_CHECK_TENANT()>
 order by record_id asc
 ;
@@ -92,7 +100,17 @@ select
 from
 <tableName()>
 where blockable_id = :blockableId
+and is_active
 <AND_CHECK_TENANT()>
 order by record_id asc
 ;
 >>
+
+unactiveEvent() ::= <<
+update
+<tableName()>
+set is_active = 0
+where id = :id
+<AND_CHECK_TENANT()>
+;
+>>
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 cfa82ed..a4061e9 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -12,6 +12,7 @@ CREATE TABLE blocking_states (
     block_entitlement bool NOT NULL,
     block_billing bool NOT NULL,
     effective_date datetime NOT NULL,
+    is_active bool DEFAULT 1,
     created_date datetime NOT NULL,
     account_record_id int(11) unsigned default null,
     tenant_record_id int(11) unsigned default null,
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlement.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlement.java
index 655ceec..2f77d9a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlement.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestDefaultEntitlement.java
@@ -97,6 +97,41 @@ public class TestDefaultEntitlement extends EntitlementTestSuiteWithEmbeddedDB {
             assertEquals(entitlement3.getState(), EntitlementState.CANCELLED);
             assertEquals(entitlement3.getEffectiveEndDate(), cancelDate);
 
+        } catch (EntitlementApiException e) {
+            Assert.fail("Test failed " + e.getMessage());
+        } catch (AccountApiException e) {
+            Assert.fail("Test failed " + e.getMessage());
+        }
+    }
+
+
+    @Test(groups = "slow")
+    public void testUncancel() {
+
+        try {
+            final LocalDate initialDate = new LocalDate(2013, 8, 7);
+            clock.setDay(initialDate);
+
+            final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+            // Create entitlement and check each field
+            final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+            assertEquals(entitlement.getState(), EntitlementState.ACTIVE);
+
+            clock.addDays(5);
+            final LocalDate cancelDate = new LocalDate(clock.getUTCToday().plusDays(1));
+            entitlement.cancelEntitlementWithDate(cancelDate, true, callContext);
+            final Entitlement entitlement2 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+            assertEquals(entitlement2.getState(), EntitlementState.ACTIVE);
+            assertEquals(entitlement2.getEffectiveEndDate(), cancelDate);
+
+            entitlement2.uncancelEntitlement(callContext);
+
+            clock.addDays(1);
+            final Entitlement entitlement3 = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+            assertEquals(entitlement3.getState(), EntitlementState.ACTIVE);
 
         } catch (EntitlementApiException e) {
             Assert.fail("Test failed " + e.getMessage());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
index 81fae38..0791ec2 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
@@ -116,6 +116,11 @@ public class MockBlockingStateDao implements BlockingStateDao {
         blockingStates.get(state.getBlockedId()).add(state);
     }
 
+    @Override
+    public void unactiveBlockingState(final UUID blockableId, final InternalCallContext context) {
+        throw new UnsupportedOperationException();
+    }
+
     public synchronized void setBlockingStates(final UUID blockedId, final List<BlockingState> states) {
         blockingStates.put(blockedId, states);
     }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/EntitlementResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/EntitlementResource.java
index f1f6540..6378fa1 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/EntitlementResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/EntitlementResource.java
@@ -230,14 +230,10 @@ public class EntitlementResource extends JaxRsResourceBase {
                                             @HeaderParam(HDR_REASON) final String reason,
                                             @HeaderParam(HDR_COMMENT) final String comment,
                                             @javax.ws.rs.core.Context final HttpServletRequest request) throws EntitlementApiException {
-
-        throw new UnsupportedOperationException("Call not implemented");
-/*
         final UUID uuid = UUID.fromString(entitlementId);
         final Entitlement current = entitlementApi.getEntitlementForId(uuid, context.createContext(createdBy, reason, comment, request));
-        current.uncancel(context.createContext(createdBy, reason, comment, request));
+        current.uncancelEntitlement(context.createContext(createdBy, reason, comment, request));
         return Response.status(Status.OK).build();
-        */
     }
 
     @DELETE
@@ -249,7 +245,7 @@ public class EntitlementResource extends JaxRsResourceBase {
                                           @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("5") final long timeoutSec,
                                           @QueryParam(QUERY_ENTITLEMENT_POLICY) final String entitlementPolicyString,
                                           @QueryParam(QUERY_BILLING_POLICY) final String billingPolicyString,
-                                          @QueryParam(QUERY_USE_REQUESTED_DATE_FOR_BILLING) @DefaultValue("true") final Boolean useRequestedDateForBilling,
+                                          @QueryParam(QUERY_USE_REQUESTED_DATE_FOR_BILLING) @DefaultValue("false") final Boolean useRequestedDateForBilling,
                                           @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                           @HeaderParam(HDR_REASON) final String reason,
                                           @HeaderParam(HDR_COMMENT) final String comment,
@@ -264,7 +260,7 @@ public class EntitlementResource extends JaxRsResourceBase {
             @Override
             public Response doOperation(final CallContext ctx)
                     throws EntitlementApiException, InterruptedException,
-                           TimeoutException, AccountApiException {
+                           TimeoutException, AccountApiException, SubscriptionApiException {
                 final UUID uuid = UUID.fromString(entitlementId);
 
                 final Entitlement current = entitlementApi.getEntitlementForId(uuid, ctx);
@@ -284,7 +280,12 @@ public class EntitlementResource extends JaxRsResourceBase {
                     final BillingActionPolicy billingPolicy = BillingActionPolicy.valueOf(billingPolicyString.toUpperCase());
                     newEntitlement = current.cancelEntitlementWithPolicyOverrideBillingPolicy(entitlementPolicy, billingPolicy, ctx);
                 }
-                isImmediateOp = newEntitlement.getState() == EntitlementState.ACTIVE;
+
+                final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(newEntitlement.getId(), ctx);
+
+                final LocalDate nowInAccountTimeZone = new LocalDate(clock.getUTCNow(), subscription.getBillingEndDate().getChronology().getZone());
+                isImmediateOp = subscription.getBillingEndDate() != null &&
+                                !subscription.getBillingEndDate().isAfter(nowInAccountTimeZone);
                 return Response.status(Status.OK).build();
             }
 
@@ -346,7 +347,7 @@ public class EntitlementResource extends JaxRsResourceBase {
 
     private interface EntitlementCallCompletionCallback<T> {
 
-        public T doOperation(final CallContext ctx) throws EntitlementApiException, InterruptedException, TimeoutException, AccountApiException;
+        public T doOperation(final CallContext ctx) throws EntitlementApiException, InterruptedException, TimeoutException, AccountApiException, SubscriptionApiException;
 
         public boolean isImmOperation();
 
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestEntitlement.java b/server/src/test/java/com/ning/billing/jaxrs/TestEntitlement.java
index 6b66dbb..f8d056e 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestEntitlement.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestEntitlement.java
@@ -69,22 +69,22 @@ public class TestEntitlement extends TestJaxrsBase {
         final String newProductName = "Assault-Rifle";
 
         final SubscriptionJson newInput = new SubscriptionJson(null,
-                                                                                   null,
-                                                                                   entitlementJson.getSubscriptionId(),
-                                                                                   null,
-                                                                                   null,
-                                                                                   newProductName,
-                                                                                   entitlementJson.getProductCategory(),
-                                                                                   entitlementJson.getBillingPeriod(),
-                                                                                   entitlementJson.getPriceList(),
-                                                                                   null,
-                                                                                   null,
-                                                                                   null,
-                                                                                   null,
-                                                                                   null,
-                                                                                   null,
-                                                                                   null,
-                                                                                   null);
+                                                               null,
+                                                               entitlementJson.getSubscriptionId(),
+                                                               null,
+                                                               null,
+                                                               newProductName,
+                                                               entitlementJson.getProductCategory(),
+                                                               entitlementJson.getBillingPeriod(),
+                                                               entitlementJson.getPriceList(),
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               null,
+                                                               null);
         baseJson = mapper.writeValueAsString(newInput);
 
         final Map<String, String> queryParams = getQueryParamsForCallCompletion(CALL_COMPLETION_TIMEOUT_SEC);
@@ -99,7 +99,7 @@ public class TestEntitlement extends TestJaxrsBase {
 
         crappyWaitForLackOfProperSynchonization();
 
-        // Cancel EOT
+        // Cancel IMM (Billing EOT)
         uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + entitlementJson.getSubscriptionId();
         response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
         assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
@@ -113,19 +113,64 @@ public class TestEntitlement extends TestJaxrsBase {
         objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
         assertNotNull(objFromJson.getCancelledDate());
         assertTrue(objFromJson.getCancelledDate().compareTo(new LocalDate(clock.getUTCNow())) == 0);
+    }
+
+
+    @Test(groups = "slow")
+    public void testEntitlementUncancel() throws Exception {
+
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final AccountJson accountJson = createAccountWithDefaultPaymentMethod("xil", "shdxilhkkl", "xil@yahoo.com");
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+
+        final SubscriptionJson entitlementJson = createEntitlement(accountJson.getAccountId(), "99999", productName, ProductCategory.BASE.toString(), term.toString(), true);
+
+        String uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + entitlementJson.getSubscriptionId();
+
+        // Retrieves with GET
+        Response response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        String baseJson = response.getResponseBody();
+        SubscriptionJson objFromJson = mapper.readValue(baseJson, SubscriptionJson.class);
+        Assert.assertTrue(objFromJson.equals(entitlementJson));
+
+        // MOVE AFTER TRIAL
+        final Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
+        clock.addDeltaFromReality(it.toDurationMillis());
+
+        crappyWaitForLackOfProperSynchonization();
+
+        // Cancel EOT
+        final Map<String, String> queryParams = getQueryParamsForCallCompletion(CALL_COMPLETION_TIMEOUT_SEC);
+        queryParams.put(JaxrsResource.QUERY_BILLING_POLICY, "END_OF_TERM");
+        queryParams.put(JaxrsResource.QUERY_ENTITLEMENT_POLICY, "END_OF_TERM");
+
+        uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + entitlementJson.getSubscriptionId();
+        response = doDelete(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC * 10000);
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+
+        // Retrieves to check EndDate
+        uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + entitlementJson.getSubscriptionId();
+        response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
+
+        assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
+        baseJson = response.getResponseBody();
 
-    /*
         uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + entitlementJson.getSubscriptionId() + "/uncancel";
         response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
         Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
-        */
     }
 
+
     @Test(groups = "slow")
     public void testWithNonExistentEntitlement() throws Exception {
         final String uri = JaxrsResource.ENTITLEMENTS_PATH + "/" + UUID.randomUUID().toString();
         final SubscriptionJson subscriptionJson = new SubscriptionJson(null, null, UUID.randomUUID().toString(), null, null, "Pistol", ProductCategory.BASE.toString(), BillingPeriod.MONTHLY.toString(),
-                                                                                           PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null, null, null, null);
+                                                                       PriceListSet.DEFAULT_PRICELIST_NAME, null, null, null, null, null, null, null, null);
         final String baseJson = mapper.writeValueAsString(subscriptionJson);
 
         Response response = doPut(uri, baseJson, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
@@ -161,16 +206,16 @@ public class TestEntitlement extends TestJaxrsBase {
 
         // Change billing period immediately
         final SubscriptionJson newInput = new SubscriptionJson(null,
-                                                                                   null,
-                                                                                   SubscriptionJson.getSubscriptionId(),
-                                                                                   null,
-                                                                                   null,
-                                                                                   SubscriptionJson.getProductName(),
-                                                                                   SubscriptionJson.getProductCategory(),
-                                                                                   BillingPeriod.MONTHLY.toString(),
-                                                                                   SubscriptionJson.getPriceList(),
-                                                                                   SubscriptionJson.getCancelledDate(),
-                                                                                   null, null, null, null, null, null, null);
+                                                               null,
+                                                               SubscriptionJson.getSubscriptionId(),
+                                                               null,
+                                                               null,
+                                                               SubscriptionJson.getProductName(),
+                                                               SubscriptionJson.getProductCategory(),
+                                                               BillingPeriod.MONTHLY.toString(),
+                                                               SubscriptionJson.getPriceList(),
+                                                               SubscriptionJson.getCancelledDate(),
+                                                               null, null, null, null, null, null, null);
         baseJson = mapper.writeValueAsString(newInput);
         final Map<String, String> queryParams = getQueryParamsForCallCompletion(CALL_COMPLETION_TIMEOUT_SEC);
         queryParams.put(JaxrsResource.QUERY_BILLING_POLICY, "immediate");