Search in sources :

Example 16 with SubscriptionBaseError

use of org.killbill.billing.subscription.exceptions.SubscriptionBaseError in project killbill by killbill.

the class DefaultSubscriptionDao method buildSubscription.

private SubscriptionBase buildSubscription(final SubscriptionBase input, final InternalTenantContext context) throws CatalogApiException {
    if (input == null) {
        return null;
    }
    final List<SubscriptionBase> bundleInput = new ArrayList<SubscriptionBase>();
    if (input.getCategory() == ProductCategory.ADD_ON) {
        final SubscriptionBase baseSubscription = getBaseSubscription(input.getBundleId(), false, context);
        if (baseSubscription == null) {
            return null;
        }
        bundleInput.add(baseSubscription);
        bundleInput.add(input);
    } else {
        bundleInput.add(input);
    }
    final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(bundleInput, null, null, context);
    for (final SubscriptionBase cur : reloadedSubscriptions) {
        if (cur.getId().equals(input.getId())) {
            return cur;
        }
    }
    throw new SubscriptionBaseError("Unexpected code path in buildSubscription");
}
Also used : SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) ArrayList(java.util.ArrayList)

Example 17 with SubscriptionBaseError

use of org.killbill.billing.subscription.exceptions.SubscriptionBaseError in project killbill by killbill.

the class DefaultSubscriptionBaseTransferApi method createEvent.

private SubscriptionBaseEvent createEvent(final boolean firstEvent, final ExistingEvent existingEvent, final DefaultSubscriptionBase subscription, final DateTime transferDate, final InternalTenantContext context) throws CatalogApiException {
    SubscriptionBaseEvent newEvent = null;
    final DateTime effectiveDate = existingEvent.getEffectiveDate().isBefore(transferDate) ? transferDate : existingEvent.getEffectiveDate();
    final PlanPhaseSpecifier spec = existingEvent.getPlanPhaseSpecifier();
    if (spec == null || existingEvent.getPlanPhaseName() == null) {
        // Ignore cancellations - we assume that transferred subscriptions should always be active
        return null;
    }
    final ApiEventBuilder apiBuilder = new ApiEventBuilder().setSubscriptionId(subscription.getId()).setEventPlan(existingEvent.getPlanName()).setEventPlanPhase(existingEvent.getPlanPhaseName()).setEventPriceList(spec.getPriceListName()).setEffectiveDate(effectiveDate).setFromDisk(true);
    switch(existingEvent.getSubscriptionTransitionType()) {
        case TRANSFER:
        case CREATE:
            newEvent = new ApiEventTransfer(apiBuilder);
            break;
        // Should we even keep future change events; product question really
        case CHANGE:
            newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) : new ApiEventChange(apiBuilder);
            break;
        case PHASE:
            newEvent = firstEvent ? new ApiEventTransfer(apiBuilder) : PhaseEventData.createNextPhaseEvent(subscription.getId(), existingEvent.getPlanPhaseName(), effectiveDate);
            break;
        case CANCEL:
            break;
        default:
            throw new SubscriptionBaseError(String.format("Unexpected transitionType %s", existingEvent.getSubscriptionTransitionType()));
    }
    return newEvent;
}
Also used : PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) ApiEventBuilder(org.killbill.billing.subscription.events.user.ApiEventBuilder) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) ApiEventTransfer(org.killbill.billing.subscription.events.user.ApiEventTransfer) SubscriptionBaseEvent(org.killbill.billing.subscription.events.SubscriptionBaseEvent) DateTime(org.joda.time.DateTime) ApiEventChange(org.killbill.billing.subscription.events.user.ApiEventChange)

Example 18 with SubscriptionBaseError

use of org.killbill.billing.subscription.exceptions.SubscriptionBaseError in project killbill by killbill.

the class DefaultSubscriptionBase method getEffectiveDateForPolicy.

public DateTime getEffectiveDateForPolicy(final BillingActionPolicy policy, @Nullable final BillingAlignment alignment, @Nullable final Integer accountBillCycleDayLocal, final InternalTenantContext context) {
    final DateTime candidateResult;
    switch(policy) {
        case IMMEDIATE:
            candidateResult = clock.getUTCNow();
            break;
        case START_OF_TERM:
            if (chargedThroughDate == null) {
                candidateResult = getStartDate();
            // Will take care of billing IN_ARREAR or subscriptions that are not invoiced up to date
            } else if (!chargedThroughDate.isAfter(clock.getUTCNow())) {
                candidateResult = chargedThroughDate;
            } else {
                // In certain path (dryRun, or default catalog START_OF_TERM policy), the info is not easily available and as a result, such policy is not implemented
                Preconditions.checkState(alignment != null && context != null && accountBillCycleDayLocal != null, "START_OF_TERM not implemented in dryRun use case");
                Preconditions.checkState(alignment != BillingAlignment.BUNDLE || category != ProductCategory.ADD_ON, "START_OF_TERM not implemented for AO configured with a BUNDLE billing alignment");
                // If BCD was overriden at the subscription level, we take its latest value (it should also be reflected in the chargedThroughDate) but still required for
                // alignment purpose
                Integer bcd = getBillCycleDayLocal();
                if (bcd == null) {
                    bcd = BillCycleDayCalculator.calculateBcdForAlignment(null, this, this, alignment, context, accountBillCycleDayLocal);
                }
                final BillingPeriod billingPeriod = getLastActivePlan().getRecurringBillingPeriod();
                DateTime proposedDate = chargedThroughDate;
                while (proposedDate.isAfter(clock.getUTCNow())) {
                    proposedDate = proposedDate.minus(billingPeriod.getPeriod());
                }
                final LocalDate resultingLocalDate = BillCycleDayCalculator.alignProposedBillCycleDate(proposedDate, bcd, billingPeriod, context);
                candidateResult = context.toUTCDateTime(resultingLocalDate);
            }
            break;
        case END_OF_TERM:
            // 
            // If we have a chargedThroughDate that is 'up to date' we use it, if not default to now
            // chargedThroughDate could exist and be less than now if:
            // 1. account is not being invoiced, for e.g AUTO_INVOICING_OFF nis set
            // 2. In the case if FIXED item CTD is set using startDate of the service period
            // 
            candidateResult = (chargedThroughDate != null && chargedThroughDate.isAfter(clock.getUTCNow())) ? chargedThroughDate : clock.getUTCNow();
            break;
        default:
            throw new SubscriptionBaseError(String.format("Unexpected policy type %s", policy.toString()));
    }
    // Finally we verify we won't cancel prior the beginning of our current PHASE  -- mostly as a sanity or for test stability
    final DateTime lastTransitionTime = getCurrentPhaseStart();
    return (candidateResult.compareTo(lastTransitionTime) < 0) ? lastTransitionTime : candidateResult;
}
Also used : BillingPeriod(org.killbill.billing.catalog.api.BillingPeriod) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) LocalDate(org.joda.time.LocalDate) DateTime(org.joda.time.DateTime)

Example 19 with SubscriptionBaseError

use of org.killbill.billing.subscription.exceptions.SubscriptionBaseError in project killbill by killbill.

the class DefaultSubscriptionBaseCreateApi method verifyAndBuildSubscriptionSpecifiers.

private List<SubscriptionSpecifier> verifyAndBuildSubscriptionSpecifiers(final SubscriptionBaseBundle bundle, final boolean hasBaseOrStandalonePlanSpecifier, final List<EntitlementSpecifier> entitlements, final List<Plan> entitlementsPlans, final boolean isMigrated, final DateTime effectiveDate, final SubscriptionCatalog catalog, final AddonUtils addonUtils, final TenantContext callContext, final InternalCallContext context) throws SubscriptionBaseApiException, CatalogApiException {
    final List<SubscriptionSpecifier> subscriptions = new ArrayList<SubscriptionSpecifier>();
    for (int i = 0; i < entitlements.size(); i++) {
        final EntitlementSpecifier entitlement = entitlements.get(i);
        final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
        if (spec == null) {
            // BP already exists
            continue;
        }
        final Plan plan = entitlementsPlans.get(i);
        final PlanPhase phase = plan.getAllPhases()[0];
        if (phase == null) {
            throw new SubscriptionBaseError(String.format("No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog", spec.getProductName(), spec.getBillingPeriod().toString(), plan.getPriceList()));
        }
        // verify the number of subscriptions (of the same kind) allowed per bundle and the existing ones
        if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
            if (plan.getPlansAllowedInBundle() != -1 && plan.getPlansAllowedInBundle() > 0) {
                // TODO We should also look to the specifiers being created for validation
                final List<DefaultSubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundle.getId(), null, catalog, addonUtils, callContext, context);
                final int existingAddOnsWithSamePlanName = addonUtils.countExistingAddOnsWithSamePlanName(subscriptionsForBundle, plan.getName());
                final int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlementsPlans, plan);
                if ((existingAddOnsWithSamePlanName + currentAddOnsWithSamePlanName) > plan.getPlansAllowedInBundle()) {
                    // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
                    throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
                }
            }
        }
        final DateTime bundleStartDate;
        if (hasBaseOrStandalonePlanSpecifier) {
            bundleStartDate = effectiveDate;
        } else {
            final SubscriptionBase baseSubscription = dao.getBaseSubscription(bundle.getId(), catalog, context);
            bundleStartDate = getBundleStartDateWithSanity(bundle.getId(), baseSubscription, plan, effectiveDate, addonUtils, context);
        }
        final SubscriptionSpecifier subscription = new SubscriptionSpecifier();
        subscription.setRealPriceList(plan.getPriceList().getName());
        subscription.setEffectiveDate(effectiveDate);
        subscription.setProcessedDate(context.getCreatedDate());
        subscription.setPlan(plan);
        subscription.setInitialPhase(spec.getPhaseType());
        subscription.setBuilder(new SubscriptionBuilder().setId(UUIDs.randomUUID()).setBundleId(bundle.getId()).setExternalKey(entitlement.getExternalKey()).setBundleExternalKey(bundle.getExternalKey()).setCategory(plan.getProduct().getCategory()).setBundleStartDate(bundleStartDate).setAlignStartDate(effectiveDate).setMigrated(isMigrated).setSubscriptionBCD(entitlement.getBillCycleDay()));
        subscriptions.add(subscription);
    }
    return subscriptions;
}
Also used : SubscriptionSpecifier(org.killbill.billing.subscription.api.user.SubscriptionSpecifier) PlanPhaseSpecifier(org.killbill.billing.catalog.api.PlanPhaseSpecifier) ArrayList(java.util.ArrayList) SubscriptionBuilder(org.killbill.billing.subscription.api.user.SubscriptionBuilder) Plan(org.killbill.billing.catalog.api.Plan) DateTime(org.joda.time.DateTime) EntitlementSpecifier(org.killbill.billing.entitlement.api.EntitlementSpecifier) SubscriptionBase(org.killbill.billing.subscription.api.SubscriptionBase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) PlanPhase(org.killbill.billing.catalog.api.PlanPhase) DefaultSubscriptionBase(org.killbill.billing.subscription.api.user.DefaultSubscriptionBase) SubscriptionBaseApiException(org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)

Example 20 with SubscriptionBaseError

use of org.killbill.billing.subscription.exceptions.SubscriptionBaseError in project killbill by killbill.

the class DefaultSubscriptionBaseService method onPhaseEvent.

private boolean onPhaseEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent readyPhaseEvent, final SubscriptionCatalog catalog, final InternalCallContext context) {
    try {
        final TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, readyPhaseEvent.getEffectiveDate(), catalog, context);
        final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ? PhaseEventData.createNextPhaseEvent(subscription.getId(), nextTimedPhase.getPhase().getName(), nextTimedPhase.getStartPhase()) : null;
        if (nextPhaseEvent != null) {
            dao.createNextPhaseEvent(subscription, readyPhaseEvent, nextPhaseEvent, context);
            return true;
        }
    } catch (final SubscriptionBaseError e) {
        log.warn("Error inserting next phase for subscriptionId='{}'", subscription.getId(), e);
    }
    return false;
}
Also used : PhaseEvent(org.killbill.billing.subscription.events.phase.PhaseEvent) SubscriptionBaseError(org.killbill.billing.subscription.exceptions.SubscriptionBaseError) TimedPhase(org.killbill.billing.subscription.alignment.TimedPhase)

Aggregations

SubscriptionBaseError (org.killbill.billing.subscription.exceptions.SubscriptionBaseError)22 DateTime (org.joda.time.DateTime)11 Plan (org.killbill.billing.catalog.api.Plan)7 PlanPhase (org.killbill.billing.catalog.api.PlanPhase)6 PlanPhaseSpecifier (org.killbill.billing.catalog.api.PlanPhaseSpecifier)6 ArrayList (java.util.ArrayList)5 DefaultSubscriptionBase (org.killbill.billing.subscription.api.user.DefaultSubscriptionBase)5 SubscriptionBaseEvent (org.killbill.billing.subscription.events.SubscriptionBaseEvent)5 CatalogApiException (org.killbill.billing.catalog.api.CatalogApiException)4 PlanSpecifier (org.killbill.billing.catalog.api.PlanSpecifier)4 SubscriptionBase (org.killbill.billing.subscription.api.SubscriptionBase)4 SubscriptionBaseApiException (org.killbill.billing.subscription.api.user.SubscriptionBaseApiException)4 PhaseEvent (org.killbill.billing.subscription.events.phase.PhaseEvent)4 UUID (java.util.UUID)3 Catalog (org.killbill.billing.catalog.api.Catalog)3 SubscriptionBuilder (org.killbill.billing.subscription.api.user.SubscriptionBuilder)3 Date (java.util.Date)2 LinkedList (java.util.LinkedList)2 LocalDate (org.joda.time.LocalDate)2 InternalCallContext (org.killbill.billing.callcontext.InternalCallContext)2