Search in sources :

Example 21 with Entitlement

use of org.killbill.billing.entitlement.api.Entitlement in project killbill by killbill.

the class SubscriptionResource method createEntitlementWithAddOns.

@TimedResource
@POST
@Path("/createEntitlementWithAddOns")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Create an entitlement with addOn products")
@ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid entitlement supplied") })
public Response createEntitlementWithAddOns(final List<SubscriptionJson> entitlements, @QueryParam(QUERY_REQUESTED_DT) final String requestedDate, /* This is deprecated, only used for backward compatibility */
@QueryParam(QUERY_ENTITLEMENT_REQUESTED_DT) final String entitlementDate, @QueryParam(QUERY_BILLING_REQUESTED_DT) final String billingDate, @QueryParam(QUERY_MIGRATED) @DefaultValue("false") final Boolean isMigrated, @QueryParam(QUERY_CALL_COMPLETION) @DefaultValue("false") final Boolean callCompletion, @QueryParam(QUERY_CALL_TIMEOUT) @DefaultValue("3") final long timeoutSec, @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request, @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
    Preconditions.checkArgument(Iterables.size(entitlements) > 0, "Subscription list mustn't be null or empty.");
    logDeprecationParameterWarningIfNeeded(QUERY_REQUESTED_DT, QUERY_ENTITLEMENT_REQUESTED_DT, QUERY_BILLING_REQUESTED_DT);
    final int baseSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {

        @Override
        public boolean apply(final SubscriptionJson subscription) {
            return ProductCategory.BASE.toString().equals(subscription.getProductCategory());
        }
    }));
    verifyNumberOfElements(baseSubscriptionsSize, 1, "Only one BASE product is allowed.");
    final int addOnSubscriptionsSize = Iterables.size(Iterables.filter(entitlements, new Predicate<SubscriptionJson>() {

        @Override
        public boolean apply(final SubscriptionJson subscription) {
            return ProductCategory.ADD_ON.toString().equals(subscription.getProductCategory());
        }
    }));
    verifyNumberOfElements(addOnSubscriptionsSize, entitlements.size() - 1, "It should be " + (entitlements.size() - 1) + " ADD_ON products.");
    final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
    final CallContext callContext = context.createContext(createdBy, reason, comment, request);
    final SubscriptionJson baseEntitlement = Iterables.tryFind(entitlements, new Predicate<SubscriptionJson>() {

        @Override
        public boolean apply(final SubscriptionJson subscription) {
            return ProductCategory.BASE.toString().equalsIgnoreCase(subscription.getProductCategory());
        }
    }).orNull();
    verifyNonNull(baseEntitlement.getAccountId(), "SubscriptionJson accountId needs to be set for BASE product.");
    final EntitlementCallCompletionCallback<List<Entitlement>> callback = new EntitlementCallCompletionCallback<List<Entitlement>>() {

        @Override
        public List<Entitlement> doOperation(final CallContext ctx) throws InterruptedException, TimeoutException, EntitlementApiException, SubscriptionApiException, AccountApiException {
            final Account account = getAccountFromSubscriptionJson(baseEntitlement, callContext);
            final List<EntitlementSpecifier> entitlementSpecifierList = buildEntitlementSpecifierList(entitlements, account.getCurrency());
            final LocalDate resolvedEntitlementDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(entitlementDate);
            final LocalDate resolvedBillingDate = requestedDate != null ? toLocalDate(requestedDate) : toLocalDate(billingDate);
            final UUID bundleId = baseEntitlement.getBundleId() != null ? UUID.fromString(baseEntitlement.getBundleId()) : null;
            BaseEntitlementWithAddOnsSpecifier baseEntitlementSpecifierWithAddOns = buildBaseEntitlementWithAddOnsSpecifier(entitlementSpecifierList, resolvedEntitlementDate, resolvedBillingDate, bundleId, baseEntitlement, isMigrated);
            final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
            baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementSpecifierWithAddOns);
            return entitlementApi.createBaseEntitlementsWithAddOns(account.getId(), baseEntitlementWithAddOnsSpecifierList, pluginProperties, callContext);
        }

        @Override
        public boolean isImmOperation() {
            return true;
        }

        @Override
        public Response doResponseOk(final List<Entitlement> entitlements) {
            return uriBuilder.buildResponse(uriInfo, BundleResource.class, "getBundle", entitlements.get(0).getBundleId(), request);
        }
    };
    final EntitlementCallCompletion<List<Entitlement>> callCompletionCreation = new EntitlementCallCompletion<List<Entitlement>>();
    return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
}
Also used : Account(org.killbill.billing.account.api.Account) SubscriptionJson(org.killbill.billing.jaxrs.json.SubscriptionJson) ArrayList(java.util.ArrayList) CallContext(org.killbill.billing.util.callcontext.CallContext) LocalDate(org.joda.time.LocalDate) Predicate(com.google.common.base.Predicate) EntitlementSpecifier(org.killbill.billing.entitlement.api.EntitlementSpecifier) PluginProperty(org.killbill.billing.payment.api.PluginProperty) BaseEntitlementWithAddOnsSpecifier(org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier) List(java.util.List) ArrayList(java.util.ArrayList) Entitlement(org.killbill.billing.entitlement.api.Entitlement) UUID(java.util.UUID) Path(javax.ws.rs.Path) TimedResource(org.killbill.commons.metrics.TimedResource) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) Produces(javax.ws.rs.Produces) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 22 with Entitlement

use of org.killbill.billing.entitlement.api.Entitlement in project killbill by killbill.

the class TestDefaultBlockingStateDao method testUnnecessaryEventsAreNotAdded.

@Test(groups = "slow", description = "Verify we don't insert extra add-on events")
public void testUnnecessaryEventsAreNotAdded() throws Exception {
    // This is a simple smoke test at the dao level only to make sure we do sane
    // things in case there are no future add-on cancellation events to add in the stream.
    // See TestEntitlementUtils for a more comprehensive test
    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
    testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
    final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
    assertListenerStatus();
    final BlockingStateType type = BlockingStateType.SUBSCRIPTION;
    final String state = "state";
    final String service = "service";
    // Verify initial state
    Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 1);
    // Set a state in the future so no event
    final DateTime stateDateTime = new DateTime(2013, 5, 6, 10, 11, 12, DateTimeZone.UTC);
    final BlockingState blockingState = new DefaultBlockingState(entitlement.getId(), type, state, service, false, false, false, stateDateTime);
    blockingStateDao.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableMap.<BlockingState, Optional<UUID>>of(blockingState, Optional.<UUID>of(entitlement.getBundleId())), internalCallContext);
    assertListenerStatus();
    Assert.assertEquals(blockingStateDao.getBlockingAllForAccountRecordId(internalCallContext).size(), 2);
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) BlockingStateType(org.killbill.billing.entitlement.api.BlockingStateType) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) BlockingState(org.killbill.billing.entitlement.api.BlockingState) Entitlement(org.killbill.billing.entitlement.api.Entitlement) UUID(java.util.UUID) DateTime(org.joda.time.DateTime) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) Test(org.testng.annotations.Test)

Example 23 with Entitlement

use of org.killbill.billing.entitlement.api.Entitlement in project killbill by killbill.

the class TestDefaultInternalBillingApi method testBlockingStatesWithSameEffectiveDate.

// This test was originally for https://github.com/killbill/killbill/issues/123.
// The invocationCount > 0 was to trigger an issue where events would come out-of-order randomly.
// While the bug shouldn't occur anymore, we're keeping it just in case (the test will also try to insert the events out-of-order manually).
// This test also checks we don't generate billing events for blocking durations less than a day (https://github.com/killbill/killbill/issues/267).
@Test(groups = "slow", description = "Check blocking states with same effective date are correctly handled", invocationCount = 10, enabled = false)
public void testBlockingStatesWithSameEffectiveDate() throws Exception {
    final LocalDate initialDate = new LocalDate(2013, 8, 7);
    clock.setDay(initialDate);
    final Account account = createAccount(getAccountData(7));
    testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
    final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
    final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, null, null, false, ImmutableList.<PluginProperty>of(), callContext);
    final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
    assertListenerStatus();
    final DateTime block1Date = clock.getUTCNow();
    testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
    final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, true, block1Date);
    blockingInternalApi.setBlockingState(state1, internalCallContext);
    // Same date, we'll order by record id asc
    final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, block1Date);
    blockingInternalApi.setBlockingState(state2, internalCallContext);
    assertListenerStatus();
    clock.addDays(5);
    final DateTime block2Date = clock.getUTCNow();
    testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
    final DefaultBlockingState state3 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, true, block2Date);
    blockingInternalApi.setBlockingState(state3, internalCallContext);
    // Same date, we'll order by record id asc
    final DefaultBlockingState state4 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, block2Date);
    blockingInternalApi.setBlockingState(state4, internalCallContext);
    assertListenerStatus();
    final DateTime block3Date = block2Date.plusDays(3);
    // Pass the phase
    testListener.pushExpectedEvent(NextEvent.PHASE);
    clock.addDays(50);
    assertListenerStatus();
    final DateTime block4Date = clock.getUTCNow();
    final DateTime block5Date = block4Date.plusDays(3);
    // Only one event on the bus (for state5)
    testListener.pushExpectedEvents(NextEvent.BLOCK);
    // Insert the clear state first, to make sure the order in which we insert blocking states doesn't matter
    // Since we are already in an ENT_STATE_CLEAR state for service ENTITLEMENT_SERVICE_NAME, we need to use a different
    // state name to simulate this behavior (otherwise, by design, this event won't be created)
    final DefaultBlockingState state6 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_CLEAR + "-something", EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, block5Date);
    blockingInternalApi.setBlockingState(state6, internalCallContext);
    final DefaultBlockingState state5 = new DefaultBlockingState(entitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something", EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, true, block4Date);
    blockingInternalApi.setBlockingState(state5, internalCallContext);
    assertListenerStatus();
    // Now, add back blocking states at an earlier date, for a different blockable id, to make sure the effective
    // date ordering is correctly respected when computing blocking durations
    testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
    final DefaultBlockingState state7 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something2", EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, true, block3Date);
    blockingInternalApi.setBlockingState(state7, internalCallContext);
    final DefaultBlockingState state8 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, DefaultEntitlementApi.ENT_STATE_CLEAR + "-something2", EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, block4Date);
    blockingInternalApi.setBlockingState(state8, internalCallContext);
    assertListenerStatus();
    // Advance for state6 to be active
    testListener.pushExpectedEvents(NextEvent.BLOCK);
    clock.addDays(5);
    assertListenerStatus();
    // Expected blocking duration:
    // * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
    final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, internalCallContext));
    Assert.assertEquals(events.size(), 3);
    Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
    Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
    Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
    Assert.assertEquals(events.get(1).getEffectiveDate(), block3Date);
    Assert.assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
    Assert.assertEquals(events.get(2).getEffectiveDate(), block5Date);
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) Account(org.killbill.billing.account.api.Account) BillingEvent(org.killbill.billing.junction.BillingEvent) Entitlement(org.killbill.billing.entitlement.api.Entitlement) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime) DefaultBlockingState(org.killbill.billing.junction.DefaultBlockingState) Test(org.testng.annotations.Test)

Example 24 with Entitlement

use of org.killbill.billing.entitlement.api.Entitlement in project killbill by killbill.

the class OverdueStateApplicator method cancelSubscriptionsIfRequired.

private void cancelSubscriptionsIfRequired(final DateTime effectiveDate, final ImmutableAccountData account, final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
    if (nextOverdueState.getOverdueCancellationPolicy() == OverdueCancellationPolicy.NONE) {
        return;
    }
    final CallContext callContext = internalCallContextFactory.createCallContext(context);
    try {
        final BillingActionPolicy actionPolicy;
        switch(nextOverdueState.getOverdueCancellationPolicy()) {
            case END_OF_TERM:
                actionPolicy = BillingActionPolicy.END_OF_TERM;
                break;
            case IMMEDIATE:
                actionPolicy = BillingActionPolicy.IMMEDIATE;
                break;
            default:
                throw new IllegalStateException("Unexpected OverdueCancellationPolicy " + nextOverdueState.getOverdueCancellationPolicy());
        }
        final List<Entitlement> toBeCancelled = new LinkedList<Entitlement>();
        computeEntitlementsToCancel(account, toBeCancelled, callContext);
        try {
            entitlementInternalApi.cancel(toBeCancelled, context.toLocalDate(effectiveDate), actionPolicy, ImmutableList.<PluginProperty>of(), context);
        } catch (final EntitlementApiException e) {
            throw new OverdueException(e);
        }
    } catch (final EntitlementApiException e) {
        throw new OverdueException(e);
    }
}
Also used : BillingActionPolicy(org.killbill.billing.catalog.api.BillingActionPolicy) EntitlementApiException(org.killbill.billing.entitlement.api.EntitlementApiException) OverdueException(org.killbill.billing.overdue.config.api.OverdueException) Entitlement(org.killbill.billing.entitlement.api.Entitlement) CallContext(org.killbill.billing.util.callcontext.CallContext) InternalCallContext(org.killbill.billing.callcontext.InternalCallContext) LinkedList(java.util.LinkedList)

Example 25 with Entitlement

use of org.killbill.billing.entitlement.api.Entitlement in project killbill by killbill.

the class UsageResource method recordUsage.

@TimedResource
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Record usage for a subscription")
@ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid subscription (e.g. inactive)") })
public Response recordUsage(final SubscriptionUsageRecordJson json, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request, @javax.ws.rs.core.Context final UriInfo uriInfo) throws EntitlementApiException, AccountApiException, UsageApiException {
    verifyNonNullOrEmpty(json, "SubscriptionUsageRecordJson body should be specified");
    verifyNonNullOrEmpty(json.getSubscriptionId(), "SubscriptionUsageRecordJson subscriptionId needs to be set", json.getUnitUsageRecords(), "SubscriptionUsageRecordJson unitUsageRecords needs to be set");
    Preconditions.checkArgument(!json.getUnitUsageRecords().isEmpty());
    for (final UnitUsageRecordJson unitUsageRecordJson : json.getUnitUsageRecords()) {
        verifyNonNullOrEmpty(unitUsageRecordJson.getUnitType(), "UnitUsageRecordJson unitType need to be set");
        Preconditions.checkArgument(Iterables.size(unitUsageRecordJson.getUsageRecords()) > 0, "UnitUsageRecordJson usageRecords must have at least one element.");
        for (final UsageRecordJson usageRecordJson : unitUsageRecordJson.getUsageRecords()) {
            verifyNonNull(usageRecordJson.getAmount(), "UsageRecordJson amount needs to be set");
            verifyNonNull(usageRecordJson.getRecordDate(), "UsageRecordJson recordDate needs to be set");
        }
    }
    final CallContext callContext = context.createContext(createdBy, reason, comment, request);
    // Verify subscription exists..
    final Entitlement entitlement = entitlementApi.getEntitlementForId(UUID.fromString(json.getSubscriptionId()), callContext);
    if (entitlement.getState() != EntitlementState.ACTIVE) {
        return Response.status(Status.BAD_REQUEST).build();
    }
    final SubscriptionUsageRecord record = json.toSubscriptionUsageRecord();
    usageUserApi.recordRolledUpUsage(record, callContext);
    return Response.status(Status.CREATED).build();
}
Also used : UnitUsageRecordJson(org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UnitUsageRecordJson) UnitUsageRecordJson(org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UnitUsageRecordJson) UsageRecordJson(org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson.UsageRecordJson) SubscriptionUsageRecordJson(org.killbill.billing.jaxrs.json.SubscriptionUsageRecordJson) Entitlement(org.killbill.billing.entitlement.api.Entitlement) SubscriptionUsageRecord(org.killbill.billing.usage.api.SubscriptionUsageRecord) CallContext(org.killbill.billing.util.callcontext.CallContext) TimedResource(org.killbill.commons.metrics.TimedResource) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) Produces(javax.ws.rs.Produces) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Aggregations

Entitlement (org.killbill.billing.entitlement.api.Entitlement)47 LocalDate (org.joda.time.LocalDate)32 Test (org.testng.annotations.Test)30 Account (org.killbill.billing.account.api.Account)28 PlanPhaseSpecifier (org.killbill.billing.catalog.api.PlanPhaseSpecifier)27 DefaultEntitlement (org.killbill.billing.entitlement.api.DefaultEntitlement)23 ArrayList (java.util.ArrayList)12 DateTime (org.joda.time.DateTime)12 BigDecimal (java.math.BigDecimal)10 EntitlementApiException (org.killbill.billing.entitlement.api.EntitlementApiException)10 PluginProperty (org.killbill.billing.payment.api.PluginProperty)10 UUID (java.util.UUID)9 CallContext (org.killbill.billing.util.callcontext.CallContext)9 ApiOperation (io.swagger.annotations.ApiOperation)8 ApiResponses (io.swagger.annotations.ApiResponses)8 Produces (javax.ws.rs.Produces)8 AccountData (org.killbill.billing.account.api.AccountData)8 ExpectedInvoiceItemCheck (org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck)8 Subscription (org.killbill.billing.entitlement.api.Subscription)8 Invoice (org.killbill.billing.invoice.api.Invoice)8