Details
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
index 5e94a51..3b54252 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
@@ -75,6 +75,11 @@ public class EhCacheOverriddenPlanCache implements OverriddenPlanCache {
return (DefaultPlan) cacheController.get(planName, argument);
}
+ @Override
+ public void addDryRunPlan(final String planName, final Plan plan) {
+ cacheController.putIfAbsent(planName, plan);
+ }
+
private DefaultPlan loadOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException {
final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
index f0f8178..f888dc8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/OverriddenPlanCache.java
@@ -20,9 +20,12 @@ package org.killbill.billing.catalog.caching;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.DefaultPlan;
import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.StaticCatalog;
public interface OverriddenPlanCache {
DefaultPlan getOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException;
+
+ void addDryRunPlan(final String planName, final Plan plan);
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
index c8165b5..86b8739 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
@@ -18,8 +18,11 @@
package org.killbill.billing.catalog.override;
import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
import org.joda.time.DateTime;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalCallContext;
@@ -43,6 +46,8 @@ import com.google.inject.Inject;
public class DefaultPriceOverride implements PriceOverride {
+ private static final AtomicLong DRY_RUN_PLAN_IDX = new AtomicLong(0);
+
public static final Pattern CUSTOM_PLAN_NAME_PATTERN = Pattern.compile("(.*)-(\\d+)$");
private final CatalogOverrideDao overrideDao;
@@ -55,7 +60,7 @@ public class DefaultPriceOverride implements PriceOverride {
}
@Override
- public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException {
+ public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, @Nullable final InternalCallContext context) throws CatalogApiException {
final PlanPhasePriceOverride[] resolvedOverride = new PlanPhasePriceOverride[parentPlan.getAllPhases().length];
int index = 0;
@@ -97,9 +102,17 @@ public class DefaultPriceOverride implements PriceOverride {
}
}
- final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan.getName(), catalogEffectiveDate, resolvedOverride, context);
- final String planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
+ final String planName;
+ if (context != null) {
+ final CatalogOverridePlanDefinitionModelDao overriddenPlan = overrideDao.getOrCreateOverridePlanDefinition(parentPlan.getName(), catalogEffectiveDate, resolvedOverride, context);
+ planName = new StringBuffer(parentPlan.getName()).append("-").append(overriddenPlan.getRecordId()).toString();
+ } else {
+ planName = new StringBuffer(parentPlan.getName()).append("-dryrun-").append(DRY_RUN_PLAN_IDX.incrementAndGet()).toString();
+ }
final DefaultPlan result = new DefaultPlan(planName, (DefaultPlan) parentPlan, resolvedOverride);
+ if (context == null) {
+ overriddenPlanCache.addDryRunPlan(planName, result);
+ }
return result;
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index 8a458c4..fbffc1f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -78,7 +78,7 @@ public class StandaloneCatalogWithPriceOverride extends StandaloneCatalog implem
return defaultPlan;
}
- final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(overrides.getCallContext());
+ final InternalCallContext internalCallContext = overrides.getCallContext() != null ? internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(overrides.getCallContext()) : null;
return priceOverride.getOrCreateOverriddenPlan(defaultPlan, CatalogDateHelper.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
index 112f24b..79a2b8b 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
@@ -947,7 +947,7 @@ public class CatalogJson {
this.number = number;
}
- public DurationJson(final Duration duration) throws CurrencyValueNull {
+ public DurationJson(final Duration duration) {
this(duration.getUnit(), duration.getNumber());
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java
new file mode 100644
index 0000000..4f93cad
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueConditionJson.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.jaxrs.json.CatalogJson.DurationJson;
+import org.killbill.billing.overdue.api.OverdueCondition;
+import org.killbill.billing.overdue.config.DefaultDuration;
+import org.killbill.billing.overdue.config.DefaultOverdueCondition;
+import org.killbill.billing.util.tag.ControlTagType;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OverdueConditionJson {
+
+ private final DurationJson timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+ private final ControlTagType controlTagInclusion;
+ private final ControlTagType controlTagExclusion;
+
+ @JsonCreator
+ public OverdueConditionJson(@JsonProperty("timeSinceEarliestUnpaidInvoiceEqualsOrExceeds") final DurationJson timeSinceEarliestUnpaidInvoiceEqualsOrExceeds,
+ @JsonProperty("controlTagInclusion") final ControlTagType controlTagInclusion,
+ @JsonProperty("controlTagExclusion") final ControlTagType controlTagExclusion) {
+ this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+ this.controlTagInclusion = controlTagInclusion;
+ this.controlTagExclusion = controlTagExclusion;
+ }
+
+ public OverdueConditionJson(final OverdueCondition overdueCondition) {
+ this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = new DurationJson(overdueCondition.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds());
+ this.controlTagInclusion = overdueCondition.getInclusionControlTagType();
+ this.controlTagExclusion = overdueCondition.getExclusionControlTagType();
+ }
+
+ public DurationJson getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds() {
+ return timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+ }
+
+ public ControlTagType getControlTagInclusion() {
+ return controlTagInclusion;
+ }
+
+ public ControlTagType getControlTagExclusion() {
+ return controlTagExclusion;
+ }
+
+ @Override
+ public String toString() {
+ return "OverdueConditionJson{" +
+ "timeSinceEarliestUnpaidInvoiceEqualsOrExceeds=" + timeSinceEarliestUnpaidInvoiceEqualsOrExceeds +
+ ", controlTagInclusion=" + controlTagInclusion +
+ ", controlTagExclusion=" + controlTagExclusion +
+ '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof OverdueConditionJson)) {
+ return false;
+ }
+
+ final OverdueConditionJson that = (OverdueConditionJson) o;
+
+ if (timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null ? !timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.equals(that.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds) : that.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null) {
+ return false;
+ }
+ if (controlTagInclusion != that.controlTagInclusion) {
+ return false;
+ }
+ return controlTagExclusion == that.controlTagExclusion;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds != null ? timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.hashCode() : 0;
+ result = 31 * result + (controlTagInclusion != null ? controlTagInclusion.hashCode() : 0);
+ result = 31 * result + (controlTagExclusion != null ? controlTagExclusion.hashCode() : 0);
+ return result;
+ }
+
+ public static DefaultOverdueCondition toOverdueCondition(final OverdueConditionJson input) {
+ final DefaultOverdueCondition result = new DefaultOverdueCondition();
+ if (input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds() != null) {
+ result.setTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds(new DefaultDuration().setUnit(input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getUnit()).setNumber(input.getTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds().getNumber()));
+ }
+ result.setControlTagInclusion(input.getControlTagInclusion());
+ result.setControlTagExclusion(input.getControlTagExclusion());
+ return result;
+ }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java
new file mode 100644
index 0000000..f59c110
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueJson.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import java.util.List;
+
+import org.killbill.billing.catalog.api.CurrencyValueNull;
+import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.overdue.api.OverdueApiException;
+import org.killbill.billing.overdue.api.OverdueConfig;
+import org.killbill.billing.overdue.api.OverdueState;
+import org.killbill.billing.overdue.config.DefaultDuration;
+import org.killbill.billing.overdue.config.DefaultOverdueConfig;
+import org.killbill.billing.overdue.config.DefaultOverdueState;
+import org.killbill.billing.overdue.config.DefaultOverdueStatesAccount;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class OverdueJson {
+
+ private final Integer initialReevaluationIntervalDays;
+ private final List<OverdueStateConfigJson> overdueStates;
+
+ @JsonCreator
+ public OverdueJson(@JsonProperty("initialReevaluationInterval") final Integer initialReevaluationInterval,
+ @JsonProperty("overdueStates") final List<OverdueStateConfigJson> overdueStates) {
+ this.initialReevaluationIntervalDays = initialReevaluationInterval;
+ this.overdueStates = overdueStates;
+ }
+
+ public OverdueJson(final OverdueConfig overdueConfig) {
+ this.initialReevaluationIntervalDays = overdueConfig.getOverdueStatesAccount().getInitialReevaluationInterval() != null ?
+ overdueConfig.getOverdueStatesAccount().getInitialReevaluationInterval().getDays() : null;
+ this.overdueStates = ImmutableList.copyOf(Iterables.transform(ImmutableList.copyOf(overdueConfig.getOverdueStatesAccount().getStates()), new Function<OverdueState, OverdueStateConfigJson>() {
+ @Override
+ public OverdueStateConfigJson apply(final OverdueState input) {
+ return new OverdueStateConfigJson(input);
+ }
+ }));
+ }
+
+ public Integer getInitialReevaluationInterval() {
+ return initialReevaluationIntervalDays;
+ }
+
+ public List<OverdueStateConfigJson> getOverdueStates() {
+ return overdueStates;
+ }
+
+ @Override
+ public String toString() {
+ return "OverdueJson{" +
+ "initialReevaluationIntervalDays=" + initialReevaluationIntervalDays +
+ ", overdueStates=" + overdueStates +
+ '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof OverdueJson)) {
+ return false;
+ }
+
+ final OverdueJson that = (OverdueJson) o;
+
+ if (initialReevaluationIntervalDays != null ? !initialReevaluationIntervalDays.equals(that.initialReevaluationIntervalDays) : that.initialReevaluationIntervalDays != null) {
+ return false;
+ }
+ return overdueStates != null ? overdueStates.equals(that.overdueStates) : that.overdueStates == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = initialReevaluationIntervalDays != null ? initialReevaluationIntervalDays.hashCode() : 0;
+ result = 31 * result + (overdueStates != null ? overdueStates.hashCode() : 0);
+ return result;
+ }
+
+ public static OverdueConfig toOverdueConfig(final OverdueJson input) {
+ final DefaultOverdueConfig result = new DefaultOverdueConfig();
+ final DefaultOverdueStatesAccount overdueStateAccount = new DefaultOverdueStatesAccount();
+ result.setOverdueStates(overdueStateAccount);
+
+ final DefaultOverdueState [] states = new DefaultOverdueState[input.getOverdueStates().size()];
+ int i = 0;
+ for (final OverdueStateConfigJson cur : input.getOverdueStates()) {
+ final DefaultOverdueState state = new DefaultOverdueState();
+ state.setName(cur.getName());
+ state.setExternalMessage(cur.getExternalMessage());
+ state.setBlockChanges(cur.getBlockChanges());
+ state.setDisableEntitlement(cur.getDisableEntitlement());
+ state.setSubscriptionCancellationPolicy(cur.getSubscriptionCancellationPolicy());
+ state.setClearState(cur.isClearState());
+ state.setAutoReevaluationInterval((new DefaultDuration()).setUnit(TimeUnit.DAYS).setNumber(cur.getAutoReevaluationIntervalDays()));
+ state.setCondition(OverdueConditionJson.toOverdueCondition(cur.getCondition()));
+ states[i++] = state;
+ }
+ overdueStateAccount.setAccountOverdueStates(states);
+ overdueStateAccount.setInitialReevaluationInterval(null);
+ return result;
+ }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java
new file mode 100644
index 0000000..ab06260
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/OverdueStateConfigJson.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import org.killbill.billing.overdue.api.OverdueApiException;
+import org.killbill.billing.overdue.api.OverdueCancellationPolicy;
+import org.killbill.billing.overdue.api.OverdueState;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class OverdueStateConfigJson {
+
+ private final String name;
+ private final Boolean isClearState;
+ private final OverdueConditionJson condition;
+ private final String externalMessage;
+ private final Boolean blockChanges;
+ private final Boolean disableEntitlement;
+ private final OverdueCancellationPolicy subscriptionCancellationPolicy;
+ private final Integer autoReevaluationIntervalDays;
+
+ @JsonCreator
+ public OverdueStateConfigJson(@JsonProperty("name") final String name,
+ @JsonProperty("isClearState") final Boolean isClearState,
+ @JsonProperty("condition") final OverdueConditionJson condition,
+ @JsonProperty("externalMessage") final String externalMessage,
+ @JsonProperty("blockChanges") final Boolean blockChanges,
+ @JsonProperty("disableEntitlement") final Boolean disableEntitlement,
+ @JsonProperty("subscriptionCancellationPolicy") final OverdueCancellationPolicy subscriptionCancellationPolicy,
+ @JsonProperty("autoReevaluationIntervalDays") final Integer autoReevaluationInterval) {
+ this.name = name;
+ this.isClearState = isClearState;
+ this.condition = condition;
+ this.externalMessage = externalMessage;
+ this.blockChanges = blockChanges;
+ this.disableEntitlement = disableEntitlement;
+ this.subscriptionCancellationPolicy = subscriptionCancellationPolicy;
+ this.autoReevaluationIntervalDays = autoReevaluationInterval;
+ }
+
+ public OverdueStateConfigJson(final OverdueState input) {
+ this.name = input.getName();
+ this.isClearState = input.isClearState();
+ this.condition = input.getOverdueCondition() != null ? new OverdueConditionJson(input.getOverdueCondition()) : null;
+ this.externalMessage = input.getExternalMessage();
+ this.blockChanges = input.isBlockChanges();
+ this.disableEntitlement = input.isDisableEntitlementAndChangesBlocked();
+ this.subscriptionCancellationPolicy = input.getOverdueCancellationPolicy();
+ Integer tmpAutoReevaluationIntervalDays = null;
+ try {
+ tmpAutoReevaluationIntervalDays = input.getAutoReevaluationInterval().getDays();
+ } catch (final OverdueApiException e) {
+ } finally {
+ this.autoReevaluationIntervalDays = tmpAutoReevaluationIntervalDays;
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @JsonProperty("isClearState")
+ public Boolean isClearState() {
+ return isClearState;
+ }
+
+ public OverdueConditionJson getCondition() {
+ return condition;
+ }
+
+ public String getExternalMessage() {
+ return externalMessage;
+ }
+
+ public Boolean getBlockChanges() {
+ return blockChanges;
+ }
+
+ public Boolean getDisableEntitlement() {
+ return disableEntitlement;
+ }
+
+ public OverdueCancellationPolicy getSubscriptionCancellationPolicy() {
+ return subscriptionCancellationPolicy;
+ }
+
+ public Integer getAutoReevaluationIntervalDays() {
+ return autoReevaluationIntervalDays;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof OverdueStateConfigJson)) {
+ return false;
+ }
+
+ final OverdueStateConfigJson that = (OverdueStateConfigJson) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+ if (isClearState != null ? !isClearState.equals(that.isClearState) : that.isClearState != null) {
+ return false;
+ }
+ if (condition != null ? !condition.equals(that.condition) : that.condition != null) {
+ return false;
+ }
+ if (externalMessage != null ? !externalMessage.equals(that.externalMessage) : that.externalMessage != null) {
+ return false;
+ }
+ if (blockChanges != null ? !blockChanges.equals(that.blockChanges) : that.blockChanges != null) {
+ return false;
+ }
+ if (disableEntitlement != null ? !disableEntitlement.equals(that.disableEntitlement) : that.disableEntitlement != null) {
+ return false;
+ }
+ if (subscriptionCancellationPolicy != that.subscriptionCancellationPolicy) {
+ return false;
+ }
+ return autoReevaluationIntervalDays != null ? autoReevaluationIntervalDays.equals(that.autoReevaluationIntervalDays) : that.autoReevaluationIntervalDays == null;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (isClearState != null ? isClearState.hashCode() : 0);
+ result = 31 * result + (condition != null ? condition.hashCode() : 0);
+ result = 31 * result + (externalMessage != null ? externalMessage.hashCode() : 0);
+ result = 31 * result + (blockChanges != null ? blockChanges.hashCode() : 0);
+ result = 31 * result + (disableEntitlement != null ? disableEntitlement.hashCode() : 0);
+ result = 31 * result + (subscriptionCancellationPolicy != null ? subscriptionCancellationPolicy.hashCode() : 0);
+ result = 31 * result + (autoReevaluationIntervalDays != null ? autoReevaluationIntervalDays.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "OverdueStateConfigJson{" +
+ "name='" + name + '\'' +
+ ", isClearState=" + isClearState +
+ ", condition=" + condition +
+ ", externalMessage='" + externalMessage + '\'' +
+ ", blockChanges=" + blockChanges +
+ ", disableEntitlement=" + disableEntitlement +
+ ", subscriptionCancellationPolicy=" + subscriptionCancellationPolicy +
+ ", autoReevaluationIntervalDays=" + autoReevaluationIntervalDays +
+ '}';
+ }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
index a30a0cf..3ae8ae6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
@@ -20,6 +20,8 @@ package org.killbill.billing.jaxrs.resources;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
@@ -28,14 +30,24 @@ import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.StaticCatalog;
+import org.killbill.billing.jaxrs.json.CatalogJson;
+import org.killbill.billing.jaxrs.json.OverdueJson;
import org.killbill.billing.jaxrs.util.Context;
import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
+import org.killbill.billing.overdue.api.DefaultOverdueApi;
import org.killbill.billing.overdue.api.OverdueApi;
+import org.killbill.billing.overdue.api.OverdueConfig;
import org.killbill.billing.overdue.config.DefaultOverdueConfig;
import org.killbill.billing.payment.api.PaymentApi;
import org.killbill.billing.util.api.AuditUserApi;
@@ -54,6 +66,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponses;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
@Singleton
@@ -80,7 +93,7 @@ public class OverdueResource extends JaxRsResourceBase {
@TimedResource
@GET
@Produces(APPLICATION_XML)
- @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
+ @ApiOperation(value = "Retrieve the overdue config as XML", response = String.class, hidden = true)
@ApiResponses(value = {})
public Response getOverdueConfigXml(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
final TenantContext tenantContext = context.createContext(request);
@@ -106,4 +119,38 @@ public class OverdueResource extends JaxRsResourceBase {
overdueApi.uploadOverdueConfig(overdueXML, callContext);
return uriBuilder.buildResponse(uriInfo, OverdueResource.class, null, null);
}
+
+ @TimedResource
+ @GET
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the overdue config as JSON" , response = OverdueJson.class)
+ @ApiResponses(value = {})
+ public Response getOverdueConfigJson(@javax.ws.rs.core.Context final HttpServletRequest request) throws Exception {
+ final TenantContext tenantContext = context.createContext(request);
+ final OverdueConfig overdueConfig = overdueApi.getOverdueConfig(tenantContext);
+ final OverdueJson result = new OverdueJson(overdueConfig);
+ return Response.status(Status.OK).entity(result).build();
+ }
+
+
+
+ @TimedResource
+ @POST
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Upload the full overdue config as JSON")
+ @ApiResponses(value = {})
+ public Response uploadOverdueConfigJson(final OverdueJson overdueJson,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final HttpServletRequest request,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws Exception {
+ final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+
+ final OverdueConfig overdueConfig = OverdueJson.toOverdueConfig(overdueJson);
+ ((DefaultOverdueApi)overdueApi).uploadOverdueConfig(overdueConfig, callContext);
+ return uriBuilder.buildResponse(uriInfo, OverdueResource.class, null, null);
+ }
+
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
index 580e4f9..2364cf4 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/api/DefaultOverdueApi.java
@@ -21,6 +21,7 @@ import java.util.UUID;
import javax.inject.Inject;
+import org.killbill.billing.ErrorCode;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.BlockingStateType;
@@ -36,6 +37,7 @@ import org.killbill.billing.tenant.api.TenantUserApi;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.xmlloader.XMLWriter;
public class DefaultOverdueApi implements OverdueApi {
@@ -76,6 +78,15 @@ public class DefaultOverdueApi implements OverdueApi {
}
}
+ public void uploadOverdueConfig(final OverdueConfig overdueConfig, final CallContext callContext) throws OverdueApiException {
+ try {
+ final String overdueXML = XMLWriter.writeXML((DefaultOverdueConfig) overdueConfig, DefaultOverdueConfig.class);
+ uploadOverdueConfig(overdueXML, callContext);
+ } catch (final Exception e) {
+ throw new OverdueApiException(ErrorCode.OVERDUE_INVALID_FOR_TENANT, callContext.getTenantId());
+ }
+ }
+
@Override
public OverdueState getOverdueStateFor(final UUID accountId, final TenantContext tenantContext) throws OverdueApiException {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
index d1d73c2..49396ea 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultDuration.java
@@ -111,12 +111,12 @@ public class DefaultDuration extends ValidatingConfig<DefaultOverdueConfig> impl
return errors;
}
- protected DefaultDuration setUnit(final TimeUnit unit) {
+ public DefaultDuration setUnit(final TimeUnit unit) {
this.unit = unit;
return this;
}
- protected DefaultDuration setNumber(final Integer number) {
+ public DefaultDuration setNumber(final Integer number) {
this.number = number;
return this;
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
index 3cca103..a4e4b51 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueCondition.java
@@ -157,6 +157,30 @@ public class DefaultOverdueCondition extends ValidatingConfig<DefaultOverdueConf
return controlTagExclusion;
}
+ public void setNumberOfUnpaidInvoicesEqualsOrExceeds(final Integer numberOfUnpaidInvoicesEqualsOrExceeds) {
+ this.numberOfUnpaidInvoicesEqualsOrExceeds = numberOfUnpaidInvoicesEqualsOrExceeds;
+ }
+
+ public void setTotalUnpaidInvoiceBalanceEqualsOrExceeds(final BigDecimal totalUnpaidInvoiceBalanceEqualsOrExceeds) {
+ this.totalUnpaidInvoiceBalanceEqualsOrExceeds = totalUnpaidInvoiceBalanceEqualsOrExceeds;
+ }
+
+ public void setTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds(final DefaultDuration timeSinceEarliestUnpaidInvoiceEqualsOrExceeds) {
+ this.timeSinceEarliestUnpaidInvoiceEqualsOrExceeds = timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+ }
+
+ public void setResponseForLastFailedPayment(final PaymentResponse[] responseForLastFailedPayment) {
+ this.responseForLastFailedPayment = responseForLastFailedPayment;
+ }
+
+ public void setControlTagInclusion(final ControlTagType controlTagInclusion) {
+ this.controlTagInclusion = controlTagInclusion;
+ }
+
+ public void setControlTagExclusion(final ControlTagType controlTagExclusion) {
+ this.controlTagExclusion = controlTagExclusion;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DefaultOverdueCondition{");
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
index 9d33ddd..af49f62 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueState.java
@@ -119,22 +119,26 @@ public class DefaultOverdueState extends ValidatingConfig<DefaultOverdueConfig>
return autoReevaluationInterval.toJodaPeriod();
}
- protected DefaultOverdueState setName(final String name) {
+ public void setAutoReevaluationInterval(final DefaultDuration autoReevaluationInterval) {
+ this.autoReevaluationInterval = autoReevaluationInterval;
+ }
+
+ public DefaultOverdueState setName(final String name) {
this.name = name;
return this;
}
- protected DefaultOverdueState setClearState(final boolean isClearState) {
+ public DefaultOverdueState setClearState(final boolean isClearState) {
this.isClearState = isClearState;
return this;
}
- protected DefaultOverdueState setExternalMessage(final String externalMessage) {
+ public DefaultOverdueState setExternalMessage(final String externalMessage) {
this.externalMessage = externalMessage;
return this;
}
- protected DefaultOverdueState setDisableEntitlement(final boolean cancel) {
+ public DefaultOverdueState setDisableEntitlement(final boolean cancel) {
this.disableEntitlement = cancel;
return this;
}
@@ -144,12 +148,12 @@ public class DefaultOverdueState extends ValidatingConfig<DefaultOverdueConfig>
return this;
}
- protected DefaultOverdueState setBlockChanges(final boolean cancel) {
+ public DefaultOverdueState setBlockChanges(final boolean cancel) {
this.blockChanges = cancel;
return this;
}
- protected DefaultOverdueState setCondition(final DefaultOverdueCondition condition) {
+ public DefaultOverdueState setCondition(final DefaultOverdueCondition condition) {
this.condition = condition;
return this;
}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
index 0bd4e63..004fd2d 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/config/DefaultOverdueStatesAccount.java
@@ -45,12 +45,12 @@ public class DefaultOverdueStatesAccount extends DefaultOverdueStateSet implemen
return initialReevaluationInterval.toJodaPeriod();
}
- protected DefaultOverdueStatesAccount setAccountOverdueStates(final DefaultOverdueState[] accountOverdueStates) {
+ public DefaultOverdueStatesAccount setAccountOverdueStates(final DefaultOverdueState[] accountOverdueStates) {
this.accountOverdueStates = accountOverdueStates;
return this;
}
- protected DefaultOverdueStatesAccount setInitialReevaluationInterval(final DefaultDuration initialReevaluationInterval) {
+ public DefaultOverdueStatesAccount setInitialReevaluationInterval(final DefaultDuration initialReevaluationInterval) {
this.initialReevaluationInterval = initialReevaluationInterval;
return this;
}
diff --git a/overdue/src/test/resources/OverdueConfig3.xml b/overdue/src/test/resources/OverdueConfig3.xml
new file mode 100644
index 0000000..ddba32f
--- /dev/null
+++ b/overdue/src/test/resources/OverdueConfig3.xml
@@ -0,0 +1,71 @@
+<!--
+ ~ Copyright 2014-2016 Groupon, Inc
+ ~ Copyright 2014-2016 The Billing Project, LLC
+ ~
+ ~ The Billing Project licenses this file to you under the Apache License, version 2.0
+ ~ (the "License"); you may not use this file except in compliance with the
+ ~ License. You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ ~ License for the specific language governing permissions and limitations
+ ~ under the License.
+ -->
+
+<overdueConfig>
+ <accountOverdueStates>
+ <state name="OD4">
+ <condition>
+ <numberOfUnpaidInvoicesEqualsOrExceeds>5</numberOfUnpaidInvoicesEqualsOrExceeds>
+ <controlTagInclusion>AUTO_PAY_OFF</controlTagInclusion>
+ </condition>
+ <externalMessage>Reached OD3</externalMessage>
+ <blockChanges>true</blockChanges>
+ <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+ <autoReevaluationInterval>
+ <unit>DAYS</unit><number>5</number>
+ </autoReevaluationInterval>
+ </state>
+ <state name="OD3">
+ <condition>
+ <responseForLastFailedPaymentIn>
+ <response>INVALID_CARD</response>
+ <response>LOST_OR_STOLEN_CARD</response>
+ </responseForLastFailedPaymentIn>
+ </condition>
+ <externalMessage>Reached OD3</externalMessage>
+ <blockChanges>true</blockChanges>
+ <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+ <autoReevaluationInterval>
+ <unit>DAYS</unit><number>5</number>
+ </autoReevaluationInterval>
+ </state>
+ <state name="OD2">
+ <condition>
+ <totalUnpaidInvoiceBalanceEqualsOrExceeds>5.00</totalUnpaidInvoiceBalanceEqualsOrExceeds>
+ </condition>
+ <externalMessage>Reached OD2</externalMessage>
+ <blockChanges>true</blockChanges>
+ <disableEntitlementAndChangesBlocked>true</disableEntitlementAndChangesBlocked>
+ <autoReevaluationInterval>
+ <unit>DAYS</unit><number>5</number>
+ </autoReevaluationInterval>
+ </state>
+ <state name="OD1">
+ <condition>
+ <timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+ <unit>DAYS</unit><number>30</number>
+ </timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>
+ </condition>
+ <externalMessage>Reached OD1</externalMessage>
+ <blockChanges>true</blockChanges>
+ <disableEntitlementAndChangesBlocked>false</disableEntitlementAndChangesBlocked>
+ <autoReevaluationInterval>
+ <unit>DAYS</unit><number>5</number>
+ </autoReevaluationInterval>
+ </state>
+ </accountOverdueStates>
+</overdueConfig>
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index ad3d7c7..f2165f8 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -588,13 +588,18 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
List<SubscriptionBaseEvent> dryRunEvents = null;
try {
final PlanPhaseSpecifier inputSpec = dryRunArguments.getPlanPhaseSpecifier();
+ final boolean isInputSpecNullOrEmpty = inputSpec == null ||
+ (inputSpec.getPlanName() == null && inputSpec.getProductName() == null && inputSpec.getBillingPeriod() == null);
final Catalog catalog = catalogService.getFullCatalog(true, true, context);
- final PlanPhasePriceOverridesWithCallContext overridesWithContext = null; // TODO not supported to dryRun with custom price
- final Plan plan = (inputSpec != null && inputSpec.getProductName() != null && inputSpec.getBillingPeriod() != null) ?
- catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow) : null;
+ // Create an overridesWithContext with a null context to indicate this is dryRun and no price overriden plan should be created.
+ final PlanPhasePriceOverridesWithCallContext overridesWithContext = new DefaultPlanPhasePriceOverridesWithCallContext(dryRunArguments.getPlanPhasePriceOverrides(), null);
+ final Plan plan = isInputSpecNullOrEmpty ?
+ null :
+ catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow);
final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
+
if (dryRunArguments != null) {
switch (dryRunArguments.getAction()) {
case START_BILLING: