Search in sources :

Example 36 with SubscriptionBaseApiException

use of org.killbill.billing.subscription.api.user.SubscriptionBaseApiException in project killbill by killbill.

the class DefaultSubscriptionBaseCreateApi method createPlansIfNeededAndReorderBPOrStandaloneSpecFirstWithSanity.

private boolean createPlansIfNeededAndReorderBPOrStandaloneSpecFirstWithSanity(final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier, final SubscriptionCatalog catalog, final DateTime effectiveDate, final Collection<EntitlementSpecifier> outputEntitlementSpecifier, final Collection<Plan> outputEntitlementPlans, final CallContext callContext) throws SubscriptionBaseApiException, CatalogApiException {
    EntitlementSpecifier basePlanSpecifier = null;
    Plan basePlan = null;
    final Collection<EntitlementSpecifier> addOnSpecifiers = new ArrayList<EntitlementSpecifier>();
    final Collection<EntitlementSpecifier> standaloneSpecifiers = new ArrayList<EntitlementSpecifier>();
    final Collection<Plan> addOnsPlans = new ArrayList<Plan>();
    final Collection<Plan> standalonePlans = new ArrayList<Plan>();
    for (final EntitlementSpecifier cur : subscriptionBaseWithAddOnsSpecifier.getEntitlementSpecifiers()) {
        final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(cur.getOverrides(), callContext);
        // Called by createBaseSubscriptionsWithAddOns only -- no need for subscription start date
        final StaticCatalog catalogVersion = catalog.versionForDate(effectiveDate);
        final Plan plan = catalogVersion.createOrFindPlan(cur.getPlanPhaseSpecifier(), overridesWithContext);
        final boolean isBase = isBaseSpecifier(plan);
        final boolean isStandalone = isStandaloneSpecifier(plan);
        if (isStandalone) {
            standaloneSpecifiers.add(cur);
            standalonePlans.add(plan);
        } else if (isBase) {
            if (basePlanSpecifier == null) {
                basePlanSpecifier = cur;
                basePlan = plan;
            } else {
                log.warn("createPlansIfNeededAndReorderBPOrStandaloneSpecFirstWithSanity: multiple basePlanSpecifier");
                throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER, "multiple base plan specifiers");
            }
        } else {
            addOnSpecifiers.add(cur);
            addOnsPlans.add(plan);
        }
    }
    if (basePlanSpecifier != null) {
        outputEntitlementSpecifier.add(basePlanSpecifier);
        outputEntitlementPlans.add(basePlan);
    }
    outputEntitlementSpecifier.addAll(addOnSpecifiers);
    outputEntitlementPlans.addAll(addOnsPlans);
    if (!outputEntitlementSpecifier.isEmpty() && !standaloneSpecifiers.isEmpty()) {
        log.warn("createPlansIfNeededAndReorderBPOrStandaloneSpecFirstWithSanity: both outputEntitlementSpecifier and standaloneSpecifiers specified");
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER, "mix of base/add-on specifier(s) and standalone specifier(s)");
    }
    if (standaloneSpecifiers.isEmpty()) {
        return basePlanSpecifier != null;
    } else {
        outputEntitlementSpecifier.addAll(standaloneSpecifiers);
        outputEntitlementPlans.addAll(standalonePlans);
        return true;
    }
}
Also used : EntitlementSpecifier(org.killbill.billing.entitlement.api.EntitlementSpecifier) PlanPhasePriceOverridesWithCallContext(org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext) ArrayList(java.util.ArrayList) Plan(org.killbill.billing.catalog.api.Plan) StaticCatalog(org.killbill.billing.catalog.api.StaticCatalog) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 37 with SubscriptionBaseApiException

use of org.killbill.billing.subscription.api.user.SubscriptionBaseApiException in project killbill by killbill.

the class DefaultSubscriptionBaseCreateApi method prepareSubscriptionAndAddOnsSpecifier.

private void prepareSubscriptionAndAddOnsSpecifier(final Collection<SubscriptionAndAddOnsSpecifier> subscriptionAndAddOns, final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier, final boolean renameCancelledBundleIfExist, final SubscriptionCatalog catalog, final AddonUtils addonUtils, final CacheController<UUID, UUID> accountIdCacheController, final CallContext callContext, final InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException {
    SubscriptionBaseBundle bundle = getBundleWithSanity(subscriptionBaseWithAddOnsSpecifier, catalog, callContext, context);
    final DateTime billingRequestedDateRaw = (subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate() != null) ? context.toUTCDateTime(subscriptionBaseWithAddOnsSpecifier.getBillingEffectiveDate()) : context.getCreatedDate();
    final SubscriptionBase baseSubscription;
    final DateTime billingRequestedDate;
    if (bundle != null) {
        baseSubscription = dao.getBaseSubscription(bundle.getId(), catalog, context);
        billingRequestedDate = computeActualBillingRequestedDate(bundle, billingRequestedDateRaw, baseSubscription, catalog, context);
    } else {
        baseSubscription = null;
        billingRequestedDate = billingRequestedDateRaw;
    }
    final List<EntitlementSpecifier> reorderedSpecifiers = new ArrayList<EntitlementSpecifier>();
    final List<Plan> createdOrRetrievedPlans = new ArrayList<Plan>();
    final boolean hasBaseOrStandalonePlanSpecifier = createPlansIfNeededAndReorderBPOrStandaloneSpecFirstWithSanity(subscriptionBaseWithAddOnsSpecifier, catalog, billingRequestedDate, reorderedSpecifiers, createdOrRetrievedPlans, callContext);
    if (hasBaseOrStandalonePlanSpecifier && baseSubscription != null) {
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundle.getExternalKey());
    }
    if (bundle == null && hasBaseOrStandalonePlanSpecifier) {
        bundle = createBundleForAccount(callContext.getAccountId(), subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey(), renameCancelledBundleIfExist, catalog, accountIdCacheController, context);
    } else if (bundle == null) {
        log.warn("Invalid specifier: {}", subscriptionBaseWithAddOnsSpecifier);
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, subscriptionBaseWithAddOnsSpecifier.getBundleExternalKey());
    }
    final List<SubscriptionSpecifier> subscriptionSpecifiers = verifyAndBuildSubscriptionSpecifiers(bundle, hasBaseOrStandalonePlanSpecifier, reorderedSpecifiers, createdOrRetrievedPlans, subscriptionBaseWithAddOnsSpecifier.isMigrated(), billingRequestedDate, catalog, addonUtils, callContext, context);
    final SubscriptionAndAddOnsSpecifier subscriptionAndAddOnsSpecifier = new SubscriptionAndAddOnsSpecifier(bundle, billingRequestedDate, subscriptionSpecifiers);
    subscriptionAndAddOns.add(subscriptionAndAddOnsSpecifier);
}
Also used : SubscriptionSpecifier(org.killbill.billing.subscription.api.user.SubscriptionSpecifier) ArrayList(java.util.ArrayList) SubscriptionAndAddOnsSpecifier(org.killbill.billing.subscription.api.user.SubscriptionAndAddOnsSpecifier) Plan(org.killbill.billing.catalog.api.Plan) DateTime(org.joda.time.DateTime) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) EntitlementSpecifier(org.killbill.billing.entitlement.api.EntitlementSpecifier) SubscriptionBaseBundle(org.killbill.billing.subscription.api.user.SubscriptionBaseBundle) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 38 with SubscriptionBaseApiException

use of org.killbill.billing.subscription.api.user.SubscriptionBaseApiException in project killbill by killbill.

the class TestInvoiceDispatcher method testWithParking.

@Test(groups = "slow")
public void testWithParking() throws InvoiceApiException, AccountApiException, CatalogApiException, SubscriptionBaseApiException, TagDefinitionApiException {
    final UUID accountId = account.getId();
    final BillingEventSet events = new MockBillingEventSet();
    final Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
    final PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
    final DateTime effectiveDate = clock.getUTCNow().minusDays(1);
    final Currency currency = Currency.USD;
    final BigDecimal fixedPrice = null;
    events.add(invoiceUtil.createMockBillingEvent(account, subscription, effectiveDate, plan, planPhase, fixedPrice, BigDecimal.ONE, currency, BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE, "", 1L, SubscriptionBaseTransitionType.CREATE));
    Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<DryRunArguments>any(), Mockito.<LocalDate>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
    final LocalDate target = internalCallContext.toLocalDate(effectiveDate);
    final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao, internalCallContextFactory, invoicePluginDispatcher, locker, bus, notificationQueueService, invoiceConfig, clock, invoiceOptimizer, parkedAccountsManager);
    // Verify initial tags state for account
    Assert.assertTrue(tagUserApi.getTagsForAccount(accountId, true, callContext).isEmpty());
    // Create chaos on disk
    final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(accountId, target, target, currency, false);
    final InvoiceItemModelDao invoiceItemModelDao1 = new InvoiceItemModelDao(clock.getUTCNow(), InvoiceItemType.RECURRING, invoiceModelDao.getId(), accountId, subscription.getBundleId(), subscription.getId(), "Bad data", null, plan.getName(), planPhase.getName(), null, null, effectiveDate.toLocalDate(), effectiveDate.plusMonths(1).toLocalDate(), BigDecimal.TEN, BigDecimal.ONE, currency, null);
    final InvoiceItemModelDao invoiceItemModelDao2 = new InvoiceItemModelDao(clock.getUTCNow(), InvoiceItemType.RECURRING, invoiceModelDao.getId(), accountId, subscription.getBundleId(), subscription.getId(), "Bad data", null, plan.getName(), planPhase.getName(), null, null, effectiveDate.plusDays(1).toLocalDate(), effectiveDate.plusMonths(1).toLocalDate(), BigDecimal.TEN, BigDecimal.ONE, currency, null);
    invoiceModelDao.addInvoiceItem(invoiceItemModelDao1);
    invoiceModelDao.addInvoiceItem(invoiceItemModelDao2);
    invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(invoiceModelDao), events, ImmutableSet.of(), context);
    try {
        dispatcher.processAccountFromNotificationOrBusEvent(accountId, target, new DryRunFutureDateArguments(), false, context);
        Assert.fail();
    } catch (final InvoiceApiException e) {
        Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
        Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected"));
    }
    // Dry-run: no side effect on disk
    Assert.assertEquals(invoiceDao.getInvoicesByAccount(false, context).size(), 1);
    Assert.assertTrue(tagUserApi.getTagsForAccount(accountId, true, callContext).isEmpty());
    try {
        dispatcher.processAccountFromNotificationOrBusEvent(accountId, target, null, false, context);
        Assert.fail();
    } catch (final InvoiceApiException e) {
        Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
        Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected"));
    }
    Assert.assertEquals(invoiceDao.getInvoicesByAccount(false, context).size(), 1);
    // No dry-run: account is parked
    final List<Tag> tags = tagUserApi.getTagsForAccount(accountId, false, callContext);
    Assert.assertEquals(tags.size(), 1);
    Assert.assertEquals(tags.get(0).getTagDefinitionId(), SystemTags.PARK_TAG_DEFINITION_ID);
    // isApiCall=false
    final Invoice nullInvoice1 = dispatcher.processAccountFromNotificationOrBusEvent(accountId, target, null, false, context);
    Assert.assertNull(nullInvoice1);
    // No dry-run and isApiCall=true
    try {
        dispatcher.processAccount(true, accountId, target, null, false, context);
        Assert.fail();
    } catch (final InvoiceApiException e) {
        Assert.assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
        Assert.assertTrue(e.getCause().getMessage().startsWith("Double billing detected"));
    }
    // Idempotency
    Assert.assertEquals(invoiceDao.getInvoicesByAccount(false, context).size(), 1);
    Assert.assertEquals(tagUserApi.getTagsForAccount(accountId, false, callContext), tags);
    // Fix state
    dbi.withHandle(new HandleCallback<Void>() {

        @Override
        public Void withHandle(final Handle handle) throws Exception {
            handle.execute("delete from invoices");
            handle.execute("delete from invoice_items");
            return null;
        }
    });
    // Dry-run and isApiCall=false: still parked
    final Invoice nullInvoice2 = dispatcher.processAccountFromNotificationOrBusEvent(accountId, target, new DryRunFutureDateArguments(), false, context);
    Assert.assertNull(nullInvoice2);
    // Dry-run and isApiCall=true: call goes through
    final Invoice invoice1 = dispatcher.processAccount(true, accountId, target, new DryRunFutureDateArguments(), false, context);
    Assert.assertNotNull(invoice1);
    Assert.assertEquals(invoiceDao.getInvoicesByAccount(false, context).size(), 0);
    // Dry-run: still parked
    Assert.assertEquals(tagUserApi.getTagsForAccount(accountId, false, callContext).size(), 1);
    // No dry-run and isApiCall=true: call goes through
    final Invoice invoice2 = dispatcher.processAccount(true, accountId, target, null, false, context);
    Assert.assertNotNull(invoice2);
    Assert.assertEquals(invoiceDao.getInvoicesByAccount(false, context).size(), 1);
    // No dry-run: now unparked
    Assert.assertEquals(tagUserApi.getTagsForAccount(accountId, false, callContext).size(), 0);
    Assert.assertEquals(tagUserApi.getTagsForAccount(accountId, true, callContext).size(), 1);
}
Also used : Invoice(org.killbill.billing.invoice.api.Invoice) InvoiceModelDao(org.killbill.billing.invoice.dao.InvoiceModelDao) MockPlan(org.killbill.billing.catalog.MockPlan) Plan(org.killbill.billing.catalog.api.Plan) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime) BigDecimal(java.math.BigDecimal) CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) AccountApiException(org.killbill.billing.account.api.AccountApiException) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) TagDefinitionApiException(org.killbill.billing.util.api.TagDefinitionApiException) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException) Handle(org.skife.jdbi.v2.Handle) InvoiceApiException(org.killbill.billing.invoice.api.InvoiceApiException) InvoiceItemModelDao(org.killbill.billing.invoice.dao.InvoiceItemModelDao) BillingEventSet(org.killbill.billing.junction.BillingEventSet) Currency(org.killbill.billing.catalog.api.Currency) DryRunFutureDateArguments(org.killbill.billing.invoice.TestInvoiceHelper.DryRunFutureDateArguments) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) MockPlanPhase(org.killbill.billing.catalog.MockPlanPhase) Tag(org.killbill.billing.util.tag.Tag) UUID(java.util.UUID) Test(org.testng.annotations.Test)

Example 39 with SubscriptionBaseApiException

use of org.killbill.billing.subscription.api.user.SubscriptionBaseApiException in project killbill by killbill.

the class AddonUtils method checkAddonCreationRights.

public void checkAddonCreationRights(final SubscriptionBase baseSubscription, final Plan targetAddOnPlan, final DateTime requestedDate, final InternalTenantContext context) throws SubscriptionBaseApiException {
    if (baseSubscription.getState() == EntitlementState.CANCELLED || (baseSubscription.getState() == EntitlementState.PENDING && context.toLocalDate(baseSubscription.getStartDate()).compareTo(context.toLocalDate(requestedDate)) < 0)) {
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_BP_NON_ACTIVE, targetAddOnPlan.getName());
    }
    final Plan currentOrPendingPlan = baseSubscription.getCurrentOrPendingPlan();
    final Product baseProduct = currentOrPendingPlan.getProduct();
    if (isAddonIncluded(baseProduct, targetAddOnPlan)) {
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_ALREADY_INCLUDED, targetAddOnPlan.getName(), currentOrPendingPlan.getProduct().getName());
    }
    if (!isAddonAvailable(baseProduct, targetAddOnPlan)) {
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_NOT_AVAILABLE, targetAddOnPlan.getName(), currentOrPendingPlan.getProduct().getName());
    }
}
Also used : Product(org.killbill.billing.catalog.api.Product) Plan(org.killbill.billing.catalog.api.Plan) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 40 with SubscriptionBaseApiException

use of org.killbill.billing.subscription.api.user.SubscriptionBaseApiException in project killbill by killbill.

the class DefaultSubscriptionInternalApi method setChargedThroughDate.

@Override
public void setChargedThroughDate(final UUID subscriptionId, final DateTime chargedThruDate, final InternalCallContext context) throws SubscriptionBaseApiException {
    try {
        final DefaultSubscriptionBase subscription = (DefaultSubscriptionBase) dao.getSubscriptionFromId(subscriptionId, context);
        final SubscriptionBuilder builder = new SubscriptionBuilder(subscription).setChargedThroughDate(chargedThruDate);
        dao.updateChargedThroughDate(new DefaultSubscriptionBase(builder), context);
    } catch (final CatalogApiException e) {
        throw new SubscriptionBaseApiException(e);
    }
}
Also used : CatalogApiException(org.killbill.billing.catalog.api.CatalogApiException) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException) PlanPhasePriceOverride(org.killbill.billing.catalog.api.PlanPhasePriceOverride)

Aggregations

SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)72 ArrayList (java.util.ArrayList)29 DateTime (org.joda.time.DateTime)29 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)28 UUID (java.util.UUID)27 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)24 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)24 WithEntitlementPlugin (org.killbill.billing.entitlement.api.EntitlementPluginExecution.WithEntitlementPlugin)19 EntitlementContext (org.killbill.billing.entitlement.plugin.api.EntitlementContext)19 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)17 SubscriptionBaseBundle (org.killbill.billing.subscription.api.user.SubscriptionBaseBundle)16 PlanPhasePriceOverride (org.killbill.billing.catalog.api.PlanPhasePriceOverride)14 DefaultBlockingState (org.killbill.billing.junction.DefaultBlockingState)14 InternalTenantContext (org.killbill.billing.callcontext.InternalTenantContext)11 Plan (org.killbill.billing.catalog.api.Plan)11 LinkedList (java.util.LinkedList)10 SubscriptionCatalog (org.killbill.billing.subscription.catalog.SubscriptionCatalog)10 AccountApiException (org.killbill.billing.account.api.AccountApiException)8 PlanPhasePriceOverridesWithCallContext (org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext)8 EntitlementApiException (org.killbill.billing.entitlement.api.EntitlementApiException)8