diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
index 3d81d73..f9342ac 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java
@@ -35,12 +35,12 @@ import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.DryRunType;
import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.payment.api.Payment;
import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
-import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
@@ -271,4 +271,68 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
assertTrue(payment.getPurchasedAmount().compareTo(new BigDecimal("199.90")) == 0);
}
+ @Test(groups = "slow")
+ public void testDraftInvoice() throws Exception {
+
+ final int billingDay = 14;
+ final DateTime initialCreationDate = new DateTime(2015, 5, 15, 0, 0, 0, 0, testTimeZone);
+
+ log.info("Beginning test with BCD of " + billingDay);
+ final Account account = createAccountWithNonOsgiPaymentMethod(getAccountData(billingDay));
+
+ // set clock to the initial start date
+ clock.setTime(initialCreationDate);
+ int invoiceItemCount = 1;
+
+ //
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ //
+ DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ DefaultSubscriptionBase subscription = subscriptionDataFromSubscription(baseEntitlement.getSubscriptionBase());
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 6, 14), new LocalDate(2015, 7, 14), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+ // Move through time and verify we get the same invoice
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+ clock.addDays(30);
+ assertListenerStatus();
+
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ invoiceChecker.checkInvoice(invoices.get(1).getId(), callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ // This will verify that the upcoming invoice notification is found and the invoice is generated at the right date, with correct items
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 7, 14), new LocalDate(2015, 8, 14), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+ // add create external charge
+ final LocalDate date = clock.getToday(account.getTimeZone());
+ final List<InvoiceItem> invoiceItemList = new ArrayList<InvoiceItem>();
+ ExternalChargeInvoiceItem item = new ExternalChargeInvoiceItem(null, account.getId(), subscription.getBundleId(), "", date, BigDecimal.TEN, account.getCurrency());
+ invoiceItemList.add(item);
+ final List<InvoiceItem> draftInvoiceItems = invoiceUserApi.insertExternalCharges(account.getId(), date, invoiceItemList, callContext);
+
+ // add expected invoice
+ final List<ExpectedInvoiceItemCheck> expectedDrafInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+ expectedDrafInvoices.add(new ExpectedInvoiceItemCheck(InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
+
+ // Move through time and verify we get the same invoice
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_ADJUSTMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+
+ invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedDrafInvoices);
+ invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+ invoiceUserApi.invoiceStatusTransition(account.getId(), draftInvoiceItems.get(0).getInvoiceId(), callContext);
+ assertListenerStatus();
+
+ final List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, null, callContext);
+ assertEquals(accountPayments.size(), 3);
+ assertEquals(accountPayments.get(2).getPurchasedAmount(), new BigDecimal("10.000000000"));
+
+ }
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 677473e..b15fdab 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -20,10 +20,12 @@ package org.killbill.billing.invoice.api.user;
import java.io.IOException;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -39,6 +41,9 @@ import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.events.BusInternalEvent;
+import org.killbill.billing.events.InvoiceAdjustmentInternalEvent;
+import org.killbill.billing.events.InvoiceInternalEvent;
import org.killbill.billing.invoice.InvoiceDispatcher;
import org.killbill.billing.invoice.api.DryRunArguments;
import org.killbill.billing.invoice.api.Invoice;
@@ -493,5 +498,21 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
public void invoiceStatusTransition(final UUID accountId, final UUID invoiceId, final CallContext context) throws InvoiceApiException {
final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(accountId, context);
dao.changeInvoiceStatus(accountId, invoiceId, InvoiceStatus.COMMITTED, internalCallContext);
+ final Invoice invoice = this.getInvoice(invoiceId, context);
+
+ // notify invoice creation event
+ final DefaultInvoiceCreationEvent defaultInvoiceCreationEvent = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
+ invoice.getBalance(), invoice.getCurrency(),
+ internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), context.getUserToken());
+ postEvent(defaultInvoiceCreationEvent, accountId, internalCallContext);
}
+
+ private void postEvent(final BusInternalEvent event, final UUID accountId, final InternalCallContext context) {
+ try {
+ eventBus.post(event);
+ } catch (final EventBusException e) {
+ log.error(String.format("Failed to post event %s for account %s", event.getBusEventType(), accountId), e);
+ }
+ }
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index cf7c200..cd00376 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -60,6 +60,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceNotifier;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
import org.killbill.billing.invoice.api.user.DefaultInvoiceNotificationInternalEvent;
@@ -368,9 +369,13 @@ public class InvoiceDispatcher {
setChargedThroughDates(billingEvents.getAccountDateAndTimeZoneContext(), invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
- // TODO we should send bus events when we commit the ionvoice on disk in commitInvoice
- postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+ if (InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
+ // TODO we should send bus events when we commit the ionvoice on disk in commitInvoice
+ postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+ }
+
+ // TODO should we include this notification inside of previous if clause?
notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
}
return invoice;