killbill-memoizeit

Merge branch 'integration' of github.com:ning/killbill

2/22/2012 2:34:54 PM

Changes

Details

diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 363e483..977d6f7 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -39,4 +39,6 @@ public interface InvoiceUserApi {
     public void notifyOfPaymentAttempt(InvoicePayment invoicePayment);
 
     public Collection<Invoice> getUnpaidInvoicesByAccountId(UUID accountId, DateTime upToDate);
+    
+    public Invoice triggerInvoiceGeneration(UUID accountId, DateTime targetDate, boolean dryrun) throws InvoiceApiException;
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
index 6d00162..5523e12 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
@@ -77,7 +77,7 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
 	 * @see com.ning.billing.catalog.IPlanPhase#getRecurringPrice()
 	 */
     @Override
-	public InternationalPrice getRecurringPrice() {
+	public DefaultInternationalPrice getRecurringPrice() {
         return recurringPrice;
     }
 
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
index 5fc51b8..092a863 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
@@ -32,9 +32,9 @@ public class MockCatalog extends StandaloneCatalog {
 	
 	public MockCatalog() {
 		setEffectiveDate(new Date());
-		populateProducts();
+		setProducts(MockProduct.createAll());
+		setPlans(MockPlan.createAll());
 		populateRules();
-		populatePlans();
 		populatePriceLists();
 	}
 	
@@ -51,25 +51,6 @@ public class MockCatalog extends StandaloneCatalog {
 		
 	}
 
-	public void populateProducts() {
-		String[] names = getProductNames();
-		DefaultProduct[] products = new DefaultProduct[names.length];
-		for(int i = 0; i < names.length; i++) {
-			products[i] = new DefaultProduct(names[i], ProductCategory.BASE);
-		}
-		setProducts(products);
-	}
-	
-	public void populatePlans() {
-		DefaultProduct[] products = getCurrentProducts();
-		DefaultPlan[] plans = new DefaultPlan[products.length];
-		for(int i = 0; i < products.length; i++) {
-			DefaultPlanPhase phase = new DefaultPlanPhase().setPhaseType(PhaseType.EVERGREEN).setBillingPeriod(BillingPeriod.MONTHLY).setRecurringPrice(new DefaultInternationalPrice());
-			plans[i] = new MockPlan().setName(products[i].getName().toLowerCase() + "-plan").setProduct(products[i]).setFinalPhase(phase);
-		}
-		setPlans(plans);
-	}
-
 	public void populatePriceLists() {
 		DefaultPlan[] plans = getCurrentPlans();
 		
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
index cc3a679..3df36aa 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
@@ -16,16 +16,22 @@
 
 package com.ning.billing.catalog;
 
-import com.ning.billing.catalog.api.Currency;
-
 import java.math.BigDecimal;
 
+import com.ning.billing.catalog.api.Currency;
+
 public class MockInternationalPrice extends DefaultInternationalPrice {
+	
+	public static MockInternationalPrice create0USD() {
+		return new MockInternationalPrice(new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.ZERO));
+	}
+	
+	public static MockInternationalPrice create1USD() {
+		return new MockInternationalPrice(new DefaultPrice().setCurrency(Currency.USD).setValue(BigDecimal.ONE));
+	}
 
-	public MockInternationalPrice() {
-		setPrices(new DefaultPrice[] {
-			new DefaultPrice().setCurrency(Currency.USD).setValue(new BigDecimal(1))
-		});
+	public static MockInternationalPrice createUSD(String value) {
+		return new MockInternationalPrice(new DefaultPrice().setCurrency(Currency.USD).setValue(new BigDecimal(value)));
 	}
 
 	public MockInternationalPrice(DefaultPrice... price) {
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java b/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java
index 53c73fe..58483c4 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java
@@ -16,7 +16,65 @@
 
 package com.ning.billing.catalog;
 
+
 public class MockPlan extends DefaultPlan {
+	
+	public static MockPlan createBicycleTrialEvergreen1USD(int trialDurationInDays) {
+		return new MockPlan("BicycleTrialEvergreen1USD",
+				MockProduct.createBicycle(),
+				new DefaultPlanPhase[]{ MockPlanPhase.createTrial(trialDurationInDays) },
+				MockPlanPhase.create1USDMonthlyEvergreen(),
+				-1);
+	}
+
+	
+	public static MockPlan createBicycleTrialEvergreen1USD() {
+		return new MockPlan("BicycleTrialEvergreen1USD",
+				MockProduct.createBicycle(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial() },
+				MockPlanPhase.create1USDMonthlyEvergreen(),
+				-1);
+	}
+
+	public static MockPlan createSportsCarTrialEvergreen100USD() {
+		return new MockPlan("SportsCarTrialEvergreen100USD",
+				MockProduct.createSportsCar(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial() },
+				MockPlanPhase.createUSDMonthlyEvergreen("100.00",null),
+				-1);
+	}
+	
+	public static MockPlan createPickupTrialEvergreen10USD() {
+		return new MockPlan("PickupTrialEvergreen10USD",
+				MockProduct.createPickup(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial() },
+				MockPlanPhase.createUSDMonthlyEvergreen("10.00",null),
+				-1);
+	}
+
+	public static MockPlan createJetTrialEvergreen1000USD() {
+		return new MockPlan("JetTrialEvergreen1000USD",
+				MockProduct.createJet(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial() },
+				MockPlanPhase.create1USDMonthlyEvergreen(),
+				-1);
+	}
+
+	public static MockPlan createJetTrialFixedTermEvergreen1000USD() {
+		return new MockPlan("JetTrialEvergreen1000USD",
+				MockProduct.createJet(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial(), MockPlanPhase.createUSDMonthlyFixedTerm("500.00", null, 6) },
+				MockPlanPhase.create1USDMonthlyEvergreen(),
+				-1);
+	}
+
+	public MockPlan() {
+		this("BicycleTrialEvergreen1USD",
+				MockProduct.createBicycle(),
+				new DefaultPlanPhase[]{ MockPlanPhase.create30DayTrial()},
+				MockPlanPhase.create1USDMonthlyEvergreen(),
+				-1);
+	}
 
 	public MockPlan(String name, DefaultProduct product, DefaultPlanPhase[] planPhases, DefaultPlanPhase finalPhase, int plansAllowedInBundle) {
 		setName(name);
@@ -24,31 +82,51 @@ public class MockPlan extends DefaultPlan {
 		setFinalPhase(finalPhase);
 		setInitialPhases(planPhases);
 		setPlansAllowedInBundle(plansAllowedInBundle);
-	}
-	
-	public MockPlan() {
-		setName("test-plan");
-		setProduct(new MockProduct());
-		setFinalPhase(new MockPlanPhase(this));
-		setInitialPhases(null);
-		setPlansAllowedInBundle(1);
+		
+		finalPhase.setPlan(this);
+		for (DefaultPlanPhase pp : planPhases) {
+			pp.setPlan(this);
+		}
 	}
 
-    public MockPlan(String planName) {
-		setName(planName);
-		setProduct(new MockProduct());
-		setFinalPhase(new MockPlanPhase(this));
-		setInitialPhases(null);
-		setPlansAllowedInBundle(1);
-	}
 
+
+	public static MockPlan createBicycleNoTrialEvergreen1USD() {
+		return new MockPlan("BicycleNoTrialEvergreen1USD",
+				MockProduct.createBicycle(),
+				new DefaultPlanPhase[]{ },
+				MockPlanPhase.createUSDMonthlyEvergreen("1.0", null)	,
+				-1);
+	}
+	
 	public MockPlan(MockPlanPhase mockPlanPhase) {
-		setName("test-plan");
-		setProduct(new MockProduct());
+		setName("Test");
+		setProduct(MockProduct.createBicycle());
 		setFinalPhase(mockPlanPhase);
-		setInitialPhases(null);
-		setPlansAllowedInBundle(1);
+		
+		mockPlanPhase.setPlan(this);
 	}
 
+	  public MockPlan(String planName) {
+			setName(planName);
+			setProduct(new MockProduct());
+			setFinalPhase(new MockPlanPhase(this));
+			setInitialPhases(null);
+			setPlansAllowedInBundle(1);
+	  }
+
+
+	public static DefaultPlan[] createAll() {
+		return new MockPlan[]{
+				createBicycleTrialEvergreen1USD(),
+				createBicycleNoTrialEvergreen1USD(),
+				createPickupTrialEvergreen10USD(),
+				createSportsCarTrialEvergreen100USD(),
+				createJetTrialEvergreen1000USD(),
+				createJetTrialFixedTermEvergreen1000USD()
+		};
+	}
+	
+	
 
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java b/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java
index d4ae5ff..b995bef 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java
@@ -16,14 +16,51 @@
 
 package com.ning.billing.catalog;
 
+import javax.annotation.Nullable;
+
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.TimeUnit;
 
-import javax.annotation.Nullable;
-
 public class MockPlanPhase extends DefaultPlanPhase {
+	
+	public static MockPlanPhase create1USDMonthlyEvergreen() {
+		return (MockPlanPhase) new MockPlanPhase(BillingPeriod.MONTHLY,
+				PhaseType.EVERGREEN,
+				new DefaultDuration().setUnit(TimeUnit.UNLIMITED),
+				MockInternationalPrice.create1USD(),
+				null).setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());
+	}
+	
+	public static MockPlanPhase createUSDMonthlyEvergreen(String reccuringUSDPrice, String fixedPrice) {
+		return new MockPlanPhase(BillingPeriod.MONTHLY,
+				PhaseType.EVERGREEN,
+				new DefaultDuration().setUnit(TimeUnit.UNLIMITED),
+				(reccuringUSDPrice == null) ? null : MockInternationalPrice.createUSD(reccuringUSDPrice),
+				(fixedPrice == null) ? null :MockInternationalPrice.createUSD(fixedPrice));
+	}
+
+	public static MockPlanPhase createUSDMonthlyFixedTerm(String reccuringUSDPrice, String fixedPrice, int durationInMonths) {
+		return new MockPlanPhase(BillingPeriod.MONTHLY,
+				PhaseType.FIXEDTERM,
+				new DefaultDuration().setUnit(TimeUnit.MONTHS).setNumber(durationInMonths),
+				(reccuringUSDPrice == null) ? null : MockInternationalPrice.createUSD(reccuringUSDPrice),
+				(fixedPrice == null) ? null :MockInternationalPrice.createUSD(fixedPrice));
+	}
+
+	public static MockPlanPhase create30DayTrial() {
+		return createTrial(30);
+	}
+
+	public static MockPlanPhase createTrial(int days) {
+		return new MockPlanPhase(BillingPeriod.NO_BILLING_PERIOD,
+				PhaseType.TRIAL,
+				new DefaultDuration().setUnit(TimeUnit.DAYS).setNumber(days),
+				null,
+				MockInternationalPrice.create1USD()
+				);
+	}
 
     public MockPlanPhase(
     		BillingPeriod billingPeriod, 
@@ -38,6 +75,7 @@ public class MockPlanPhase extends DefaultPlanPhase {
 		setFixedPrice(fixedPrice);
 	}
     
+  
     public MockPlanPhase() {
         this(new MockInternationalPrice(), null);
 	}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java b/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java
index 7587dba..52ffd91 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java
@@ -23,6 +23,54 @@ public class MockProduct extends DefaultProduct {
 	public MockProduct() {
 		setName("TestProduct");
 		setCatagory(ProductCategory.BASE);
-		setCatalogName("Ning");
+		setCatalogName("Vehcles");
 	}
+	
+	public MockProduct(String name, ProductCategory category, String catalogName) {
+		setName(name);
+		setCatagory(category);
+		setCatalogName(catalogName);
+	}
+	
+	public static MockProduct createBicycle() {
+		return new MockProduct("Bicycle", ProductCategory.BASE, "Vehcles");
+	}
+	
+	public static MockProduct createPickup() {
+		return new MockProduct("Pickup", ProductCategory.BASE, "Vehcles");
+	}
+	
+	public static MockProduct createSportsCar() {
+		return new MockProduct("SportsCar", ProductCategory.BASE, "Vehcles");
+	}
+	
+	public static MockProduct createJet() {
+		return new MockProduct("Jet", ProductCategory.BASE, "Vehcles");
+	}
+	
+	public static MockProduct createHorn() {
+		return new MockProduct("Horn", ProductCategory.ADD_ON, "Vehcles");
+	}
+	
+	public static MockProduct createSpotlight() {
+		return new MockProduct("spotlight", ProductCategory.ADD_ON, "Vehcles");
+	}
+	
+	public static MockProduct createRedPaintJob() {
+		return new MockProduct("RedPaintJob", ProductCategory.ADD_ON, "Vehcles");
+	}
+
+	public static DefaultProduct[] createAll() {
+		return new MockProduct[]{
+				createBicycle(),
+				createPickup(),
+				createSportsCar(),
+				createJet(),
+				createHorn(),
+				createRedPaintJob()
+		};
+	}
+	
+	
+	
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java b/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
index 3e6a755..c14a157 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
@@ -66,6 +66,7 @@ public class TestInternationalPrice {
   public void testPriceInitialization() throws URISyntaxException, CatalogApiException  {
 	  StandaloneCatalog c = new MockCatalog();
 	  c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
+	  c.getCurrentPlans()[0].getFinalPhase().getRecurringPrice().setPrices(null);
 	  c.initialize(c, new URI("foo://bar"));
 	  Assert.assertEquals(c.getCurrentPlans()[0].getFinalPhase().getRecurringPrice().getPrice(Currency.GBP), new BigDecimal(0));
   }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
index e58f71c..a8714c1 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
@@ -37,7 +37,7 @@ public class TestPlan {
 
 		StandaloneCatalog c = new MockCatalog();
 		c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
-		DefaultPlan p1 =  new MockPlan();
+		DefaultPlan p1 =  MockPlan.createBicycleTrialEvergreen1USD();
 		p1.setEffectiveDateForExistingSubscriptons(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
 		ValidationErrors errors = p1.validate(c, new ValidationErrors());
 		Assert.assertEquals(errors.size(), 1);
@@ -45,79 +45,18 @@ public class TestPlan {
 
 	}
 	
-	private static class MyDuration extends DefaultDuration {
-		final int days;
-		
-		public MyDuration(int days) {
-			this.days = days;
-		}
-		
-		@Override
-		public DateTime addToDateTime(DateTime dateTime) {
-			return dateTime.plusDays(days);
-		}
-	}
-	
-	private static class MyPlanPhase extends MockPlanPhase {
-		Duration duration;
-		boolean recurringPriceIsZero;
-		
-		MyPlanPhase(int duration, boolean recurringPriceIsZero) {
-			this.duration= new MyDuration( duration );
-			this.recurringPriceIsZero = recurringPriceIsZero;
-		}
-		@Override
-		public Duration getDuration(){
-			return duration;
-		}
-		
-		@Override
-		public InternationalPrice getRecurringPrice() {
-			return new MockInternationalPrice() {
-				@Override
-				public boolean isZero() {
-					return recurringPriceIsZero;
-				}
-			};
-		}
-	}
-	
 	@Test(groups={"fast"}, enabled = true)
 	public void testDataCalc() {
-		DefaultPlan p0 =  new MockPlan() {
-			public PlanPhase[] getAllPhases() {
-				return new PlanPhase[]{
-						new MyPlanPhase(10, true),
-						new MyPlanPhase(10, false),
-				};
-			}
-		};
+		DefaultPlan p0 = MockPlan.createBicycleTrialEvergreen1USD();  
+					
+		DefaultPlan p1 =  MockPlan.createBicycleTrialEvergreen1USD(100);
 		
-		DefaultPlan p1 =  new MockPlan() {
-			public PlanPhase[] getAllPhases() {
-				return new PlanPhase[]{
-						new MyPlanPhase(10, true),
-						new MyPlanPhase(10, true),
-						new MyPlanPhase(10, true),
-						new MyPlanPhase(10, true),
-						new MyPlanPhase(10, false),
-						new MyPlanPhase(10, true),
-				};
-			}
-		};
+		DefaultPlan p2 =  MockPlan.createBicycleNoTrialEvergreen1USD(); 
 		
-		DefaultPlan p2 =  new MockPlan() {
-			public PlanPhase[] getAllPhases() {
-				return new PlanPhase[]{
-						new MyPlanPhase(10, false),
-						new MyPlanPhase(10, true),
-				};
-			}
-		};
 		DateTime requestedDate = new DateTime();
-		Assert.assertEquals(p0.dateOfFirstRecurringNonZeroCharge(requestedDate), requestedDate.plusDays(10));
-		Assert.assertEquals(p1.dateOfFirstRecurringNonZeroCharge(requestedDate), requestedDate.plusDays(40));
-		Assert.assertEquals(p2.dateOfFirstRecurringNonZeroCharge(requestedDate), requestedDate.plusDays(0));
+		Assert.assertEquals(p0.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(30)), 0);
+		Assert.assertEquals(p1.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(100)), 0);
+		Assert.assertEquals(p2.dateOfFirstRecurringNonZeroCharge(requestedDate).compareTo(requestedDate.plusDays(0)), 0);
 
 	}
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
index 9b07bfe..a3b825d 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
@@ -32,17 +32,18 @@ public class TestPlanPhase {
 	public void testValidation() {
 		log.info("Testing Plan Phase Validation");
 		
-		DefaultPlanPhase pp = new MockPlanPhase().setBillCycleDuration(BillingPeriod.MONTHLY).setRecurringPrice(null).setFixedPrice(new DefaultInternationalPrice());
+		DefaultPlanPhase pp = MockPlanPhase.createUSDMonthlyEvergreen(null, "1.00").setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());//new MockPlanPhase().setBillCycleDuration(BillingPeriod.MONTHLY).setRecurringPrice(null).setFixedPrice(new DefaultInternationalPrice());
+		
 		ValidationErrors errors = pp.validate(new MockCatalog(), new ValidationErrors());
 		errors.log(log);
 		Assert.assertEquals(errors.size(), 1);
 
-		pp = new MockPlanPhase().setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD).setRecurringPrice(new MockInternationalPrice());
+		pp = MockPlanPhase.createUSDMonthlyEvergreen("1.00", null).setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD).setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());// new MockPlanPhase().setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD).setRecurringPrice(new MockInternationalPrice());
 		errors = pp.validate(new MockCatalog(), new ValidationErrors());
 		errors.log(log);
 		Assert.assertEquals(errors.size(), 1);
 
-		pp = new MockPlanPhase().setRecurringPrice(null).setFixedPrice(null).setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD);
+		pp = MockPlanPhase.createUSDMonthlyEvergreen(null, null).setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD).setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());//new MockPlanPhase().setRecurringPrice(null).setFixedPrice(null).setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD);
 		errors = pp.validate(new MockCatalog(), new ValidationErrors());
 		errors.log(log);
 		Assert.assertEquals(errors.size(), 1);
@@ -53,11 +54,11 @@ public class TestPlanPhase {
 		String planName = "Foo";
 		String planNameExt = planName + "-";
 		
-		DefaultPlan p = new MockPlan().setName(planName);
-		DefaultPlanPhase ppDiscount = new MockPlanPhase().setPhaseType(PhaseType.DISCOUNT).setPlan(p);
-		DefaultPlanPhase ppTrial = new MockPlanPhase().setPhaseType(PhaseType.TRIAL).setPlan(p);
-		DefaultPlanPhase ppEvergreen = new MockPlanPhase().setPhaseType(PhaseType.EVERGREEN).setPlan(p);
-		DefaultPlanPhase ppFixedterm = new MockPlanPhase().setPhaseType(PhaseType.FIXEDTERM).setPlan(p);
+		DefaultPlan p = MockPlan.createBicycleNoTrialEvergreen1USD().setName(planName);
+		DefaultPlanPhase ppDiscount = MockPlanPhase.create1USDMonthlyEvergreen().setPhaseType(PhaseType.DISCOUNT).setPlan(p);
+		DefaultPlanPhase ppTrial = MockPlanPhase.create30DayTrial().setPhaseType(PhaseType.TRIAL).setPlan(p);
+		DefaultPlanPhase ppEvergreen = MockPlanPhase.create1USDMonthlyEvergreen().setPhaseType(PhaseType.EVERGREEN).setPlan(p);
+		DefaultPlanPhase ppFixedterm = MockPlanPhase.create1USDMonthlyEvergreen().setPhaseType(PhaseType.FIXEDTERM).setPlan(p);
 		
 		String ppnDiscount = DefaultPlanPhase.phaseName(p, ppDiscount);
 		String ppnTrial = DefaultPlanPhase.phaseName(p, ppTrial);
diff --git a/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
new file mode 100644
index 0000000..0657fd4
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/mock/catalog/MockPlan.java
@@ -0,0 +1,102 @@
+/*
+ * 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.mock.catalog;
+
+import java.util.Date;
+import java.util.Iterator;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.Product;
+
+public class MockPlan implements Plan {
+
+	@Override
+	public PlanPhase[] getInitialPhases() {
+		return null;
+	}
+
+	@Override
+	public Product getProduct() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return null;
+	}
+
+	@Override
+	public boolean isRetired() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	@Override
+	public Iterator<PlanPhase> getInitialPhaseIterator() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public PlanPhase getFinalPhase() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public BillingPeriod getBillingPeriod() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public int getPlansAllowedInBundle() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	@Override
+	public PlanPhase[] getAllPhases() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public Date getEffectiveDateForExistingSubscriptons() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public PlanPhase findPhase(String name) throws CatalogApiException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public DateTime dateOfFirstRecurringNonZeroCharge(
+			DateTime subscriptionStartDate) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index ee9ec81..788cda1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -16,10 +16,24 @@ w * Copyright 2010-2011 Ning, Inc.
 
 package com.ning.billing.entitlement.api.billing;
 
+import java.util.Date;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.catalog.api.BillingAlignment;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogApiException;
@@ -32,34 +46,23 @@ import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
-import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.SubscriptionSqlDao;
-import org.joda.time.DateTime;
-import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
 
 
 public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 	private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
 
-    private final EntitlementDao dao;
+    private final EntitlementDao entitlementDao;
     private final AccountUserApi accountApi;
     private final CatalogService catalogService;
 
     @Inject
     public DefaultEntitlementBillingApi(final EntitlementDao dao, final AccountUserApi accountApi, final CatalogService catalogService) {
         super();
-        this.dao = dao;
+        this.entitlementDao = dao;
         this.accountApi = accountApi;
         this.catalogService = catalogService;
     }
@@ -68,23 +71,22 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
     public SortedSet<BillingEvent> getBillingEventsForAccount(
             final UUID accountId) {
 
-        List<SubscriptionBundle> bundles = dao.getSubscriptionBundleForAccount(accountId);
-        List<Subscription> subscriptions = new ArrayList<Subscription>();
-        for (final SubscriptionBundle bundle: bundles) {
-            subscriptions.addAll(dao.getSubscriptions(bundle.getId()));
-        }
-
+        List<SubscriptionBundle> bundles = entitlementDao.getSubscriptionBundleForAccount(accountId);
         SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
-        for (final Subscription subscription: subscriptions) {
-        	for (final SubscriptionTransition transition : subscription.getAllTransitions()) {
-        		try {
-                    BillingEvent event = new DefaultBillingEvent(transition, subscription, calculateBCD(transition, accountId));
-        			result.add(event);
-        		} catch (CatalogApiException e) {
-        			log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
-        					transition.getId().toString(), e);
-                } catch (Exception e) {
-                    log.warn("Failed while getting BillingEvent", e);
+        for (final SubscriptionBundle bundle: bundles) {
+        	List<Subscription> subscriptions = entitlementDao.getSubscriptions(bundle.getId());
+
+        	for (final Subscription subscription: subscriptions) {
+        		for (final SubscriptionTransition transition : subscription.getAllTransitions()) {
+        			try {
+        				BillingEvent event = new DefaultBillingEvent(transition, subscription, calculateBcd(bundle, subscription, transition, accountId));
+        				result.add(event);
+        			} catch (CatalogApiException e) {
+        				log.error("Failing to identify catalog components while creating BillingEvent from transition: " +
+        						transition.getId().toString(), e);
+        			} catch (Exception e) {
+        				log.warn("Failed while getting BillingEvent", e);
+        			}
         		}
         	}
         }
@@ -93,10 +95,10 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
 
     @Override
     public UUID getAccountIdFromSubscriptionId(final UUID subscriptionId) {
-        return dao.getAccountIdFromSubscriptionId(subscriptionId);
+        return entitlementDao.getAccountIdFromSubscriptionId(subscriptionId);
     }
 
-    private int calculateBCD(final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException {
+    private int calculateBcd(SubscriptionBundle bundle, Subscription subscription, final SubscriptionTransition transition, final UUID accountId) throws CatalogApiException, AccountApiException {
     	Catalog catalog = catalogService.getFullCatalog();
     	Plan plan =  (transition.getTransitionType() != SubscriptionTransitionType.CANCEL) ?
     	        transition.getNextPlan() : transition.getPreviousPlan();
@@ -111,41 +113,89 @@ public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
     					transition.getNextPriceList(),
     					phase.getPhaseType()),
     					transition.getRequestedTransitionTime());
-    	int result = 0;
+    	int result = -1;
 
-        Account account = accountApi.getAccountById(accountId);
-
-    	switch (alignment) {
+		Account account = accountApi.getAccountById(accountId);
+		switch (alignment) {
     		case ACCOUNT :
     			result = account.getBillCycleDay();
+    			
+    			if(result == 0) {
+    				result = calculateBcdFromSubscription(subscription, plan, account);
+    			}
     		break;
     		case BUNDLE :
-    			SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(transition.getBundleId());
-    			//TODO result = bundle.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
-    			result = bundle.getStartDate().getDayOfMonth();
+    			result = bundle.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
     		break;
     		case SUBSCRIPTION :
-    			Subscription subscription = dao.getSubscriptionFromId(transition.getSubscriptionId());
-    			//TODO result = subscription.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
-    			result = subscription.getStartDate().getDayOfMonth();
+    			result = subscription.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
     		break;
     	}
-    	if(result == 0) {
+    	if(result == -1) {
     		throw new CatalogApiException(ErrorCode.CAT_INVALID_BILLING_ALIGNMENT, alignment.toString());
     	}
     	return result;
 
     }
+    
+   	private int calculateBcdFromSubscription(Subscription subscription, Plan plan, Account account) throws AccountApiException {
+		int result = account.getBillCycleDay();
+        if(result != 0) {
+            return result;
+        }
+        result = new DateTime(account.getTimeZone()).getDayOfMonth();
 
+        try {
+        	result = billCycleDay(subscription.getStartDate(),account.getTimeZone(), plan);
+        } catch (CatalogApiException e) {
+            log.error("Unexpected catalog error encountered when updating BCD",e);
+        }
+        
+
+        Account modifiedAccount = new DefaultAccount(
+                account.getId(),
+                account.getExternalKey(),
+                account.getEmail(),
+                account.getName(),
+                account.getFirstNameLength(),
+                account.getCurrency(),
+                result,
+                account.getPaymentProviderName(),
+                account.getTimeZone(),
+                account.getLocale(),
+                account.getAddress1(),
+                account.getAddress2(),
+                account.getCompanyName(),
+                account.getCity(),
+                account.getStateOrProvince(),
+                account.getCountry(),
+                account.getPostalCode(),
+                account.getPhone(),
+                account.getCreatedDate(),
+                null // Updated date will be set internally
+        );
+        accountApi.updateAccount(modifiedAccount);
+        return result;
+    }
+
+    private int billCycleDay(DateTime requestedDate, DateTimeZone timeZone, 
+    		Plan plan) throws CatalogApiException {
+
+        DateTime date = plan.dateOfFirstRecurringNonZeroCharge(requestedDate);
+        return date.toDateTime(timeZone).getDayOfMonth();
+
+    }
+    
+    
     @Override
     public void setChargedThroughDate(final UUID subscriptionId, final DateTime ctd) {
-        SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(subscriptionId);
+        SubscriptionData subscription = (SubscriptionData) entitlementDao.getSubscriptionFromId(subscriptionId);
 
         SubscriptionBuilder builder = new SubscriptionBuilder(subscription)
             .setChargedThroughDate(ctd)
             .setPaidThroughDate(subscription.getPaidThroughDate());
 
-        dao.updateSubscription(new SubscriptionData(builder));
+        entitlementDao.updateSubscription(new SubscriptionData(builder));
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index c9ddf90..5a04a47 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -16,17 +16,17 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
-import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
-import java.util.Collection;
-import java.util.List;
-import java.util.UUID;
 
 public interface EntitlementDao {
 
@@ -76,4 +76,5 @@ public interface EntitlementDao {
     public void migrate(UUID acountId, AccountMigrationData data);
 
     public void undoMigration(UUID accountId);
-}
+
+	}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index e5f15ed..a1a3dc5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -21,18 +21,18 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
+
 import org.joda.time.DateTime;
 import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
-import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.entitlement.api.billing.EntitlementBillingApiException;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -53,7 +53,6 @@ import com.ning.billing.util.notificationq.NotificationKey;
 import com.ning.billing.util.notificationq.NotificationQueue;
 import com.ning.billing.util.notificationq.NotificationQueueService;
 import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
-import sun.jkernel.Bundle;
 
 
 public class EntitlementSqlDao implements EntitlementDao {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
index d896ebe..7b2ddcc 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/SubscriptionSqlDao.java
@@ -46,7 +46,8 @@ import java.util.UUID;
 @ExternalizedSqlViaStringTemplate3()
 public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, CloseMe, Transmogrifier {
 
-    @SqlUpdate
+
+	@SqlUpdate
     public void insertSubscription(@Bind(binder = ISubscriptionDaoBinder.class) SubscriptionData sub);
 
     @SqlUpdate
@@ -62,7 +63,7 @@ public interface SubscriptionSqlDao extends Transactional<SubscriptionSqlDao>, C
 
     @SqlUpdate
     public void updateSubscription(@Bind("id") String id, @Bind("active_version") long activeVersion, @Bind("ctd_dt") Date ctd, @Bind("ptd_dt") Date ptd);
-
+   
     public static class ISubscriptionDaoBinder implements Binder<Bind, SubscriptionData> {
 
         private Date getDate(DateTime dateTime) {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
index 91ddc91..2d99f8d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -17,12 +17,15 @@
 package com.ning.billing.entitlement.api.billing;
 
 
+import static org.testng.Assert.assertTrue;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
@@ -42,7 +45,6 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.entitlement.api.TestApiBase;
-import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
 import com.ning.billing.entitlement.api.user.SubscriptionBundle;
@@ -55,11 +57,11 @@ import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.glue.ClockModule;
 
-import static org.testng.Assert.assertTrue;
-
 public class TestDefaultEntitlementBillingApi {
 	private static final UUID zeroId = new UUID(0L,0L);
 	private static final UUID oneId = new UUID(1L,0L);
@@ -90,7 +92,7 @@ public class TestDefaultEntitlementBillingApi {
 	@BeforeMethod(alwaysRun=true)
 	public void setupEveryTime() {
 		bundles = new ArrayList<SubscriptionBundle>();
-		final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId,  new DateTime().minusDays(4));
+		final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId,  clock.getUTCNow().minusDays(4));
 		bundles.add(bundle);
 		
 		
@@ -98,7 +100,7 @@ public class TestDefaultEntitlementBillingApi {
 		subscriptions = new ArrayList<Subscription>();
 		
 		SubscriptionBuilder builder = new SubscriptionBuilder();
-		subscriptionStartDate = new DateTime().minusDays(3);
+		subscriptionStartDate = clock.getUTCNow().minusDays(3);
 		builder.setStartDate(subscriptionStartDate).setId(oneId);
 		subscription = new SubscriptionData(builder) {
 		    public List<SubscriptionTransition> getAllTransitions() {
@@ -194,15 +196,12 @@ public class TestDefaultEntitlementBillingApi {
 				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
 		transitions.add(t);
 		
-		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
-
-			@Override
-			public Account getAccountById(UUID accountId) {
-				return new BrainDeadAccount(){@Override
-				public int getBillCycleDay() {
-					return 1;
-				}};
-			}} ;
+		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class); 
+		((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
+		
+		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);			
+		((ZombieControl)accountApi).addResult("getAccountById", account);
+				
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
@@ -244,15 +243,12 @@ public class TestDefaultEntitlementBillingApi {
 				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
 		transitions.add(t);
 		
-		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
-
-			@Override
-			public Account getAccountById(UUID accountId) {
-				return new BrainDeadAccount(){@Override
-				public int getBillCycleDay() {
-					return 1;
-				}};
-			}} ;
+		Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class); 
+		((ZombieControl)account).addResult("getBillCycleDay", 1).addResult("getTimeZone", DateTimeZone.UTC);
+		
+		AccountUserApi accountApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);			
+		((ZombieControl)accountApi).addResult("getAccountById", account);
+				
 		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
 		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
 		checkFirstEvent(events, nextPlan, bundles.get(0).getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index da7f565..d38773a 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -20,22 +20,25 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 
-import com.ning.billing.invoice.InvoiceListener;
-import com.ning.billing.invoice.api.InvoicePayment;
 import org.joda.time.DateTime;
+
 import com.google.inject.Inject;
+import com.ning.billing.invoice.InvoiceDispatcher;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoicePayment;
 import com.ning.billing.invoice.api.InvoiceUserApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
 
 public class DefaultInvoiceUserApi implements InvoiceUserApi {
     private final InvoiceDao dao;
+    private final InvoiceDispatcher dispatcher;
 
     @Inject
-    public DefaultInvoiceUserApi(final InvoiceDao dao) {
+    public DefaultInvoiceUserApi(final InvoiceDao dao, final InvoiceDispatcher dispatcher) {
         this.dao = dao;
+        this.dispatcher = dispatcher;
     }
 
     @Override
@@ -78,4 +81,10 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
     public List<Invoice> getUnpaidInvoicesByAccountId(final UUID accountId, final DateTime upToDate) {
         return dao.getUnpaidInvoicesByAccountId(accountId, upToDate);
     }
+
+	@Override
+	public Invoice triggerInvoiceGeneration(UUID accountId,
+			DateTime targetDate, boolean dryrun) throws InvoiceApiException {
+		return dispatcher.processAccount(accountId, targetDate, dryrun);
+	}
 }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 0a23d01..e79df61 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -89,31 +89,35 @@ public class InvoiceDispatcher {
                     new InvoiceApiException(ErrorCode.INVOICE_NO_ACCOUNT_ID_FOR_SUBSCRIPTION_ID, subscriptionId.toString()));
             return;
         }
-
-        GlobalLock lock = null;
+        processAccount(accountId, targetDate, false);
+    }
+    
+    public Invoice processAccount(UUID accountId, DateTime targetDate, boolean dryrun) throws InvoiceApiException {
+		GlobalLock lock = null;
         try {
             lock = locker.lockWithNumberOfTries(LockerService.INVOICE, accountId.toString(), NB_LOCK_TRY);
 
-            processAccountWithLock(accountId, targetDate);
+            return processAccountWithLock(accountId, targetDate, dryrun);
 
         } catch (LockFailedException e) {
             // Not good!
-            log.error(String.format("Failed to process invoice for account %s, subscription %s, targetDate %s",
-                    accountId.toString(), subscriptionId.toString(), targetDate), e);
+            log.error(String.format("Failed to process invoice for account %s, targetDate %s",
+                    accountId.toString(), targetDate), e);
         } finally {
             if (lock != null) {
                 lock.release();
             }
         }
+        return null;
     }
 
-    private void processAccountWithLock(final UUID accountId, final DateTime targetDate) throws InvoiceApiException {
+    private Invoice processAccountWithLock(final UUID accountId, final DateTime targetDate, boolean dryrun) throws InvoiceApiException {
 
         Account account = accountUserApi.getAccountById(accountId);
         if (account == null) {
             log.error("Failed handling entitlement change.",
                     new InvoiceApiException(ErrorCode.INVOICE_ACCOUNT_ID_INVALID, accountId.toString()));
-            return;
+            return null;
         }
 
         SortedSet<BillingEvent> events = entitlementBillingApi.getBillingEventsForAccount(accountId);
@@ -139,10 +143,12 @@ public class InvoiceDispatcher {
             }
             outputDebugData(events, invoiceItemList);
 
-            if (invoice.getNumberOfItems() > 0) {
+            if (invoice.getNumberOfItems() > 0 && !dryrun) {
                 invoiceDao.create(invoice);
             }
         }
+        
+        return invoice;
     }
 
     private void outputDebugData(Collection<BillingEvent> events, Collection<InvoiceItem> invoiceItemList) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockModule.java b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
new file mode 100644
index 0000000..ec96f60
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockModule.java
@@ -0,0 +1,70 @@
+/*
+ * 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.invoice;
+
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.IDBI;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.dbi.DBIProvider;
+import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.BusModule;
+import com.ning.billing.util.glue.GlobalLockerModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
+
+
+public class MockModule extends AbstractModule {
+
+
+    public static final String PLUGIN_NAME = "yoyo";
+
+    @Override
+    protected void configure() {
+
+        bind(Clock.class).to(ClockMock.class).asEagerSingleton();
+        bind(ClockMock.class).asEagerSingleton();
+
+        final MysqlTestingHelper helper = new MysqlTestingHelper();
+        bind(MysqlTestingHelper.class).toInstance(helper);
+        if (helper.isUsingLocalInstance()) {
+            bind(IDBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+        } else {
+            final IDBI dbi = helper.getDBI();
+            bind(IDBI.class).toInstance(dbi);
+        }
+
+        install(new GlobalLockerModule());
+        install(new NotificationQueueModule());
+        install(new InvoiceModule());
+        install(new AccountModule());
+        install(new EntitlementModule());
+        install(new CatalogModule());
+        install(new BusModule());
+
+    }
+
+ 
+}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
new file mode 100644
index 0000000..08d2a7e
--- /dev/null
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -0,0 +1,158 @@
+/*
+ * 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.invoice;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.MockInternationalPrice;
+import com.ning.billing.catalog.MockPlan;
+import com.ning.billing.catalog.MockPlanPhase;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.InternationalPrice;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.billing.BillingEvent;
+import com.ning.billing.entitlement.api.billing.BillingModeType;
+import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+import com.ning.billing.invoice.dao.InvoiceDao;
+import com.ning.billing.invoice.model.InvoiceGenerator;
+import com.ning.billing.invoice.notification.NextBillingDateNotifier;
+import com.ning.billing.mock.BrainDeadProxyFactory;
+import com.ning.billing.mock.BrainDeadProxyFactory.ZombieControl;
+import com.ning.billing.util.bus.BusService;
+import com.ning.billing.util.globallocker.GlobalLocker;
+
+@Guice(modules = {MockModule.class})
+public class TestInvoiceDispatcher {
+
+    @Inject
+    InvoiceUserApi invoiceUserApi;
+    @Inject
+ 	private InvoiceGenerator generator;
+    @Inject
+    private InvoiceDao invoiceDao;
+    @Inject
+    private GlobalLocker locker;
+    
+    @Inject
+    private MysqlTestingHelper helper;
+    
+    @Inject
+    NextBillingDateNotifier notifier;
+    
+    @Inject
+    private BusService busService;
+
+
+
+    @BeforeSuite(alwaysRun = true)
+    public void setup�() throws IOException
+    {
+
+
+        final String accountDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        final String entitlementDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+        final String invoiceDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+//        final String paymentDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/payment/ddl.sql"));
+        final String utilDdl = IOUtils.toString(TestInvoiceDispatcher.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
+        helper.startMysql();
+
+        helper.initDb(accountDdl);
+        helper.initDb(entitlementDdl);
+        helper.initDb(invoiceDdl);
+//        helper.initDb(paymentDdl);
+        helper.initDb(utilDdl);
+        notifier.initialize();
+        notifier.start();
+        
+        busService.getBus().start();
+    }
+
+
+	    @Test(groups={"fast"}, enabled=true)
+	    public void testDryrunInvoice() throws InvoiceApiException {
+	    	UUID accountId = UUID.randomUUID();
+	    	UUID subscriptionId = UUID.randomUUID();
+
+	    	AccountUserApi accountUserApi = BrainDeadProxyFactory.createBrainDeadProxyFor(AccountUserApi.class);
+	    	Account account = BrainDeadProxyFactory.createBrainDeadProxyFor(Account.class);
+	    	((ZombieControl)accountUserApi).addResult("getAccountById", account);
+	    	((ZombieControl)account).addResult("getCurrency", Currency.USD);
+	    	((ZombieControl)account).addResult("getId", accountId);
+	    	
+	    	Subscription subscription =  BrainDeadProxyFactory.createBrainDeadProxyFor(Subscription.class);
+	    	((ZombieControl)subscription).addResult("getId", subscriptionId);
+	    	SortedSet<BillingEvent> events = new TreeSet<BillingEvent>();
+	    	Plan plan = MockPlan.createBicycleNoTrialEvergreen1USD();
+	    	PlanPhase planPhase = MockPlanPhase.create1USDMonthlyEvergreen();
+			DateTime effectiveDate = new DateTime().minusDays(1);
+			InternationalPrice reccurringPrice = MockInternationalPrice.create1USD();
+			InternationalPrice fixedPrice = null;
+			events.add(new DefaultBillingEvent(subscription, effectiveDate,plan,planPhase, fixedPrice , reccurringPrice, BillingPeriod.MONTHLY, 1, BillingModeType.IN_ADVANCE,"", SubscriptionTransitionType.CREATE));
+	    	EntitlementBillingApi entitlementBillingApi = BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementBillingApi.class);
+	    	((ZombieControl)entitlementBillingApi).addResult("getBillingEventsForAccount", events);
+	    	
+	    	
+	    	DateTime target = new DateTime();
+	    	
+	    	InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountUserApi, entitlementBillingApi, invoiceDao, locker);
+	    	
+	    	Invoice invoice = dispatcher.processAccount(accountId, target, true);
+	    	Assert.assertNotNull(invoice);
+	    	
+	    	List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId);
+	    	Assert.assertEquals(invoices.size(),0);
+	    	
+	    	// Try it again to double check
+	    	invoice = dispatcher.processAccount(accountId, target, true);
+	    	Assert.assertNotNull(invoice);
+	    	
+	    	invoices = invoiceDao.getInvoicesByAccount(accountId);
+	    	Assert.assertEquals(invoices.size(),0);
+	    	
+	    	// This time no dry run
+	    	invoice = dispatcher.processAccount(accountId, target, false);
+	    	Assert.assertNotNull(invoice);
+	    	
+	    	invoices = invoiceDao.getInvoicesByAccount(accountId);
+	    	Assert.assertEquals(invoices.size(),1);
+	    	
+	    }
+}
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
index 9d9372c..31fdae3 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithEmbeddedDb.java
@@ -20,11 +20,23 @@ import com.ning.billing.util.bus.Bus;
 import org.apache.commons.collections.MapUtils;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provider;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
+import com.ning.billing.payment.setup.PaymentTestModuleWithMocks.MockProvider;
 import com.ning.billing.util.bus.InMemoryBus;
 
 public class PaymentTestModuleWithEmbeddedDb extends PaymentModule {
-    public PaymentTestModuleWithEmbeddedDb() {
+	public static class MockProvider implements Provider<EntitlementBillingApi> {
+		@Override
+		public EntitlementBillingApi get() {
+			return BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementBillingApi.class);
+		}
+		
+	}
+	
+	public PaymentTestModuleWithEmbeddedDb() {
         super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock")));
     }
 
@@ -37,5 +49,6 @@ public class PaymentTestModuleWithEmbeddedDb extends PaymentModule {
     protected void configure() {
         super.configure();
         bind(Bus.class).to(InMemoryBus.class).asEagerSingleton();
+        bind(EntitlementBillingApi.class).toProvider( MockProvider.class );
     }
 }
diff --git a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
index 144afa4..3dfc637 100644
--- a/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
+++ b/payment/src/test/java/com/ning/billing/payment/setup/PaymentTestModuleWithMocks.java
@@ -16,21 +16,32 @@
 
 package com.ning.billing.payment.setup;
 
-import com.ning.billing.util.bus.InMemoryBus;
 import org.apache.commons.collections.MapUtils;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.inject.Provider;
 import com.ning.billing.account.dao.AccountDao;
 import com.ning.billing.account.dao.MockAccountDao;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import com.ning.billing.invoice.dao.InvoiceDao;
 import com.ning.billing.invoice.dao.MockInvoiceDao;
-
+import com.ning.billing.mock.BrainDeadProxyFactory;
 import com.ning.billing.payment.dao.MockPaymentDao;
 import com.ning.billing.payment.dao.PaymentDao;
 import com.ning.billing.payment.provider.MockPaymentProviderPluginModule;
 import com.ning.billing.util.bus.Bus;
+import com.ning.billing.util.bus.InMemoryBus;
 
 public class PaymentTestModuleWithMocks extends PaymentModule {
+	public static class MockProvider implements Provider<EntitlementBillingApi> {
+		@Override
+		public EntitlementBillingApi get() {
+			return BrainDeadProxyFactory.createBrainDeadProxyFor(EntitlementBillingApi.class);
+		}
+		
+	}
+	
+	
     public PaymentTestModuleWithMocks() {
         super(MapUtils.toProperties(ImmutableMap.of("killbill.payment.provider.default", "my-mock")));
     }
@@ -53,5 +64,6 @@ public class PaymentTestModuleWithMocks extends PaymentModule {
         bind(AccountDao.class).to(MockAccountDao.class);
         bind(MockInvoiceDao.class).asEagerSingleton();
         bind(InvoiceDao.class).to(MockInvoiceDao.class);
+        bind(EntitlementBillingApi.class).toProvider( MockProvider.class );
     }
 }
diff --git a/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java b/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.java
new file mode 100644
index 0000000..31dcd30
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.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.mock;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrainDeadProxyFactory {
+	private static final Logger log = LoggerFactory.getLogger(BrainDeadProxyFactory.class);
+
+	public static interface ZombieControl {
+		
+		public ZombieControl addResult(String method, Object result);
+		
+		public ZombieControl clearResults();
+		
+	}
+
+	@SuppressWarnings("unchecked")
+	public static <T> T createBrainDeadProxyFor(final Class<T> clazz) {
+		return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
+                new Class[] { clazz , ZombieControl.class},
+                new InvocationHandler() {
+					private Map<String,Object> results = new HashMap<String,Object>();
+			
+					@Override
+					public Object invoke(Object proxy, Method method, Object[] args)
+							throws Throwable {
+						
+						if(method.getDeclaringClass().equals(ZombieControl.class)) {
+							if(method.getName().equals("addResult")) {
+								results.put((String) args[0], args[1]);
+								return proxy;
+							} else if(method.getName().equals("clearResults")) {
+								results.clear();
+								return proxy;
+							}
+
+						} else {
+							
+							Object result = results.get(method.getName());
+							if (result != null) {
+								return result;
+							} else {
+								log.error(String.format("No result for Method: '%s' on Class '%s'",method.getName(), method.getDeclaringClass().getName()));
+								throw new UnsupportedOperationException();
+							}
+						}
+						return (Void) null;
+					}
+				});
+	}
+}