use of org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier in project killbill by killbill.
the class TestDefaultInternalBillingApi method testBCDUpdateMultipleSubscriptionsAccountAligned.
@Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/865")
public void testBCDUpdateMultipleSubscriptionsAccountAligned() throws Exception {
final LocalDate initialDate = new LocalDate(2013, 8, 7);
clock.setDay(initialDate);
// Account with no BCD
final Account account = createAccount(getAccountData(0));
Assert.assertEquals(account.getBillCycleDayLocal(), (Integer) 0);
// Create 2 entitlements, one base with one add-on. All entitlements are ACCOUNT aligned
final String bundleKey1 = UUID.randomUUID().toString();
final EntitlementSpecifier entitlementSpecifierBase1 = new DefaultEntitlementSpecifier(new PlanPhaseSpecifier("Shotgun", BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null));
final EntitlementSpecifier entitlementSpecifierAO1 = new DefaultEntitlementSpecifier(new PlanPhaseSpecifier("Cabinet", BillingPeriod.TRIANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null));
final BaseEntitlementWithAddOnsSpecifier specifier1 = new DefaultBaseEntitlementWithAddOnsSpecifier(null, bundleKey1, ImmutableList.of(entitlementSpecifierBase1, entitlementSpecifierAO1), null, null, false);
testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK, NextEvent.CREATE, NextEvent.BLOCK);
entitlementApi.createBaseEntitlementsWithAddOns(account.getId(), ImmutableList.of(specifier1), false, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
Assert.assertEquals(entitlements.size(), 2);
Assert.assertEquals(entitlements.get(0).getLastActiveProduct().getName(), "Shotgun");
// See bug description at https://github.com/killbill/killbill/issues/865 and PR discussion at https://github.com/killbill/killbill/pull/1067/files/926ca68c32c8f8c93bd5eda94fba17f6e19e593d#r237981095
Assert.assertNull(entitlements.get(0).getBillCycleDayLocal());
Assert.assertEquals(entitlements.get(1).getLastActiveProduct().getName(), "Cabinet");
Assert.assertNull(entitlements.get(1).getBillCycleDayLocal());
// Account still has no BCD
final Account accountNoBCD = accountApi.getAccountById(account.getId(), callContext);
Assert.assertEquals(accountNoBCD.getBillCycleDayLocal(), (Integer) 0);
List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, null, internalCallContext));
Assert.assertEquals(events.size(), 3);
for (final BillingEvent billingEvent : events) {
Assert.assertEquals(billingEvent.getBillCycleDayLocal(), 7);
}
// Verify BCD
final Account accountWithBCD = accountApi.getAccountById(account.getId(), callContext);
Assert.assertEquals(accountWithBCD.getBillCycleDayLocal(), (Integer) 7);
// Verify GET
final List<Entitlement> entitlementsUpdated = entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext);
Assert.assertEquals(entitlementsUpdated.size(), 2);
Assert.assertEquals(entitlementsUpdated.get(0).getLastActiveProduct().getName(), "Shotgun");
Assert.assertEquals(entitlementsUpdated.get(0).getBillCycleDayLocal(), (Integer) 7);
Assert.assertEquals(entitlementsUpdated.get(1).getLastActiveProduct().getName(), "Cabinet");
Assert.assertEquals(entitlementsUpdated.get(1).getBillCycleDayLocal(), (Integer) 7);
events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), null, null, internalCallContext));
Assert.assertEquals(events.size(), 3);
for (final BillingEvent billingEvent : events) {
Assert.assertEquals(billingEvent.getBillCycleDayLocal(), 7);
}
}
use of org.killbill.billing.entitlement.api.BaseEntitlementWithAddOnsSpecifier in project killbill by killbill.
the class SubscriptionResource method createSubscriptionsWithAddOnsInternal.
public Response createSubscriptionsWithAddOnsInternal(final List<BulkSubscriptionsBundleJson> entitlementsWithAddOns, final String entitlementDate, final String billingDate, final Boolean isMigrated, final Boolean skipResponse, final Boolean renameKeyIfExistsAndUnused, final Boolean callCompletion, final long timeoutSec, final List<String> pluginPropertiesString, final String createdBy, final String reason, final String comment, final HttpServletRequest request, final UriInfo uriInfo, final ObjectType responseObject) throws EntitlementApiException, AccountApiException, SubscriptionApiException {
Preconditions.checkArgument(Iterables.size(entitlementsWithAddOns) > 0, "No subscription specified to create");
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final CallContext callContextNoAccountId = context.createCallContextNoAccountId(createdBy, reason, comment, request);
Preconditions.checkArgument(Iterables.size(entitlementsWithAddOns.get(0).getBaseEntitlementAndAddOns()) > 0, "SubscriptionJson body should be specified");
final Account account = accountUserApi.getAccountById(entitlementsWithAddOns.get(0).getBaseEntitlementAndAddOns().get(0).getAccountId(), callContextNoAccountId);
final CallContext callContext = context.createCallContextWithAccountId(account.getId(), createdBy, reason, comment, request);
final Collection<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
for (final BulkSubscriptionsBundleJson subscriptionsBundleJson : entitlementsWithAddOns) {
UUID bundleId = null;
String bundleExternalKey = null;
final Collection<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
for (final SubscriptionJson entitlement : subscriptionsBundleJson.getBaseEntitlementAndAddOns()) {
// verifications
verifyNonNullOrEmpty(entitlement, "SubscriptionJson body should be specified for each element");
if (entitlement.getPlanName() == null) {
verifyNonNullOrEmpty(entitlement.getProductName(), "SubscriptionJson productName needs to be set when no planName is specified", entitlement.getProductCategory(), "SubscriptionJson productCategory needs to be set when no planName is specified", entitlement.getBillingPeriod(), "SubscriptionJson billingPeriod needs to be set when no planName is specified", entitlement.getPriceList(), "SubscriptionJson priceList needs to be set when no planName is specified");
} else {
Preconditions.checkArgument(entitlement.getProductName() == null, "SubscriptionJson productName should not be set when planName is specified");
Preconditions.checkArgument(entitlement.getProductCategory() == null, "SubscriptionJson productCategory should not be set when planName is specified");
Preconditions.checkArgument(entitlement.getBillingPeriod() == null, "SubscriptionJson billingPeriod should not be set when planName is specified");
Preconditions.checkArgument(entitlement.getPriceList() == null, "SubscriptionJson priceList should not be set when planName is specified");
}
Preconditions.checkArgument(account.getId().equals(entitlement.getAccountId()), "SubscriptionJson accountId should be the same for each element");
// If set on one element, it should be set on all elements
Preconditions.checkArgument(bundleId == null || bundleId.equals(entitlement.getBundleId()), "SubscriptionJson bundleId should be the same for each element");
if (bundleId == null) {
bundleId = entitlement.getBundleId();
}
// Can be set on a single element (e.g. BASE + ADD_ON for a new bundle)
Preconditions.checkArgument(bundleExternalKey == null || entitlement.getBundleExternalKey() == null || bundleExternalKey.equals(entitlement.getBundleExternalKey()), "SubscriptionJson externalKey should be the same for each element");
if (bundleExternalKey == null) {
bundleExternalKey = entitlement.getBundleExternalKey();
}
// create the entitlementSpecifier
final EntitlementSpecifier spec = buildEntitlementSpecifier(entitlement, account.getCurrency(), entitlement.getExternalKey());
entitlementSpecifierList.add(spec);
}
final LocalDate resolvedEntitlementDate = toLocalDate(entitlementDate);
final LocalDate resolvedBillingDate = toLocalDate(billingDate);
final BaseEntitlementWithAddOnsSpecifier baseEntitlementSpecifierWithAddOns = buildBaseEntitlementWithAddOnsSpecifier(entitlementSpecifierList, resolvedEntitlementDate, resolvedBillingDate, bundleId, bundleExternalKey, isMigrated);
baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementSpecifierWithAddOns);
}
final EntitlementCallCompletionCallback<List<UUID>> callback = new EntitlementCallCompletionCallback<List<UUID>>() {
// By default, wait for invoice and payment
// This is very 101 and won't always work though: an entitlement plugin could override dates on the fly,
// an invoice plugin could reschedule the invoice generation, etc.
private boolean isImmediateOp = true;
@Override
public List<UUID> doOperation(final CallContext ctx) throws EntitlementApiException {
for (final BaseEntitlementWithAddOnsSpecifier spec : baseEntitlementWithAddOnsSpecifierList) {
if (spec.getBillingEffectiveDate() != null) {
final boolean inTheFuture = isInTheFuture(spec.getBillingEffectiveDate(), account);
if (inTheFuture) {
// At least one subscription has a billing date in the future: don't wait for any event
// We don't support callCompletion=true for a bulk creation call with dates all over the place
isImmediateOp = false;
break;
}
}
}
return entitlementApi.createBaseEntitlementsWithAddOns(account.getId(), baseEntitlementWithAddOnsSpecifierList, renameKeyIfExistsAndUnused, pluginProperties, callContext);
}
@Override
public boolean isImmOperation() {
return isImmediateOp;
}
@Override
public Response doResponseOk(final List<UUID> entitlementIds) {
if (responseObject == ObjectType.SUBSCRIPTION) {
return uriBuilder.buildResponse(uriInfo, SubscriptionResource.class, "getSubscription", Iterables.getFirst(entitlementIds, null), request);
}
// Workaround for https://github.com/killbill/killbill/issues/1336
// While we could tweak the container to support large number of bundles in the filter (e.g. Jetty's RequestBufferSize),
// the full list is probably not that useful for the client in practice.
final Collection<String> bundleIds = new LinkedHashSet<String>();
if (!skipResponse && entitlementIds.size() < MAX_NB_SUBSCRIPTIONS_TO_FOLLOW) {
try {
for (final Entitlement entitlement : entitlementApi.getAllEntitlementsForAccountId(account.getId(), callContext)) {
if (entitlementIds.contains(entitlement.getId())) {
bundleIds.add(entitlement.getBundleId().toString());
}
}
} catch (final EntitlementApiException e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}
if (responseObject == ObjectType.ACCOUNT) {
return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getAccountBundles", account.getId(), buildBundlesFilterQueryParam(bundleIds), request);
} else if (responseObject == ObjectType.BUNDLE) {
return uriBuilder.buildResponse(uriInfo, BundleResource.class, "getBundle", Iterables.getFirst(bundleIds, null), request);
} else {
throw new IllegalStateException("Unexpected input responseObject " + responseObject);
}
}
};
final EntitlementCallCompletion<List<UUID>> callCompletionCreation = new EntitlementCallCompletion<List<UUID>>();
return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
}
Aggregations