killbill-aplcache

Details

diff --git a/api/src/main/java/com/ning/billing/account/api/ControlTagType.java b/api/src/main/java/com/ning/billing/account/api/ControlTagType.java
index 23c23ae..926c32f 100644
--- a/api/src/main/java/com/ning/billing/account/api/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/account/api/ControlTagType.java
@@ -18,7 +18,8 @@ package com.ning.billing.account.api;
 
 public enum ControlTagType {
     AUTO_BILLING_OFF("Suspends billing until removed.", true, false),
-    AUTO_INVOICING_OFF("Suspends invoicing until removed.", false, true);
+    AUTO_INVOICING_OFF("Suspends invoicing until removed.", false, true),
+    OVERDUE_ENFORCEMENT_OFF("Suspends invoicing until removed.", false, false);
 
     private final String description;
     private final boolean autoPaymentOff;
diff --git a/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingState.java b/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingState.java
new file mode 100644
index 0000000..d1c30a1
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingState.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.api.overdue;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.util.tag.Tag;
+
+public class BillingState {
+	private final UUID objectId;
+	private final int numberOfUnpaidInvoices;
+	private final BigDecimal balanceOfUnpaidInvoices;
+	private final DateTime dateOfEarliestUnpaidInvoice;
+	private final PaymentResponse responseForLastFailedPayment;
+	private final Tag[] tags;
+	
+	public BillingState(UUID id, int numberOfUnpaidInvoices, BigDecimal balanceOfUnpaidInvoices,
+			DateTime dateOfEarliestUnpaidInvoice,
+			PaymentResponse responseForLastFailedPayment,
+			Tag[] tags) {
+		super();
+		this.objectId = id;
+		this.numberOfUnpaidInvoices = numberOfUnpaidInvoices;
+		this.balanceOfUnpaidInvoices = balanceOfUnpaidInvoices;
+		this.dateOfEarliestUnpaidInvoice = dateOfEarliestUnpaidInvoice;
+		this.responseForLastFailedPayment = responseForLastFailedPayment;
+		this.tags = tags;
+	}
+
+	public UUID getObjectId() {
+		return objectId;
+	}
+	
+	public int getNumberOfUnpaidInvoices() {
+		return numberOfUnpaidInvoices;
+	}
+
+	public BigDecimal getBalanceOfUnpaidInvoices() {
+		return balanceOfUnpaidInvoices;
+	}
+
+	public DateTime getDateOfEarliestUnpaidInvoice() {
+		return dateOfEarliestUnpaidInvoice;
+	}
+	
+	public PaymentResponse getResponseForLastFailedPayment() {
+		return responseForLastFailedPayment;
+	}
+
+	public Tag[] getTags() {
+		return tags;
+	}
+
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingStateBundle.java b/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingStateBundle.java
new file mode 100644
index 0000000..e640f61
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/overdue/BillingStateBundle.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.api.overdue;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PriceList;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.util.tag.Tag;
+
+public class BillingStateBundle extends BillingState {
+	private final BillingState accountState;
+    private final Product basePlanProduct;
+    private final BillingPeriod basePlanBillingPeriod;
+    private final PriceList basePlanPriceList;
+    
+	public BillingStateBundle(UUID id, BillingState accountState, int numberOfUnpaidInvoices, BigDecimal unpaidInvoiceBalance,
+			DateTime dateOfEarliestUnpaidInvoice,
+			PaymentResponse responseForLastFailedPayment,
+			Tag[] tags, 
+			Product basePlanProduct,
+			BillingPeriod basePlanBillingPeriod, 
+			PriceList basePlanPriceList) {
+		super(id, numberOfUnpaidInvoices, unpaidInvoiceBalance, 
+				dateOfEarliestUnpaidInvoice, responseForLastFailedPayment, tags);
+		this.accountState = accountState;
+		this.basePlanProduct = basePlanProduct;
+		this.basePlanBillingPeriod = basePlanBillingPeriod;
+		this.basePlanPriceList = basePlanPriceList;
+	}
+	
+	public BillingState getAccountState() {
+		return accountState;
+	}
+	
+	public Product getBasePlanProduct() {
+		return basePlanProduct;
+	}
+	
+	public BillingPeriod getBasePlanBillingPeriod() {
+		return basePlanBillingPeriod;
+	}
+	
+	public PriceList getBasePlanPriceList() {
+		return basePlanPriceList;
+	}
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/overdue/OverdueState.java b/api/src/main/java/com/ning/billing/catalog/api/overdue/OverdueState.java
new file mode 100644
index 0000000..48c044a
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/overdue/OverdueState.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.api.overdue;
+
+import java.util.UUID;
+
+public class OverdueState {
+	private final UUID id;
+	private final String stageName;
+	private final String externalMessage;
+	private final boolean cancel;
+	
+	public OverdueState(UUID id, String stageName, String externalMessage, boolean cancel) {
+		this.id = id;
+		this.stageName = stageName;
+		this.externalMessage = externalMessage;
+		this.cancel = cancel;
+	}
+
+	public UUID getId() {
+		return id;
+	}
+
+	public boolean cancel() {
+		return cancel;
+	}
+	
+	public String getStageName() {
+		return stageName;
+	}
+	public String getExternalMessage() {
+		return externalMessage;
+	}
+
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/overdue/PaymentResponse.java b/api/src/main/java/com/ning/billing/catalog/api/overdue/PaymentResponse.java
new file mode 100644
index 0000000..6227e2e
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/overdue/PaymentResponse.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.api.overdue;
+
+public enum PaymentResponse {
+	INSUFFICIENT_FUNDS,
+	TEMPORARY_ACCOUNT_ISSUE,
+	LOST_OR_STOLEN,
+	PERMANENT_ACCOUNT_ISSUE,
+	OTHER
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/OverdueActions.java b/api/src/main/java/com/ning/billing/catalog/api/OverdueActions.java
new file mode 100644
index 0000000..8525d91
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/OverdueActions.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.api;
+
+public enum OverdueActions  {
+	CANCEL,
+	PAYMENT_RETRY
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
index aba447d..c597dc0 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
@@ -107,10 +107,20 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         return count;
     }
 	
-	public DefaultPriceList setRetired(boolean retired) {
+	protected DefaultPriceList setRetired(boolean retired) {
 		this.retired = retired;
 		return this;
 	}
 
+	public DefaultPriceList setName(String name) {
+		this.name = name;
+		return this;
+	}
+
+	public DefaultPriceList setPlans(DefaultPlan[] plans) {
+		this.plans = plans;
+		return this;
+	}
+
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
index ff3868c..6687c75 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
@@ -55,7 +55,6 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 	//Not included in XML
 	private String catalogName;
 	
-	
 	@Override
 	public String getCatalogName() {
 		return catalogName;
diff --git a/catalog/src/main/java/com/ning/billing/catalog/overdue/BundleCondition.java b/catalog/src/main/java/com/ning/billing/catalog/overdue/BundleCondition.java
new file mode 100644
index 0000000..fb8e985
--- /dev/null
+++ b/catalog/src/main/java/com/ning/billing/catalog/overdue/BundleCondition.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlIDREF;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.DefaultPriceList;
+import com.ning.billing.catalog.DefaultProduct;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.overdue.BillingState;
+import com.ning.billing.catalog.api.overdue.BillingStateBundle;
+
+@XmlAccessorType(XmlAccessType.NONE)
+public class BundleCondition extends Condition {
+	@XmlElement(required=false, name="accountConditions")
+	private Condition accountCondition;
+	
+	@XmlElement(required=false, name="basePlanProduct")
+    @XmlIDREF
+    private DefaultProduct basePlanProduct;
+	    
+    @XmlElement(required=false, name="basePlanBillingPeriod")
+    private BillingPeriod basePlanBillingPeriod;
+    
+    @XmlElement(required=false, name="basePlanPriceList")
+    @XmlIDREF
+    private DefaultPriceList basePlanPriceList;
+
+	public boolean evaluate(BillingStateBundle state, DateTime now) {
+		return super.evaluate((BillingState)state, now) &&
+				(accountCondition      == null || accountCondition.evaluate(state.getAccountState(), now)) &&
+				(basePlanProduct       == null || basePlanProduct.equals(state.getBasePlanProduct())) &&
+				(basePlanBillingPeriod == null || basePlanBillingPeriod.equals(state.getBasePlanBillingPeriod())) &&
+				(basePlanPriceList     == null || basePlanPriceList.equals(state.getBasePlanPriceList()));				
+	}
+
+	protected BundleCondition setAccountCondition(Condition accountCondition) {
+		this.accountCondition = accountCondition;
+		return this;
+	}
+
+	protected BundleCondition setBasePlanProduct(DefaultProduct basePlanProduct) {
+		this.basePlanProduct = basePlanProduct;
+		return this;
+	}
+
+	protected BundleCondition setBasePlanBillingPeriod(BillingPeriod basePlanBillingPeriod) {
+		this.basePlanBillingPeriod = basePlanBillingPeriod;
+		return this;
+	}
+
+	protected BundleCondition setBasePlanPriceList(DefaultPriceList basePlanPriceList) {
+		this.basePlanPriceList = basePlanPriceList;
+		return this;
+	}
+	
+	
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/overdue/Condition.java b/catalog/src/main/java/com/ning/billing/catalog/overdue/Condition.java
new file mode 100644
index 0000000..c87f548
--- /dev/null
+++ b/catalog/src/main/java/com/ning/billing/catalog/overdue/Condition.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import java.math.BigDecimal;
+import java.net.URI;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.ControlTagType;
+import com.ning.billing.catalog.DefaultDuration;
+import com.ning.billing.catalog.StandaloneCatalog;
+import com.ning.billing.catalog.api.overdue.BillingState;
+import com.ning.billing.catalog.api.overdue.PaymentResponse;
+import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationErrors;
+import com.ning.billing.util.tag.Tag;
+
+@XmlAccessorType(XmlAccessType.NONE)
+public class Condition extends ValidatingConfig<StandaloneCatalog> {
+	@XmlElement(required=false, name="numberOfUnpaidInvoicesEqualsOrExceeds")
+	private Integer numberOfUnpaidInvoicesEqualsOrExceeds;
+
+	@XmlElement(required=false, name="totalUnpaidInvoiceBalanceEqualsOrExceeds")
+	private BigDecimal totalUnpaidInvoiceBalanceEqualsOrExceeds;
+
+	@XmlElement(required=false, name="timeSinceEarliestUnpaidInvoiceEqualsOrExceeds")
+	private DefaultDuration timeSinceEarliestUnpaidInvoiceEqualsOrExceeds;
+
+	@XmlElementWrapper(required=false, name="responseForLastFailedPaymentIn")
+	@XmlElement(required=false, name="response")
+	private PaymentResponse[] responseForLastFailedPayment;
+
+	@XmlElement(required=false, name="controlTag")
+	private ControlTagType controlTag;
+	
+	public boolean evaluate(BillingState state, DateTime now) {
+		return 
+				(numberOfUnpaidInvoicesEqualsOrExceeds == null || state.getNumberOfUnpaidInvoices() >= numberOfUnpaidInvoicesEqualsOrExceeds.intValue() ) &&
+				(totalUnpaidInvoiceBalanceEqualsOrExceeds == null || totalUnpaidInvoiceBalanceEqualsOrExceeds.compareTo(state.getBalanceOfUnpaidInvoices()) <= 0) &&
+				(timeSinceEarliestUnpaidInvoiceEqualsOrExceeds == null || !timeSinceEarliestUnpaidInvoiceEqualsOrExceeds.addToDateTime(state.getDateOfEarliestUnpaidInvoice()).isAfter(now)) &&
+				(responseForLastFailedPayment == null || responseIsIn(state.getResponseForLastFailedPayment(), responseForLastFailedPayment)) &&
+				(controlTag == null || isTagIn(controlTag, state.getTags()));
+	}
+	
+	private boolean responseIsIn(PaymentResponse actualResponse,
+			PaymentResponse[] responseForLastFailedPayment) {
+		for(PaymentResponse response: responseForLastFailedPayment) {
+			if(response.equals(actualResponse)) return true;
+		}
+		return false;
+	}
+
+	private boolean isTagIn(ControlTagType tag, Tag[] tags) {
+		for(Tag t : tags) {
+			if (t.getTagDefinitionName().equals(tag.toString())) return true;
+		}
+		return false;
+	}
+
+	@Override
+	public ValidationErrors validate(StandaloneCatalog root,
+			ValidationErrors errors) {
+		return errors;
+	}
+
+	@Override
+	public void initialize(StandaloneCatalog root, URI uri) {
+	}
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/overdue/Overdue.java b/catalog/src/main/java/com/ning/billing/catalog/overdue/Overdue.java
new file mode 100644
index 0000000..6d89a19
--- /dev/null
+++ b/catalog/src/main/java/com/ning/billing/catalog/overdue/Overdue.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.overdue.BillingStateBundle;
+import com.ning.billing.catalog.api.overdue.OverdueState;
+
+@XmlAccessorType(XmlAccessType.NONE)
+public class Overdue {
+
+	@XmlElement(required=false, name="bundleOverdueStages")
+	private OverdueStage<BundleCondition>[] bundleOverdueStages;
+	
+	
+	public List<OverdueState> calculateBundleOverdueState(BillingStateBundle[] states, DateTime now){
+		List<OverdueState> result = new ArrayList<OverdueState>();
+		for(BillingStateBundle state : states) {
+			for(OverdueStage<BundleCondition> stage : bundleOverdueStages) {
+				if(stage.getCondition().evaluate(state, now)) {	
+					OverdueState ods = new OverdueState(state.getObjectId(),stage.getStageName(), 
+							stage.getExternalMessage(), stage.isCancelled());
+					result.add(ods);
+					break;
+				}
+			}
+		}
+		return result;
+	}
+
+	
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/overdue/OverdueStage.java b/catalog/src/main/java/com/ning/billing/catalog/overdue/OverdueStage.java
new file mode 100644
index 0000000..3af6b26
--- /dev/null
+++ b/catalog/src/main/java/com/ning/billing/catalog/overdue/OverdueStage.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+
+@XmlAccessorType(XmlAccessType.NONE)
+public class OverdueStage<T extends Condition> {
+	@XmlElement(required=false, name="condition")
+	private T condition;
+
+	@XmlAttribute(required=false, name="stageName")
+	private String stageName;
+
+	@XmlElement(required=false, name="externalMessage")
+	private String externalMessage;
+
+	@XmlElement(required=false, name="cancel")
+	private boolean cancel;
+
+	public String getStageName() {
+		return stageName;
+	}
+
+	public String getExternalMessage() {
+		return externalMessage;
+	}
+	public boolean isCancelled() {
+		return cancel;
+	}
+
+	public T getCondition() {
+		return condition;
+	}
+
+	public OverdueStage<T> setStageName(String stageName) {
+		this.stageName = stageName;
+		return this;
+	}
+
+	public OverdueStage<T> setExternalMessage(String externalMessage) {
+		this.externalMessage = externalMessage;
+		return this;
+	}
+
+	public OverdueStage<T> setCancel(boolean cancel) {
+		this.cancel = cancel;
+		return this;
+	}
+
+	public OverdueStage<T> setCondition(T condition) {
+		this.condition = condition;
+		return this;
+	}
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockPriceList.java b/catalog/src/test/java/com/ning/billing/catalog/MockPriceList.java
new file mode 100644
index 0000000..9b672ba
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockPriceList.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog;
+
+import com.ning.billing.catalog.api.PriceListSet;
+
+public class MockPriceList extends DefaultPriceList {
+
+	public MockPriceList() {
+		setName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		setRetired(false);
+		setPlans(MockPlan.createAll());
+	}
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/overdue/TestBundleCondition.java b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestBundleCondition.java
new file mode 100644
index 0000000..69507cd
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestBundleCondition.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.ControlTagType;
+import com.ning.billing.catalog.DefaultPriceList;
+import com.ning.billing.catalog.DefaultProduct;
+import com.ning.billing.catalog.MockPriceList;
+import com.ning.billing.catalog.MockProduct;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.overdue.BillingState;
+import com.ning.billing.catalog.api.overdue.BillingStateBundle;
+import com.ning.billing.catalog.api.overdue.PaymentResponse;
+import com.ning.billing.util.config.XMLLoader;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DescriptiveTag;
+import com.ning.billing.util.tag.Tag;
+
+public class TestBundleCondition {
+
+	@XmlRootElement(name="conditions")
+	private static class MockBundleCondition extends BundleCondition {}
+
+	@Test(groups={"fast"}, enabled=true)
+	public void testAccountState() throws Exception {
+		String xml = 
+				"<conditions>" +
+				"	<accountConditions>" +
+				"		<controlTag>OVERDUE_ENFORCEMENT_OFF</controlTag>" +
+				"	</accountConditions>" +
+				"</conditions>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		BundleCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockBundleCondition.class);
+
+		DateTime now = new DateTime();
+		
+		BillingState accountState0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),new DescriptiveTag(null, "Tag", "Martin", now)});
+		BillingState accountState1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
+		BillingState accountState2 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("200.00"), now.minusDays(20), 
+				PaymentResponse.TEMPORARY_ACCOUNT_ISSUE, 
+				new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF), 
+						  new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),
+						  new DescriptiveTag(null, "Tag", "Martin", now)});
+		
+		BillingStateBundle state0 = new BillingStateBundle(new UUID(0L,1L), accountState0, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createBicycle(), BillingPeriod.MONTHLY, new MockPriceList() );
+		BillingStateBundle state1 = new BillingStateBundle(new UUID(0L,1L), accountState1, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createBicycle(), BillingPeriod.MONTHLY, new MockPriceList() );
+		BillingStateBundle state2 = new BillingStateBundle(new UUID(0L,1L), accountState2, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createBicycle(), BillingPeriod.MONTHLY, new MockPriceList() );
+		
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+		Assert.assertTrue(c.evaluate(state2, new DateTime()));
+	}
+	
+	@Test(groups={"fast"}, enabled=true)
+	public void testProduct() throws Exception {
+		DefaultProduct prod = MockProduct.createBicycle();
+		BundleCondition c = new BundleCondition().setBasePlanProduct(prod);
+
+		DateTime now = new DateTime();
+		
+		BillingState accountState0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),new DescriptiveTag(null, "Tag", "Martin", now)});
+		BillingState accountState1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
+		
+		BillingStateBundle state0 = new BillingStateBundle(new UUID(0L,1L), accountState0, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createJet(), BillingPeriod.MONTHLY, new MockPriceList() );
+		BillingStateBundle state1 = new BillingStateBundle(new UUID(0L,1L), accountState1, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, prod, BillingPeriod.MONTHLY, new MockPriceList() );
+
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+	}
+	
+	
+	@Test(groups={"fast"}, enabled=true)
+	public void testBillingPeriod() throws Exception {
+		BundleCondition c = new BundleCondition().setBasePlanBillingPeriod(BillingPeriod.ANNUAL);
+
+		DateTime now = new DateTime();
+		
+		BillingState accountState0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),new DescriptiveTag(null, "Tag", "Martin", now)});
+		BillingState accountState1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
+		
+		BillingStateBundle state0 = new BillingStateBundle(new UUID(0L,1L), accountState0, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createJet(), BillingPeriod.MONTHLY, new MockPriceList() );
+		BillingStateBundle state1 = new BillingStateBundle(new UUID(0L,1L), accountState1, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createJet(), BillingPeriod.ANNUAL, new MockPriceList() );
+
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+	}
+
+	@Test(groups={"fast"}, enabled=true)
+	public void testPriceList() throws Exception {
+		DefaultPriceList pl = new MockPriceList().setName("test");
+		BundleCondition c = new BundleCondition().setBasePlanPriceList(pl);
+
+		DateTime now = new DateTime();
+		
+		BillingState accountState0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),new DescriptiveTag(null, "Tag", "Martin", now)});
+		BillingState accountState1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
+		
+		BillingStateBundle state0 = new BillingStateBundle(new UUID(0L,1L), accountState0, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{}, MockProduct.createJet(), BillingPeriod.MONTHLY, new MockPriceList() );
+		BillingStateBundle state1 = new BillingStateBundle(new UUID(0L,1L), accountState1, 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{},  MockProduct.createJet(), BillingPeriod.MONTHLY, pl );
+
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+	}
+
+	//MDW TODO: test Pricelist and billing period
+	
+	
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/overdue/TestCondition.java b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestCondition.java
new file mode 100644
index 0000000..3993a8b
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestCondition.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import javax.swing.DefaultDesktopManager;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.ControlTagType;
+import com.ning.billing.catalog.api.overdue.BillingState;
+import com.ning.billing.catalog.api.overdue.PaymentResponse;
+import com.ning.billing.util.config.XMLLoader;
+import com.ning.billing.util.tag.ControlTag;
+import com.ning.billing.util.tag.DefaultControlTag;
+import com.ning.billing.util.tag.DefaultTagDefinition;
+import com.ning.billing.util.tag.DescriptiveTag;
+import com.ning.billing.util.tag.Tag;
+
+public class TestCondition {
+	
+	@XmlRootElement(name="condition")
+	private static class MockCondition extends Condition {}
+
+	@Test(groups={"fast"}, enabled=true)
+	public void testNumberOfUnpaidInvoicesEqualsOrExceeds() throws Exception {
+		String xml = 
+				"<condition>" +
+				"	<numberOfUnpaidInvoicesEqualsOrExceeds>1</numberOfUnpaidInvoicesEqualsOrExceeds>" +
+				"</condition>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockCondition.class);
+		
+		BillingState state0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state1 = new BillingState(new UUID(0L,1L), 1, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state2 = new BillingState(new UUID(0L,1L), 2, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+		Assert.assertTrue(c.evaluate(state2, new DateTime()));
+	}
+	
+	@Test(groups={"fast"}, enabled=true)
+	public void testTotalUnpaidInvoiceBalanceEqualsOrExceeds() throws Exception {
+		String xml = 
+				"<condition>" +
+				"	<totalUnpaidInvoiceBalanceEqualsOrExceeds>100.00</totalUnpaidInvoiceBalanceEqualsOrExceeds>" +
+				"</condition>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockCondition.class);
+		
+		BillingState state0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state2 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("200.00"), new DateTime(), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		
+		Assert.assertTrue(!c.evaluate(state0, new DateTime()));
+		Assert.assertTrue(c.evaluate(state1, new DateTime()));
+		Assert.assertTrue(c.evaluate(state2, new DateTime()));
+	}
+
+	
+	@Test(groups={"fast"}, enabled=true)
+	public void testTimeSinceEarliestUnpaidInvoiceEqualsOrExceeds() throws Exception {
+		String xml = 
+				"<condition>" +
+				"	<timeSinceEarliestUnpaidInvoiceEqualsOrExceeds><unit>DAYS</unit><number>10</number></timeSinceEarliestUnpaidInvoiceEqualsOrExceeds>" +
+				"</condition>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockCondition.class);
+		
+		DateTime now = new DateTime();
+		
+		BillingState state0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state2 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("200.00"), now.minusDays(20), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		
+		Assert.assertTrue(!c.evaluate(state0, now));
+		Assert.assertTrue(c.evaluate(state1, now));
+		Assert.assertTrue(c.evaluate(state2, now));
+	}
+
+	@Test(groups={"fast"}, enabled=true)
+	public void testResponseForLastFailedPaymentIn() throws Exception {
+		String xml = 
+				"<condition>" +
+				"	<responseForLastFailedPaymentIn><response>INSUFFICIENT_FUNDS</response><response>TEMPORARY_ACCOUNT_ISSUE</response></responseForLastFailedPaymentIn>" +
+				"</condition>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockCondition.class);
+		
+		DateTime now = new DateTime();
+		
+		BillingState state0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{});
+		BillingState state1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{});
+		BillingState state2 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("200.00"), now.minusDays(20), PaymentResponse.TEMPORARY_ACCOUNT_ISSUE, new Tag[]{});
+		
+		Assert.assertTrue(!c.evaluate(state0, now));
+		Assert.assertTrue(c.evaluate(state1, now));
+		Assert.assertTrue(c.evaluate(state2, now));
+	}
+
+	@Test(groups={"fast"}, enabled=true)
+	public void testHasControlTag() throws Exception {
+		String xml = 
+				"<condition>" +
+				"	<controlTag>OVERDUE_ENFORCEMENT_OFF</controlTag>" +
+				"</condition>";
+		InputStream is = new ByteArrayInputStream(xml.getBytes());
+		MockCondition c = XMLLoader.getObjectFromStreamNoValidation(is,  MockCondition.class);
+		
+		DateTime now = new DateTime();
+		
+		BillingState state0 = new BillingState(new UUID(0L,1L), 0, BigDecimal.ZERO, now, PaymentResponse.LOST_OR_STOLEN, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),new DescriptiveTag(null, "Tag", "Martin", now)});
+		BillingState state1 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("100.00"), now.minusDays(10), PaymentResponse.INSUFFICIENT_FUNDS, new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF)});
+		BillingState state2 = new BillingState(new UUID(0L,1L), 1, new BigDecimal("200.00"), now.minusDays(20), 
+				PaymentResponse.TEMPORARY_ACCOUNT_ISSUE, 
+				new Tag[]{new DefaultControlTag("Martin", now, ControlTagType.OVERDUE_ENFORCEMENT_OFF), 
+						  new DefaultControlTag("Martin", now, ControlTagType.AUTO_BILLING_OFF),
+						  new DescriptiveTag(null, "Tag", "Martin", now)});
+		
+		Assert.assertTrue(!c.evaluate(state0, now));
+		Assert.assertTrue(c.evaluate(state1, now));
+		Assert.assertTrue(c.evaluate(state2, now));
+	}
+
+
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/overdue/TestOverdue.java b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestOverdue.java
new file mode 100644
index 0000000..16f2118
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/overdue/TestOverdue.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.catalog.overdue;
+
+public class TestOverdue {
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/config/XMLLoader.java b/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
index d0487b8..9d35352 100644
--- a/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
+++ b/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
@@ -71,6 +71,18 @@ public class XMLLoader {
             return null;
         }
     } 
+	
+	public static <T> T getObjectFromStreamNoValidation(InputStream stream, Class<T> clazz) throws SAXException, InvalidConfigException, JAXBException, IOException, TransformerException {
+        Object o = unmarshaller(clazz).unmarshal(stream);
+        if (clazz.isInstance(o)) {
+        	@SuppressWarnings("unchecked")
+			T castObject = (T)o;
+        	return castObject;
+        } else {
+            return null;
+        }
+    } 
+
 
 	public static <T extends ValidatingConfig<T>> void validate(URI uri, T c) throws ValidationException {
             c.initialize(c, uri);