killbill-memoizeit
Changes
beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java 195(+130 -65)
Details
diff --git a/api/src/main/java/com/ning/billing/events/OverdueChangeInternalEvent.java b/api/src/main/java/com/ning/billing/events/OverdueChangeInternalEvent.java
index 20acb43..811e48e 100644
--- a/api/src/main/java/com/ning/billing/events/OverdueChangeInternalEvent.java
+++ b/api/src/main/java/com/ning/billing/events/OverdueChangeInternalEvent.java
@@ -26,4 +26,8 @@ public interface OverdueChangeInternalEvent extends BusInternalEvent {
String getPreviousOverdueStateName();
String getNextOverdueStateName();
+
+ Boolean isBlockedBilling();
+
+ Boolean isUnblockedBilling();
}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
index a7a80e5..6abfa0c 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceInternalApi.java
@@ -22,6 +22,7 @@ import java.util.Map;
import java.util.UUID;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import com.ning.billing.catalog.api.Currency;
@@ -70,4 +71,6 @@ public interface InvoiceInternalApi {
*/
public void consumeExistingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) throws InvoiceApiException;
+
+ public void scheduleInvoiceForAccount(UUID accountId, DateTimeZone accountTimeZone, InternalCallContext context) throws InvoiceApiException;
}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 15dd4ea..cba122d 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -132,7 +132,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012, 6, 31 => P1 (We se 6/31 instead of 6/30 because invoice might happen later in that day)
addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
@@ -149,9 +149,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState("OD2");
// 2012, 7, 17 => Retry P1
- addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(7, NextEvent.INVOICE_ADJUSTMENT, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
+ invoiceChecker.checkInvoice(account.getId(), 3,
+ callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-166.64")));
+
// 2012, 7, 18 => Retry P0
addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
@@ -163,29 +167,45 @@ public class TestOverdueIntegration extends TestOverdueBase {
paymentPlugin.makeAllInvoicesFailWithError(false);
final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+ int remainingUnpaidInvoices = invoices.size();
for (final Invoice invoice : invoices) {
if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ remainingUnpaidInvoices--;
+ if (remainingUnpaidInvoices > 0) {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ } else {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.PAYMENT);
+ }
}
}
-
checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
- checkChangePlanWithOverdueState(baseEntitlement, false);
- invoiceChecker.checkRepairedInvoice(account.getId(), 3,
- callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- // We paid up to 07-31, hence the adjustment
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-166.64")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("166.64")));
invoiceChecker.checkInvoice(account.getId(), 4, callContext,
// Item for the upgraded recurring plan
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("64.51")));
+
+ // Do an upgrade now
+ checkChangePlanWithOverdueState(baseEntitlement, false, true);
+
+ invoiceChecker.checkRepairedInvoice(account.getId(), 4, callContext,
+ // Item for the upgraded recurring plan
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("64.51")),
+ // Repair for upgrade
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-64.51")),
+ // CBA generated
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("64.51")));
+
+
+ invoiceChecker.checkInvoice(account.getId(), 5, callContext,
+ // Item for the upgraded recurring plan
new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("154.85")),
- // Credits consumed
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("-154.85")));
+ // Repair for upgrade
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 23), InvoiceItemType.CBA_ADJ, new BigDecimal("-64.51")));
+
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
- // Verify the account balance: 249.95 - 74.99 - 154.85
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-11.79")), 0);
+ // Verify the account balance:
+ assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}
@@ -224,7 +244,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
// 2012, 6, 31 => P1 (We se 6/31 instead of 6/30 because invoice might happen later in that day)
addDaysAndCheckForCompletion(7, NextEvent.INVOICE, NextEvent.PAYMENT_ERROR);
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
invoiceChecker.checkInvoice(account.getId(), 3, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
@@ -237,9 +257,14 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState("OD1");
// 2012, 7, 10 => Retry P0
- addDaysAndCheckForCompletion(1, NextEvent.PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(1, NextEvent.INVOICE_ADJUSTMENT, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
+ invoiceChecker.checkInvoice(account.getId(), 3,
+ callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+ // Repair for the part that was blocked
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-166.64")));
+
// 2012, 7, 17 => Retry P1
addDaysAndCheckForCompletion(7, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
@@ -255,28 +280,31 @@ public class TestOverdueIntegration extends TestOverdueBase {
paymentPlugin.makeAllInvoicesFailWithError(false);
final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+ int remainingUnpaidInvoices = invoices.size();
for (final Invoice invoice : invoices) {
if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ remainingUnpaidInvoices--;
+ if (remainingUnpaidInvoices > 0) {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ } else {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.PAYMENT);
+ }
}
}
checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+ invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+ // New invoice for the part that was unblocked
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("64.51")));
- // Add 10 days to generate next invoice
- addDaysAndCheckForCompletion(10, NextEvent.INVOICE, NextEvent.INVOICE_ADJUSTMENT, NextEvent.PAYMENT);
- invoiceChecker.checkRepairedInvoice(account.getId(), 3,
- callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- // We paid up to 07-31, hence the adjustment
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 23), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-102.13")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 8, 2), new LocalDate(2012, 8, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("102.13")));
+ // Add 10 days to generate next invoice
+ addDaysAndCheckForCompletion(10, NextEvent.INVOICE, NextEvent.PAYMENT);
- invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+ invoiceChecker.checkInvoice(account.getId(), 5, callContext,
// Item for the upgraded recurring plan
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- // Credits consumed
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 8, 2), new LocalDate(2012, 8, 2), InvoiceItemType.CBA_ADJ, new BigDecimal("-102.13")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 8, 31), callContext);
// Verify the account balance: 249.95 - 74.99 - 154.85
@@ -321,9 +349,13 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState("OD1");
// 2012, 7, 10 => Retry P0
- addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
+ addDaysAndCheckForCompletion(8, NextEvent.INVOICE_ADJUSTMENT, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
+ invoiceChecker.checkInvoice(account.getId(), 2,
+ callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2013, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2013, 5, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2136.92")));
+
// 2012, 7, 18 => Retry P0
addDaysAndCheckForCompletion(8, NextEvent.PAYMENT_ERROR);
checkODState("OD2");
@@ -332,36 +364,49 @@ public class TestOverdueIntegration extends TestOverdueBase {
addDaysAndCheckForCompletion(5);
checkODState("OD3");
-
paymentPlugin.makeAllInvoicesFailWithError(false);
final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+ int remainingUnpaidInvoices = invoices.size();
for (final Invoice invoice : invoices) {
if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ remainingUnpaidInvoices--;
+ if (remainingUnpaidInvoices > 0) {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ } else {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.PAYMENT);
+ }
}
}
checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
+ invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ // New invoice for the part that was unblocked up to the BCD
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 23), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("52.56")));
+
+
// Move to 2012, 7, 31 and Make a change of plan
- addDaysAndCheckForCompletion(8);
+ addDaysAndCheckForCompletion(8, NextEvent.INVOICE, NextEvent.PAYMENT);
- checkChangePlanWithOverdueState(baseEntitlement, false);
+ invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+ // New invoice for the part that was unblocked up to the BCD
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
- invoiceChecker.checkRepairedInvoice(account.getId(), 2,
- callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2013, 5, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 10), new LocalDate(2012, 7, 23), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-85.4588")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 5, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-1998.9012")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 7, 31), InvoiceItemType.CBA_ADJ, new BigDecimal("2084.36")));
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ checkChangePlanWithOverdueState(baseEntitlement, false, false);
+
+ invoiceChecker.checkRepairedInvoice(account.getId(), 4, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2013, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-2399.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 7, 31), InvoiceItemType.CBA_ADJ, new BigDecimal("2399.95")));
+
+ invoiceChecker.checkInvoice(account.getId(), 5, callContext,
// Item for the upgraded recurring plan
new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("599.95")),
// Credits consumed
new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 7, 31), InvoiceItemType.CBA_ADJ, new BigDecimal("-599.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 8, 31), callContext);
-
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-1484.41")), 0);
+ assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-1800")), 0);
}
@@ -406,57 +451,73 @@ public class TestOverdueIntegration extends TestOverdueBase {
// Now we should be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
// DAY 67 - 37 days after invoice
addDaysAndCheckForCompletion(2);
// Should still be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
// DAY 75 - 45 days after invoice
- addDaysAndCheckForCompletion(8);
+ addDaysAndCheckForCompletion(8, NextEvent.INVOICE_ADJUSTMENT);
// Should now be in OD2
checkODState("OD2");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
+
+
+ invoiceChecker.checkInvoice(account.getId(), 3,
+ callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
+ // We paid up to 07-31, hence the adjustment
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-124.97")));
// DAY 85 - 55 days after invoice
addDaysAndCheckForCompletion(10);
// Should now be in OD3
checkODState("OD3");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
// Add a payment method and set it as default
paymentApi.addPaymentMethod(BeatrixIntegrationModule.NON_OSGI_PLUGIN_NAME, account, true, paymentMethodPlugin, callContext);
// Pay all invoices
final Collection<Invoice> invoices = invoiceUserApi.getUnpaidInvoicesByAccountId(account.getId(), clock.getUTCToday(), callContext);
+ int remainingUnpaidInvoices = invoices.size();
for (final Invoice invoice : invoices) {
if (invoice.getBalance().compareTo(BigDecimal.ZERO) > 0) {
- createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ remainingUnpaidInvoices--;
+ if (remainingUnpaidInvoices > 0) {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.PAYMENT);
+ } else {
+ createPaymentAndCheckForCompletion(account, invoice, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.PAYMENT);
+ }
}
}
-
checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
- checkChangePlanWithOverdueState(baseEntitlement, false);
- invoiceChecker.checkRepairedInvoice(account.getId(), 3,
- callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 30), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")),
- // We paid up to 07-31, hence the adjustment
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 15), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-124.97")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("124.97")));
invoiceChecker.checkInvoice(account.getId(), 4, callContext,
// Item for the upgraded recurring plan
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("48.37")));
+
+ invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
+
+ checkChangePlanWithOverdueState(baseEntitlement, false, true);
+
+ invoiceChecker.checkRepairedInvoice(account.getId(), 4, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("48.37")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.REPAIR_ADJ, new BigDecimal("-48.37")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("48.37")));
+
+ invoiceChecker.checkInvoice(account.getId(), 5, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 31), InvoiceItemType.RECURRING, new BigDecimal("116.09")),
- // Credits consumed
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-116.09")));
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 25), new LocalDate(2012, 7, 25), InvoiceItemType.CBA_ADJ, new BigDecimal("-48.37")));
+
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 7, 31), callContext);
- // Verify the account balance: 249.95 - 124.98 - 116.09
- assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(new BigDecimal("-8.88")), 0);
+ assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}
@Test(groups = "slow")
@@ -543,7 +604,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
refundPaymentAndCheckForCompletion(account, payment, NextEvent.INVOICE_ADJUSTMENT);
// We should now be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
}
@Test(groups = "slow")
@@ -586,7 +647,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
createChargeBackAndCheckForCompletion(payment, NextEvent.INVOICE_ADJUSTMENT);
// We should now be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
}
@Test(groups = "slow")
@@ -624,7 +685,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
// Now we should be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
// We have two unpaid non-zero dollar invoices at this point
// Pay the first one via an external payment - we should then be 5 days apart from the second invoice
@@ -675,7 +736,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
// Now we should be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
// We have two unpaid non-zero dollar invoices at this point
// Adjust the first (and only) item of the first invoice - we should then be 5 days apart from the second invoice
@@ -707,7 +768,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
// We should now be in OD1
checkODState("OD1");
- checkChangePlanWithOverdueState(baseEntitlement, true);
+ checkChangePlanWithOverdueState(baseEntitlement, true, true);
invoiceChecker.checkInvoice(account.getId(), 4, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 31), new LocalDate(2012, 8, 31), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
@@ -723,7 +784,7 @@ public class TestOverdueIntegration extends TestOverdueBase {
checkODState(DefaultBlockingState.CLEAR_STATE_NAME);
}
- private void checkChangePlanWithOverdueState(final Entitlement entitlement, final boolean shouldFail) {
+ private void checkChangePlanWithOverdueState(final Entitlement entitlement, final boolean shouldFail, final boolean expectedPayment) {
if (shouldFail) {
try {
entitlement.changePlan("Pistol", term, PriceListSet.DEFAULT_PRICELIST_NAME, callContext);
@@ -732,7 +793,11 @@ public class TestOverdueIntegration extends TestOverdueBase {
}
} else {
// Upgrade - we don't expect a payment here due to the scenario (the account will have some CBA)
- changeEntitlementAndCheckForCompletion(entitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_ADJUSTMENT);
+ if (expectedPayment) {
+ changeEntitlementAndCheckForCompletion(entitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE_ADJUSTMENT, NextEvent.INVOICE, NextEvent.PAYMENT);
+ } else {
+ changeEntitlementAndCheckForCompletion(entitlement, "Assault-Rifle", BillingPeriod.MONTHLY, null, NextEvent.CHANGE, NextEvent.INVOICE_ADJUSTMENT, NextEvent.INVOICE);
+ }
}
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index e6f6bd1..7f22d92 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -18,18 +18,27 @@ package com.ning.billing.invoice.api.svcs;
import java.math.BigDecimal;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import com.ning.billing.ErrorCode;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.BillingPeriod;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceInternalApi;
import com.ning.billing.invoice.api.InvoicePayment;
import com.ning.billing.invoice.api.InvoicePaymentType;
import com.ning.billing.invoice.dao.InvoiceDao;
@@ -37,9 +46,10 @@ import com.ning.billing.invoice.dao.InvoiceModelDao;
import com.ning.billing.invoice.dao.InvoicePaymentModelDao;
import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.DefaultInvoicePayment;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.invoice.api.InvoiceInternalApi;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.util.timezone.DateAndTimeZoneContext;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@@ -48,10 +58,18 @@ import com.google.common.collect.Collections2;
public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
private final InvoiceDao dao;
+ private final NextBillingDatePoster nextBillingDatePoster;
+ private final SubscriptionBaseInternalApi subscriptionBaseApi;
+ private final Clock clock;
@Inject
- public DefaultInvoiceInternalApi(final InvoiceDao dao) {
+ public DefaultInvoiceInternalApi(final InvoiceDao dao, final SubscriptionBaseInternalApi subscriptionBaseApi,
+ final Clock clock,
+ final NextBillingDatePoster nextBillingDatePoster) {
this.dao = dao;
+ this.clock = clock;
+ this.subscriptionBaseApi = subscriptionBaseApi;
+ this.nextBillingDatePoster = nextBillingDatePoster;
}
@Override
@@ -122,4 +140,28 @@ public class DefaultInvoiceInternalApi implements InvoiceInternalApi {
public void consumeExistingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context) throws InvoiceApiException {
dao.consumeExstingCBAOnAccountWithUnpaidInvoices(accountId, context);
}
+
+ @Override
+ public void scheduleInvoiceForAccount(final UUID accountId, final DateTimeZone accountTimeZone, final InternalCallContext context) throws InvoiceApiException {
+ final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionBaseApi.getSubscriptionsForAccount(context);
+ SubscriptionBase targetSubscription = null;
+ for (UUID key : subscriptions.keySet()) {
+ for (SubscriptionBase cur : subscriptions.get(key)) {
+ if (cur.getCategory() == ProductCategory.ADD_ON) {
+ continue;
+ }
+ if (cur.getState() != EntitlementState.ACTIVE) {
+ continue;
+ }
+ if (cur.getCurrentPhase() != null && cur.getCurrentPhase().getBillingPeriod() != BillingPeriod.NO_BILLING_PERIOD) {
+ targetSubscription = cur;
+ break;
+ }
+ }
+ }
+ if (targetSubscription != null) {
+ final DateAndTimeZoneContext timeZoneContext = new DateAndTimeZoneContext(targetSubscription.getStartDate(), accountTimeZone, clock);
+ nextBillingDatePoster.insertNextBillingNotification(accountId, targetSubscription.getId(), timeZoneContext.computeUTCDateTimeFromNow(), context.getUserToken());
+ }
+ }
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index fe285ab..c0ea740 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -733,7 +733,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final Map<UUID, DateTime> callbackDateTimePerSubscriptions, final UUID userToken) {
for (final UUID subscriptionId : callbackDateTimePerSubscriptions.keySet()) {
final DateTime callbackDateTimeUTC = callbackDateTimePerSubscriptions.get(subscriptionId);
- nextBillingDatePoster.insertNextBillingNotification(entitySqlDaoWrapperFactory, accountId, subscriptionId, callbackDateTimeUTC, userToken);
+ nextBillingDatePoster.insertNextBillingNotificationFromTransaction(entitySqlDaoWrapperFactory, accountId, subscriptionId, callbackDateTimeUTC, userToken);
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 5ef7697..e3c757e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -23,12 +23,12 @@ import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.invoice.api.DefaultInvoiceService;
import com.ning.billing.notificationq.api.NotificationQueue;
import com.ning.billing.notificationq.api.NotificationQueueService;
import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.UserType;
import com.ning.billing.util.entity.dao.EntitySqlDao;
@@ -51,8 +51,8 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
}
@Override
- public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
+ public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
+ final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
final InternalCallContext context = createCallContext(accountId, userToken);
final NotificationQueue nextBillingQueue;
@@ -70,6 +70,25 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
}
}
+ @Override
+ public void insertNextBillingNotification(final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
+ final InternalCallContext context = createCallContext(accountId, userToken);
+
+ final NotificationQueue nextBillingQueue;
+ try {
+ nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
+ DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
+ log.info("Queuing next billing date notification at {} for subscriptionId {}", futureNotificationTime.toString(), subscriptionId.toString());
+
+ nextBillingQueue.recordFutureNotification(futureNotificationTime,
+ new NextBillingDateNotificationKey(subscriptionId), context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
+ } catch (NoSuchNotificationQueue e) {
+ log.error("Attempting to put items on a non-existent queue (NextBillingDateNotifier).", e);
+ } catch (IOException e) {
+ log.error("Failed to serialize notificationKey for subscriptionId {}", subscriptionId);
+ }
+ }
+
private InternalCallContext createCallContext(final UUID accountId, final UUID userToken) {
return internalCallContextFactory.createInternalCallContext(accountId, "NextBillingDatePoster", CallOrigin.INTERNAL, UserType.SYSTEM, userToken);
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
index 65b6d4d..1fe9b11 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/notification/NextBillingDatePoster.java
@@ -25,6 +25,9 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
public interface NextBillingDatePoster {
- void insertNextBillingNotification(EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, UUID accountId,
+ void insertNextBillingNotificationFromTransaction(EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, UUID accountId,
+ UUID subscriptionId, DateTime futureNotificationTime, UUID userToken);
+
+ void insertNextBillingNotification(UUID accountId,
UUID subscriptionId, DateTime futureNotificationTime, UUID userToken);
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
index 078b61e..c0bc7aa 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/notification/MockNextBillingDatePoster.java
@@ -26,7 +26,11 @@ import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
public class MockNextBillingDatePoster implements NextBillingDatePoster {
@Override
- public void insertNextBillingNotification(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
+ public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID accountId,
+ final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
+ }
+
+ @Override
+ public void insertNextBillingNotification(final UUID accountId, final UUID subscriptionId, final DateTime futureNotificationTime, final UUID userToken) {
}
}
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
index f198239..1c9288c 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/DefaultOverdueChangeEvent.java
@@ -30,16 +30,23 @@ public class DefaultOverdueChangeEvent extends BusEventBase implements OverdueCh
private final UUID overdueObjectId;
private final String previousOverdueStateName;
private final String nextOverdueStateName;
+ private final Boolean isBlockedBilling;
+ private final Boolean isUnblockedBilling;
+
@JsonCreator
public DefaultOverdueChangeEvent(@JsonProperty("overdueObjectId") final UUID overdueObjectId,
@JsonProperty("previousOverdueStateName") final String previousOverdueStateName,
@JsonProperty("nextOverdueStateName") final String nextOverdueStateName,
+ @JsonProperty("isBlockedBilling") final Boolean isBlockedBilling,
+ @JsonProperty("isUnblockedBilling") final Boolean isUnblockedBilling,
@JsonProperty("searchKey1") final Long searchKey1,
@JsonProperty("searchKey2") final Long searchKey2,
@JsonProperty("userToken") final UUID userToken) {
super(searchKey1, searchKey2, userToken);
this.overdueObjectId = overdueObjectId;
+ this.isBlockedBilling = isBlockedBilling;
+ this.isUnblockedBilling = isUnblockedBilling;
this.previousOverdueStateName = previousOverdueStateName;
this.nextOverdueStateName = nextOverdueStateName;
}
@@ -65,4 +72,15 @@ public class DefaultOverdueChangeEvent extends BusEventBase implements OverdueCh
return nextOverdueStateName;
}
+ @Override
+ @JsonProperty("isBlockedBilling")
+ public Boolean isBlockedBilling() {
+ return isBlockedBilling;
+ }
+
+ @Override
+ @JsonProperty("isUnblockedBilling")
+ public Boolean isUnblockedBilling() {
+ return isUnblockedBilling;
+ }
}
diff --git a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
index ee412f3..371b839 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/applicator/OverdueStateApplicator.java
@@ -39,6 +39,9 @@ import com.ning.billing.entitlement.api.BlockingStateType;
import com.ning.billing.entitlement.api.Entitlement;
import com.ning.billing.entitlement.api.EntitlementApi;
import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceInternalApi;
+import com.ning.billing.invoice.api.InvoiceUserApi;
import com.ning.billing.ovedue.notification.OverdueCheckPoster;
import com.ning.billing.overdue.OverdueApiException;
import com.ning.billing.overdue.OverdueCancellationPolicy;
@@ -75,6 +78,7 @@ public class OverdueStateApplicator {
private final PersistentBus bus;
private final AccountInternalApi accountApi;
private final EntitlementApi entitlementApi;
+ private final InvoiceInternalApi invoiceInternalApi;
private final OverdueEmailGenerator overdueEmailGenerator;
private final TagInternalApi tagApi;
private final EmailSender emailSender;
@@ -82,11 +86,13 @@ public class OverdueStateApplicator {
@Inject
public OverdueStateApplicator(final BlockingInternalApi accessApi, final AccountInternalApi accountApi, final EntitlementApi entitlementApi,
+ final InvoiceInternalApi invoiceInternalApi,
final Clock clock, final OverdueCheckPoster poster, final OverdueEmailGenerator overdueEmailGenerator,
final EmailConfig config, final PersistentBus bus, final NonEntityDao nonEntityDao, final TagInternalApi tagApi) {
this.blockingApi = accessApi;
this.accountApi = accountApi;
this.entitlementApi = entitlementApi;
+ this.invoiceInternalApi = invoiceInternalApi;
this.clock = clock;
this.poster = poster;
this.overdueEmailGenerator = overdueEmailGenerator;
@@ -98,7 +104,7 @@ public class OverdueStateApplicator {
public void apply(final OverdueState firstOverdueState, final BillingState billingState,
- final Account overdueable, final String previousOverdueStateName,
+ final Account account, final OverdueState previousOverdueState,
final OverdueState nextOverdueState, final InternalCallContext context) throws OverdueException {
try {
@@ -107,7 +113,7 @@ public class OverdueStateApplicator {
return;
}
- log.debug("OverdueStateApplicator:apply <enter> : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueStateName + ", nextState = " + nextOverdueState);
+ log.debug("OverdueStateApplicator:apply <enter> : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueState.getName() + ", nextState = " + nextOverdueState);
final boolean conditionForNextNotfication = !nextOverdueState.isClearState() ||
// We did not reach the first state yet but we have an unpaid invoice
@@ -115,54 +121,66 @@ public class OverdueStateApplicator {
if (conditionForNextNotfication) {
final Period reevaluationInterval = nextOverdueState.isClearState() ? firstOverdueState.getReevaluationInterval() : nextOverdueState.getReevaluationInterval();
- createFutureNotification(overdueable, clock.getUTCNow().plus(reevaluationInterval), context);
+ createFutureNotification(account, clock.getUTCNow().plus(reevaluationInterval), context);
log.debug("OverdueStateApplicator <notificationQ> : inserting notification for time = " + clock.getUTCNow().plus(reevaluationInterval));
} else if (nextOverdueState.isClearState()) {
- clearFutureNotification(overdueable, context);
+ clearFutureNotification(account, context);
}
- if (previousOverdueStateName.equals(nextOverdueState.getName())) {
+ if (previousOverdueState.getName().equals(nextOverdueState.getName())) {
return;
}
- storeNewState(overdueable, nextOverdueState, context);
+ storeNewState(account, nextOverdueState, context);
- cancelSubscriptionsIfRequired(overdueable, nextOverdueState, context);
+ cancelSubscriptionsIfRequired(account, nextOverdueState, context);
- sendEmailIfRequired(billingState, overdueable, nextOverdueState, context);
+ triggerInvoiceIfNeeded(account, previousOverdueState, nextOverdueState, context);
+
+ sendEmailIfRequired(billingState, account, nextOverdueState, context);
} catch (OverdueApiException e) {
if (e.getCode() != ErrorCode.OVERDUE_NO_REEVALUATION_INTERVAL.getCode()) {
throw new OverdueException(e);
}
+ } catch (InvoiceApiException e) {
+ throw new OverdueException(e);
}
-
try {
- bus.post(createOverdueEvent(overdueable, previousOverdueStateName, nextOverdueState.getName(), context));
+ bus.post(createOverdueEvent(account, previousOverdueState.getName(), nextOverdueState.getName(),isBlockBillingTransition(previousOverdueState, nextOverdueState),
+ isUnblockBillingTransition(previousOverdueState, nextOverdueState), context));
} catch (Exception e) {
log.error("Error posting overdue change event to bus", e);
}
}
- public void clear(final Account overdueable, final String previousOverdueStateName, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
+ private void triggerInvoiceIfNeeded(final Account account, final OverdueState previousOverdueState, final OverdueState nextOverdueState, final InternalCallContext context) throws InvoiceApiException {
+ if (isBlockBillingTransition(previousOverdueState, nextOverdueState) || isUnblockBillingTransition(previousOverdueState, nextOverdueState)) {
+ invoiceInternalApi.scheduleInvoiceForAccount(account.getId(), account.getTimeZone(), context);
+ }
+ }
+
+ public void clear(final Account overdueable, final OverdueState previousOverdueState, final OverdueState clearState, final InternalCallContext context) throws OverdueException {
- log.debug("OverdueStateApplicator:clear : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueStateName);
+ log.debug("OverdueStateApplicator:clear : time = " + clock.getUTCNow() + ", previousState = " + previousOverdueState.getName());
storeNewState(overdueable, clearState, context);
clearFutureNotification(overdueable, context);
try {
- bus.post(createOverdueEvent(overdueable, previousOverdueStateName, clearState.getName(), context));
+ bus.post(createOverdueEvent(overdueable, previousOverdueState.getName(), clearState.getName(), isBlockBillingTransition(previousOverdueState, clearState),
+ isUnblockBillingTransition(previousOverdueState, clearState), context));
} catch (Exception e) {
log.error("Error posting overdue change event to bus", e);
}
}
- private OverdueChangeInternalEvent createOverdueEvent(final Account overdueable, final String previousOverdueStateName, final String nextOverdueStateName, final InternalCallContext context) throws BlockingApiException {
- return new DefaultOverdueChangeEvent(overdueable.getId(), previousOverdueStateName, nextOverdueStateName,
+ private OverdueChangeInternalEvent createOverdueEvent(final Account overdueable, final String previousOverdueStateName, final String nextOverdueStateName,
+ final boolean isBlockedBilling, final boolean isUnblockedBilling, final InternalCallContext context) throws BlockingApiException {
+ return new DefaultOverdueChangeEvent(overdueable.getId(), previousOverdueStateName, nextOverdueStateName, isBlockedBilling, isUnblockedBilling,
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
}
@@ -182,6 +200,14 @@ public class OverdueStateApplicator {
}
}
+ private boolean isBlockBillingTransition(final OverdueState prevOverdueState, final OverdueState nextOverdueState) {
+ return !blockBilling(prevOverdueState) && blockBilling(nextOverdueState);
+ }
+
+ private boolean isUnblockBillingTransition(final OverdueState prevOverdueState, final OverdueState nextOverdueState) {
+ return blockBilling(prevOverdueState) && !blockBilling(nextOverdueState);
+ }
+
private boolean blockChanges(final OverdueState nextOverdueState) {
return nextOverdueState.blockChanges();
}
diff --git a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
index cfbaf4c..a8b07da 100644
--- a/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
+++ b/overdue/src/main/java/com/ning/billing/overdue/wrapper/OverdueWrapper.java
@@ -58,16 +58,19 @@ public class OverdueWrapper {
final BillingState billingState = billingState(context);
final String previousOverdueStateName = api.getBlockingStateForService(overdueable, OverdueService.OVERDUE_SERVICE_NAME, context).getStateName();
+
+ final OverdueState currentOverdueState = overdueStateSet.findState(previousOverdueStateName);
final OverdueState nextOverdueState = overdueStateSet.calculateOverdueState(billingState, clock.getToday(billingState.getAccountTimeZone()));
- overdueStateApplicator.apply(overdueStateSet.getFirstState(), billingState, overdueable, previousOverdueStateName, nextOverdueState, context);
+ overdueStateApplicator.apply(overdueStateSet.getFirstState(), billingState, overdueable, currentOverdueState, nextOverdueState, context);
return nextOverdueState;
}
public void clear(final InternalCallContext context) throws OverdueException, OverdueApiException {
final String previousOverdueStateName = api.getBlockingStateForService(overdueable, OverdueService.OVERDUE_SERVICE_NAME, context).getStateName();
- overdueStateApplicator.clear(overdueable, previousOverdueStateName, overdueStateSet.getClearState(), context);
+ final OverdueState previousOverdueState = overdueStateSet.findState(previousOverdueStateName);
+ overdueStateApplicator.clear(overdueable, previousOverdueState, overdueStateSet.getClearState(), context);
}
public BillingState billingState(final InternalTenantContext context) throws OverdueException {
diff --git a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
index d9ba46c..5f9b236 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/applicator/TestOverdueStateApplicator.java
@@ -48,20 +48,21 @@ public class TestOverdueStateApplicator extends OverdueTestSuiteWithEmbeddedDB {
final Account account = Mockito.mock(Account.class);
Mockito.when(account.getId()).thenReturn(UUID.randomUUID());
+ final OverdueState clearState = config.getBundleStateSet().findState(DefaultBlockingState.CLEAR_STATE_NAME);
OverdueState state;
state = config.getBundleStateSet().findState("OD1");
- applicator.apply(null, null, account, DefaultBlockingState.CLEAR_STATE_NAME, state, internalCallContext);
+ applicator.apply(null, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD1");
state = config.getBundleStateSet().findState("OD2");
- applicator.apply(null, null, account, DefaultBlockingState.CLEAR_STATE_NAME, state, internalCallContext);
+ applicator.apply(null, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD2");
state = config.getBundleStateSet().findState("OD3");
- applicator.apply(null, null, account, DefaultBlockingState.CLEAR_STATE_NAME, state, internalCallContext);
+ applicator.apply(null, null, account, clearState, state, internalCallContext);
testOverdueHelper.checkStateApplied(state);
checkBussEvent("OD3");
}