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/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/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..3acc4d0
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/mock/BrainDeadProxyFactory.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.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 void addResult(String method, Object result);
+
+ public void 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 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]);
+ } else if(method.getName().equals("clearResults")) {
+ results.clear();
+ }
+
+ } 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;
+ }
+ });
+ }
+}