use of org.killbill.billing.subscription.api.svcs.DefaultPlanPhasePriceOverridesWithCallContext in project killbill by killbill.
the class SubscriptionApiBase method populateDryRunEvents.
private void populateDryRunEvents(@Nullable final UUID bundleId, @Nullable final DryRunArguments dryRunArguments, final Collection<SubscriptionBaseEvent> outputDryRunEvents, final Collection<DefaultSubscriptionBase> outputSubscriptions, final SubscriptionCatalog catalog, final AddonUtils addonUtils, final TenantContext tenantContext, final InternalTenantContext context) throws SubscriptionBaseApiException, CatalogApiException {
if (dryRunArguments == null || dryRunArguments.getAction() == null) {
return;
}
final DateTime utcNow = clock.getUTCNow();
List<SubscriptionBaseEvent> dryRunEvents = null;
final EntitlementSpecifier entitlementSpecifier = dryRunArguments.getEntitlementSpecifier();
final PlanPhaseSpecifier inputSpec = entitlementSpecifier.getPlanPhaseSpecifier();
final boolean isInputSpecNullOrEmpty = inputSpec == null || (inputSpec.getPlanName() == null && inputSpec.getProductName() == null && inputSpec.getBillingPeriod() == null);
// Create an overridesWithContext with a null context to indicate this is dryRun and no price overridden plan should be created.
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(entitlementSpecifier.getOverrides(), null);
final LocalDate dryRunEffDt = dryRunArguments.getEffectiveDate();
// EffectiveDate for the event is LocalDate but Catalog effectiveDate is a DateTime, so to maximize the chance of finding 'a' version
// on that date we take the time closer to midnight - if 2 versions where uploaded on the same LocalDate this is returns the latest one.
final DateTime catalogTargetDt = dryRunEffDt != null ? dryRunEffDt.toDateTimeAtStartOfDay(DateTimeZone.UTC).plusDays(1).minusSeconds(1) : utcNow;
final StaticCatalog staticCatalog = catalog.versionForDate(catalogTargetDt);
final Plan plan = isInputSpecNullOrEmpty ? null : staticCatalog.createOrFindPlan(inputSpec, overridesWithContext);
switch(dryRunArguments.getAction()) {
case START_BILLING:
final SubscriptionBase baseSubscription = dao.getBaseSubscription(bundleId, catalog, context);
final DateTime startEffectiveDate = dryRunEffDt != null ? context.toUTCDateTime(dryRunEffDt) : utcNow;
final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, addonUtils, context);
final UUID subscriptionId = UUIDs.randomUUID();
dryRunEvents = apiService.getEventsOnCreation(subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), plan.getPriceList().getName(), startEffectiveDate, entitlementSpecifier.getBillCycleDay(), catalog, context);
final SubscriptionBuilder builder = new SubscriptionBuilder().setId(subscriptionId).setBundleId(bundleId).setBundleExternalKey(null).setCategory(plan.getProduct().getCategory()).setBundleStartDate(bundleStartDate).setAlignStartDate(startEffectiveDate);
final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
newSubscription.rebuildTransitions(dryRunEvents, catalog);
outputSubscriptions.add(newSubscription);
break;
case CHANGE:
final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), catalog, context);
DateTime changeEffectiveDate = getDryRunEffectiveDate(dryRunEffDt, subscriptionForChange, context);
if (changeEffectiveDate == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
policy = planChangeResult.getPolicy();
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
changeEffectiveDate = subscriptionForChange.getEffectiveDateForPolicy(policy, null, -1, context);
}
dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceList().getName(), changeEffectiveDate, true, entitlementSpecifier.getBillCycleDay(), catalog, context);
break;
case STOP_BILLING:
final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), catalog, context);
DateTime cancelEffectiveDate = getDryRunEffectiveDate(dryRunEffDt, subscriptionForCancellation, context);
if (dryRunEffDt == null) {
BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
if (policy == null) {
final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(), subscriptionForCancellation.getCurrentPhase().getPhaseType());
policy = catalog.planCancelPolicy(spec, clock.getUTCNow(), subscriptionForCancellation.getStartDate());
}
// We pass null for billingAlignment, accountTimezone, account BCD because this is not available which means that dryRun with START_OF_TERM BillingPolicy will fail
cancelEffectiveDate = subscriptionForCancellation.getEffectiveDateForPolicy(policy, null, -1, context);
}
dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, cancelEffectiveDate, true, catalog, context);
break;
default:
throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
}
if (dryRunEvents != null && !dryRunEvents.isEmpty()) {
outputDryRunEvents.addAll(dryRunEvents);
}
}
use of org.killbill.billing.subscription.api.svcs.DefaultPlanPhasePriceOverridesWithCallContext in project killbill by killbill.
the class DefaultSubscriptionBaseApiService method doChangePlan.
private void doChangePlan(final DefaultSubscriptionBase subscription, final PlanSpecifier spec, final List<PlanPhasePriceOverride> overrides, final DateTime effectiveDate, final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(overrides, context);
final Plan newPlan = catalogService.getFullCatalog(true, true, internalCallContext).createOrFindPlan(spec, overridesWithContext, effectiveDate, subscription.getStartDate());
final PhaseType initialPhaseType;
if (overrides != null && overrides.size() == 1 && overrides.get(0).getPlanPhaseSpecifier() != null && overrides.get(0).getCurrency() == null) {
initialPhaseType = overrides.get(0).getPlanPhaseSpecifier().getPhaseType();
} else {
initialPhaseType = null;
}
if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(newPlan.getProduct().getCategory().toString())) {
if (newPlan.getPlansAllowedInBundle() != -1 && newPlan.getPlansAllowedInBundle() > 0 && addonUtils.countExistingAddOnsWithSamePlanName(dao.getSubscriptions(subscription.getBundleId(), null, internalCallContext), newPlan.getName()) >= newPlan.getPlansAllowedInBundle()) {
// the plan can be changed to the new value, because it has reached its limit by bundle
throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, newPlan.getName());
}
}
if (newPlan.getProduct().getCategory() != subscription.getCategory()) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_INVALID, subscription.getId());
}
final List<DefaultSubscriptionBase> addOnSubscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
final List<SubscriptionBaseEvent> addOnCancelEvents = new ArrayList<SubscriptionBaseEvent>();
final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPlan.getPriceListName(), effectiveDate, true, addOnSubscriptionsToBeCancelled, addOnCancelEvents, initialPhaseType, internalCallContext);
dao.changePlan(subscription, changeEvents, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalCallContext);
final Catalog fullCatalog = catalogService.getFullCatalog(true, true, internalCallContext);
subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), fullCatalog);
}
use of org.killbill.billing.subscription.api.svcs.DefaultPlanPhasePriceOverridesWithCallContext in project killbill by killbill.
the class DefaultSubscriptionBaseApiService method doChangePlan.
private void doChangePlan(final DefaultSubscriptionBase subscription, final EntitlementSpecifier spec, final DateTime effectiveDate, final CallContext context) throws SubscriptionBaseApiException, CatalogApiException {
final InternalCallContext internalCallContext = createCallContextFromBundleId(subscription.getBundleId(), context);
final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(spec.getOverrides(), context);
final SubscriptionCatalog catalog = subscriptionCatalogApi.getFullCatalog(internalCallContext);
final PlanPhaseSpecifier planPhaseSpecifier = spec.getPlanPhaseSpecifier();
final StaticCatalog versionCatalog = catalog.versionForDate(effectiveDate);
final Plan newPlan = versionCatalog.createOrFindPlan(planPhaseSpecifier, overridesWithContext);
final PhaseType initialPhaseType = planPhaseSpecifier.getPhaseType();
if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(newPlan.getProduct().getCategory().toString())) {
if (newPlan.getPlansAllowedInBundle() != -1 && newPlan.getPlansAllowedInBundle() > 0 && addonUtils.countExistingAddOnsWithSamePlanName(dao.getSubscriptions(subscription.getBundleId(), null, catalog, internalCallContext), newPlan.getName()) >= newPlan.getPlansAllowedInBundle()) {
// the plan can be changed to the new value, because it has reached its limit by bundle
throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, newPlan.getName());
}
}
if (newPlan.getProduct().getCategory() != subscription.getCategory()) {
throw new SubscriptionBaseApiException(ErrorCode.SUB_CHANGE_INVALID, subscription.getId());
}
final List<DefaultSubscriptionBase> addOnSubscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
final List<SubscriptionBaseEvent> addOnCancelEvents = new ArrayList<SubscriptionBaseEvent>();
final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPlan.getPriceList().getName(), effectiveDate, true, addOnSubscriptionsToBeCancelled, addOnCancelEvents, initialPhaseType, spec.getBillCycleDay(), catalog, internalCallContext);
dao.changePlan(subscription, changeEvents, addOnSubscriptionsToBeCancelled, addOnCancelEvents, catalog, internalCallContext);
subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId(), internalCallContext), catalog);
}
Aggregations