use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.
the class DefaultSubscriptionDao method mergeDryRunEvents.
private void mergeDryRunEvents(final UUID subscriptionId, final List<SubscriptionBaseEvent> events, @Nullable final Collection<SubscriptionBaseEvent> dryRunEvents) {
if (dryRunEvents == null || dryRunEvents.isEmpty()) {
return;
}
for (final SubscriptionBaseEvent curDryRun : dryRunEvents) {
if (curDryRun.getSubscriptionId() != null && curDryRun.getSubscriptionId().equals(subscriptionId)) {
//boolean inserted = false;
final Iterator<SubscriptionBaseEvent> it = events.iterator();
while (it.hasNext()) {
final SubscriptionBaseEvent event = it.next();
if (event.getEffectiveDate().isAfter(curDryRun.getEffectiveDate())) {
it.remove();
}
}
// Set total ordering value of the fake dryRun event to make sure billing events are correctly ordered
final SubscriptionBaseEvent curAdjustedDryRun;
if (!events.isEmpty()) {
final EventBaseBuilder eventBuilder;
switch(curDryRun.getType()) {
case PHASE:
eventBuilder = new PhaseEventBuilder((PhaseEvent) curDryRun);
break;
case BCD_UPDATE:
eventBuilder = new BCDEventBuilder((BCDEvent) curDryRun);
break;
case API_USER:
default:
eventBuilder = new ApiEventBuilder((ApiEvent) curDryRun);
break;
}
eventBuilder.setTotalOrdering(events.get(events.size() - 1).getTotalOrdering() + 1);
curAdjustedDryRun = eventBuilder.build();
} else {
curAdjustedDryRun = curDryRun;
}
events.add(curAdjustedDryRun);
}
}
}
use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.
the class TestUserApiChangePlan method testChangePlanBundleAlignEOTWithChargeThroughDate.
private void testChangePlanBundleAlignEOTWithChargeThroughDate(final String fromProd, final BillingPeriod fromTerm, final String fromPlanSet, final String toProd, final BillingPeriod toTerm, final String toPlanSet) throws SubscriptionBillingApiException, SubscriptionBaseApiException {
// CREATE
DefaultSubscriptionBase subscription = testUtil.createSubscription(bundle, fromProd, fromTerm, fromPlanSet);
final PlanPhase trialPhase = subscription.getCurrentPhase();
final DateTime expectedPhaseTrialChange = TestSubscriptionHelper.addDuration(subscription.getStartDate(), trialPhase.getDuration());
assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
// MOVE TO NEXT PHASE
testListener.pushExpectedEvent(NextEvent.PHASE);
Interval it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusDays(31));
clock.addDeltaFromReality(it.toDurationMillis());
assertListenerStatus();
PlanPhase currentPhase = subscription.getCurrentPhase();
assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
// SET CTD
final Duration ctd = testUtil.getDurationMonth(1);
final DateTime newChargedThroughDate = TestSubscriptionHelper.addDuration(expectedPhaseTrialChange, ctd);
subscriptionInternalApi.setChargedThroughDate(subscription.getId(), newChargedThroughDate, internalCallContext);
// RE READ SUBSCRIPTION + CHANGE PLAN
subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
subscription.changePlan(new PlanSpecifier(toProd, toTerm, toPlanSet), null, callContext);
assertListenerStatus();
// CHECK CHANGE PLAN
currentPhase = subscription.getCurrentPhase();
checkChangePlan(subscription, fromProd, ProductCategory.BASE, fromTerm, PhaseType.DISCOUNT);
// NEXT PHASE
final DateTime nextExpectedPhaseChange = TestSubscriptionHelper.addDuration(expectedPhaseTrialChange, currentPhase.getDuration());
testUtil.checkNextPhaseChange(subscription, 2, nextExpectedPhaseChange);
// ALSO VERIFY PENDING CHANGE EVENT
final List<SubscriptionBaseEvent> events = dao.getPendingEventsForSubscription(subscription.getId(), internalCallContext);
assertTrue(events.get(0) instanceof ApiEvent);
// MOVE TO EOT
testListener.pushExpectedEvent(NextEvent.CHANGE);
it = new Interval(clock.getUTCNow(), clock.getUTCNow().plusMonths(1));
clock.addDeltaFromReality(it.toDurationMillis());
assertListenerStatus();
subscription = (DefaultSubscriptionBase) subscriptionInternalApi.getSubscriptionFromId(subscription.getId(), internalCallContext);
currentPhase = subscription.getCurrentPhase();
checkChangePlan(subscription, toProd, ProductCategory.BASE, toTerm, PhaseType.DISCOUNT);
assertListenerStatus();
}
use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.
the class DefaultSubscriptionBaseTimeline method toExistingEvents.
private List<ExistingEvent> toExistingEvents(final Catalog catalog, final ProductCategory category, final List<SubscriptionBaseEvent> events) throws CatalogApiException {
final List<ExistingEvent> result = new LinkedList<SubscriptionBaseTimeline.ExistingEvent>();
String prevPlanName = null;
String prevProductName = null;
BillingPeriod prevBillingPeriod = null;
String prevPriceListName = null;
PhaseType prevPhaseType = null;
DateTime startDate = null;
for (final SubscriptionBaseEvent cur : events) {
if (!cur.isActive()) {
continue;
}
startDate = (startDate == null) ? cur.getEffectiveDate() : startDate;
String productName = null;
BillingPeriod billingPeriod = null;
String priceListName = null;
PhaseType phaseType = null;
String planName = null;
String planPhaseName = null;
Integer billCycleDayLocal = null;
ApiEventType apiType = null;
switch(cur.getType()) {
case PHASE:
final PhaseEvent phaseEV = (PhaseEvent) cur;
planPhaseName = phaseEV.getPhase();
phaseType = catalog.findPhase(phaseEV.getPhase(), cur.getEffectiveDate(), startDate).getPhaseType();
// A PHASE event always occurs within the same plan (and is never the first event)
planName = prevPlanName;
productName = prevProductName;
billingPeriod = getBillingPeriod(catalog, phaseEV.getPhase(), cur.getEffectiveDate(), startDate);
priceListName = prevPriceListName;
break;
case BCD_UPDATE:
final BCDEvent bcdEvent = (BCDEvent) cur;
billCycleDayLocal = bcdEvent.getBillCycleDayLocal();
break;
case API_USER:
final ApiEvent userEV = (ApiEvent) cur;
apiType = userEV.getApiEventType();
planName = userEV.getEventPlan();
planPhaseName = userEV.getEventPlanPhase();
final Plan plan = (userEV.getEventPlan() != null) ? catalog.findPlan(userEV.getEventPlan(), cur.getEffectiveDate(), startDate) : null;
phaseType = (userEV.getEventPlanPhase() != null) ? catalog.findPhase(userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate).getPhaseType() : prevPhaseType;
productName = (plan != null) ? plan.getProduct().getName() : prevProductName;
billingPeriod = (userEV.getEventPlanPhase() != null) ? getBillingPeriod(catalog, userEV.getEventPlanPhase(), cur.getEffectiveDate(), startDate) : prevBillingPeriod;
priceListName = (userEV.getPriceList() != null) ? userEV.getPriceList() : prevPriceListName;
break;
}
final SubscriptionBaseTransitionType transitionType = SubscriptionBaseTransitionData.toSubscriptionTransitionType(cur.getType(), apiType);
final String planNameWithClosure = planName;
final String planPhaseNameWithClosure = planPhaseName;
final Integer billCycleDayLocalWithClosure = billCycleDayLocal;
final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(planName, phaseType);
result.add(new ExistingEvent() {
@Override
public SubscriptionBaseTransitionType getSubscriptionTransitionType() {
return transitionType;
}
@Override
public ProductCategory getProductCategory() {
return category;
}
@Override
public PlanPhaseSpecifier getPlanPhaseSpecifier() {
return spec;
}
@Override
public UUID getEventId() {
return cur.getId();
}
@Override
public DateTime getEffectiveDate() {
return cur.getEffectiveDate();
}
@Override
public String getPlanName() {
return planNameWithClosure;
}
@Override
public String getPlanPhaseName() {
return planPhaseNameWithClosure;
}
@Override
public Integer getBillCycleDayLocal() {
return billCycleDayLocalWithClosure;
}
});
prevPlanName = planName;
prevProductName = productName;
prevBillingPeriod = billingPeriod;
prevPriceListName = priceListName;
prevPhaseType = phaseType;
}
sortExistingEvent(result);
return result;
}
use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.
the class DefaultSubscriptionBase method filterOutDuplicateCancelEvents.
//
// Hardening against data integrity issues where we have multiple active CANCEL (See #619):
// We skip any cancel events after the first one (subscription cannot be cancelled multiple times).
// The code should prevent such cases from happening but because of #619, some invalid data could be there so to be safe we added this code
//
// Also we remove !onDisk cancel events when there is an onDisk cancel event (can happen during the path where we process the base plan cancel notification, and are
// in the process of adding the new cancel events for the AO)
//
private void filterOutDuplicateCancelEvents(final List<SubscriptionBaseEvent> inputEvents) {
Collections.sort(inputEvents, new Comparator<SubscriptionBaseEvent>() {
@Override
public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {
int res = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
if (res == 0) {
res = o1.getTotalOrdering() < (o2.getTotalOrdering()) ? -1 : 1;
}
return res;
}
});
final boolean isCancelled = Iterables.any(inputEvents, new Predicate<SubscriptionBaseEvent>() {
@Override
public boolean apply(final SubscriptionBaseEvent input) {
if (input.isActive() && input.getType() == EventType.API_USER) {
final ApiEvent userEV = (ApiEvent) input;
if (userEV.getApiEventType() == ApiEventType.CANCEL && userEV.isFromDisk()) {
return true;
}
}
return false;
}
});
if (!isCancelled) {
return;
}
boolean foundFirstOnDiskCancel = false;
final Iterator<SubscriptionBaseEvent> it = inputEvents.iterator();
while (it.hasNext()) {
final SubscriptionBaseEvent input = it.next();
if (!input.isActive()) {
continue;
}
if (input.getType() == EventType.API_USER) {
final ApiEvent userEV = (ApiEvent) input;
if (userEV.getApiEventType() == ApiEventType.CANCEL) {
if (userEV.isFromDisk()) {
if (!foundFirstOnDiskCancel) {
foundFirstOnDiskCancel = true;
} else {
it.remove();
}
} else {
it.remove();
}
}
}
}
}
use of org.killbill.billing.subscription.events.user.ApiEvent in project killbill by killbill.
the class MockSubscriptionDaoMemory method uncancelSubscription.
@Override
public void uncancelSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> uncancelEvents, final InternalCallContext context) {
synchronized (events) {
boolean foundCancel = false;
final Iterator<SubscriptionBaseEvent> it = events.descendingIterator();
while (it.hasNext()) {
final SubscriptionBaseEvent cur = it.next();
if (cur.getSubscriptionId() != subscription.getId()) {
continue;
}
if (cur.getType() == EventType.API_USER && ((ApiEvent) cur).getApiEventType() == ApiEventType.CANCEL) {
it.remove();
foundCancel = true;
break;
}
}
if (foundCancel) {
for (final SubscriptionBaseEvent cur : uncancelEvents) {
insertEvent(cur, context);
}
}
}
}
Aggregations