killbill-aplcache
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java 4(+2 -2)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationInvoice.java 72(+68 -4)
invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java 6(+6 -0)
invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java 85(+35 -50)
invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java 3(+2 -1)
invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java 9(+5 -4)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
index 0cbd559..fdd9767 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/overdue/TestOverdueIntegration.java
@@ -685,9 +685,9 @@ public class TestOverdueIntegration extends TestOverdueBase {
// Create an external charge on a new invoice
addDaysAndCheckForCompletion(5);
- busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, account.getId(), bundle.getId(), "For overdue", new LocalDate(2012, 5, 6), BigDecimal.TEN, Currency.USD);
- invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
assertListenerStatus();
invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 6), null, InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
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..6532ce0 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;
@@ -244,8 +244,8 @@ public class TestIntegrationInvoice extends TestIntegrationBase {
final BigDecimal accountBalance1 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
assertTrue(accountBalance1.compareTo(new BigDecimal("249.95")) == 0);
- busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
- invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), callContext);
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
+ invoiceUserApi.insertCredit(account.getId(), new BigDecimal("300"), new LocalDate(clock.getUTCNow(), account.getTimeZone()), account.getCurrency(), true, callContext);
assertListenerStatus();
final BigDecimal accountBalance2 = invoiceUserApi.getAccountBalance(account.getId(), callContext);
@@ -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, false, callContext);
+
+ // add expected invoice
+ final List<ExpectedInvoiceItemCheck> expectedDraftInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+ expectedDraftInvoices.add(new ExpectedInvoiceItemCheck(InvoiceItemType.EXTERNAL_CHARGE, BigDecimal.TEN));
+
+ // Move through time and verify invoices
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+ invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+
+ invoiceChecker.checkInvoice(invoices.get(2).getId(), callContext, expectedDraftInvoices);
+ invoiceChecker.checkInvoice(invoices.get(3).getId(), callContext, expectedInvoices);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+ invoiceUserApi.commitInvoice(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/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
index 13a3a43..d9fc32b 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestInvoicePayment.java
@@ -56,9 +56,9 @@ public class TestInvoicePayment extends TestIntegrationBase {
clock.setDay(new LocalDate(2012, 4, 1));
- busHandler.pushExpectedEvents(NextEvent.INVOICE_ADJUSTMENT);
+ busHandler.pushExpectedEvents(NextEvent.INVOICE);
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, account.getId(), null, "Initial external charge", clock.getUTCToday(), BigDecimal.TEN, Currency.USD);
- final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ final InvoiceItem item1 = invoiceUserApi.insertExternalCharges(account.getId(), clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
assertListenerStatus();
final Invoice invoice = invoiceUserApi.getInvoice(item1.getInvoiceId(), callContext);
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 29ef612..1697e4e 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
@@ -46,6 +46,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.WithAccountLock;
import org.killbill.billing.invoice.dao.InvoiceDao;
@@ -261,7 +262,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
- public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final CallContext context) throws InvoiceApiException {
+ public List<InvoiceItem> insertExternalCharges(final UUID accountId, final LocalDate effectiveDate, final Iterable<InvoiceItem> charges, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
for (final InvoiceItem charge : charges) {
if (charge.getAmount() == null || charge.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID, charge.getAmount());
@@ -283,7 +284,8 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
if (invoiceIdForExternalCharge == null) {
final Currency currency = charge.getCurrency();
if (newInvoicesForExternalCharges.get(currency) == null) {
- final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+ final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
+ final Invoice newInvoiceForExternalCharge = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency, status);
newInvoicesForExternalCharges.put(currency, newInvoiceForExternalCharge);
}
invoiceForExternalCharge = newInvoicesForExternalCharges.get(currency);
@@ -327,13 +329,18 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
@Override
public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount, final LocalDate effectiveDate,
- final Currency currency, final CallContext context) throws InvoiceApiException {
- return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, context);
+ final Currency currency, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
+ return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, autoCommit, context);
}
@Override
public InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount,
final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException {
+ return insertCreditForInvoice(accountId, invoiceId, amount, effectiveDate, currency, false, context);
+ }
+
+ private InvoiceItem insertCreditForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount, final LocalDate effectiveDate,
+ final Currency currency, final boolean autoCommit, final CallContext context) throws InvoiceApiException {
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new InvoiceApiException(ErrorCode.CREDIT_AMOUNT_INVALID, amount);
}
@@ -347,9 +354,13 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
// Create an invoice for that credit if it doesn't exist
final Invoice invoiceForCredit;
if (invoiceId == null) {
- invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency);
+ final InvoiceStatus status = autoCommit ? InvoiceStatus.COMMITTED : InvoiceStatus.DRAFT;
+ invoiceForCredit = new DefaultInvoice(accountId, effectiveDate, effectiveDate, currency, status);
} else {
invoiceForCredit = getInvoiceAndCheckCurrency(invoiceId, currency, context);
+ if (InvoiceStatus.COMMITTED.equals(invoiceForCredit.getStatus())) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_ALREADY_COMMITTED, invoiceId);
+ }
}
// Create the new credit
@@ -482,4 +493,10 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
return invoice;
}
+ @Override
+ public void commitInvoice(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, context);
+ dao.changeInvoiceStatus(invoiceId, InvoiceStatus.COMMITTED, internalCallContext);
+ }
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 1ef6f7f..7277f7f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -41,7 +41,9 @@ import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+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.notification.NextBillingDatePoster;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
@@ -275,11 +277,13 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
final List<InvoiceItemModelDao> createdInvoiceItems = new LinkedList<InvoiceItemModelDao>();
for (final InvoiceModelDao invoiceModelDao : invoices) {
boolean madeChanges = false;
+ boolean newInvoice = false;
// Create the invoice if needed
if (invoiceSqlDao.getById(invoiceModelDao.getId().toString(), context) == null) {
invoiceSqlDao.create(invoiceModelDao, context);
madeChanges = true;
+ newInvoice = true;
}
// Create the invoice items if needed
@@ -293,10 +297,16 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
if (madeChanges) {
cbaDao.addCBAComplexityFromTransaction(invoiceModelDao.getId(), entitySqlDaoWrapperFactory, context);
+ }
- // Notify the bus since the balance of the invoice changed
- // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
- notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
+ if (InvoiceStatus.COMMITTED.equals(invoiceModelDao.getStatus())) {
+ if (newInvoice) {
+ notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoiceModelDao, context);
+ } else if (madeChanges) {
+ // Notify the bus since the balance of the invoice changed (only if the invoice is COMMITTED)
+ // TODO should we post an InvoiceCreationInternalEvent event instead? Note! This will trigger a payment (see InvoiceHandler)
+ notifyBusOfInvoiceAdjustment(entitySqlDaoWrapperFactory, invoiceModelDao.getId(), invoiceModelDao.getAccountId(), context.getUserToken(), context);
+ }
}
}
@@ -854,4 +864,48 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
}
+ @Override
+ public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newStatus,
+ final InternalCallContext context) throws InvoiceApiException {
+ transactionalSqlDao.execute(InvoiceApiException.class, new EntitySqlDaoTransactionWrapper<Void>() {
+ @Override
+ public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
+ final InvoiceSqlDao transactional = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
+
+ // Retrieve the invoice and make sure it belongs to the right account
+ final InvoiceModelDao invoice = transactional.getById(invoiceId.toString(), context);
+
+ if (invoice == null ) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
+ }
+
+ if (invoice.getStatus().equals(newStatus)) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_STATUS, newStatus, invoiceId, invoice.getStatus());
+ }
+
+ transactional.updateStatus(invoiceId.toString(), newStatus.toString(), context);
+
+ if (InvoiceStatus.COMMITTED.equals(newStatus)) {
+ // notify invoice creation event
+ notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoice, context);
+ }
+
+ return null;
+ }
+ });
+ }
+
+ private void notifyBusOfInvoiceCreation(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InvoiceModelDao invoice, final InternalCallContext context) {
+ try {
+ final BigDecimal balance = InvoiceModelDaoHelper.getBalance(invoice);
+ final DefaultInvoiceCreationEvent event = new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
+ balance, invoice.getCurrency(),
+ context.getAccountRecordId(), context.getTenantRecordId(),
+ context.getUserToken());
+ eventBus.postFromTransaction(event, entitySqlDaoWrapperFactory.getHandle().getConnection());
+ } catch (final EventBusException e) {
+ log.error(String.format("Failed to post invoice creation event %s for account %s", invoice.getAccountId()), e);
+ }
+ }
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index 25c9249..7798545 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -32,6 +32,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.EntityDao;
@@ -139,4 +140,13 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
*/
public void consumeExstingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context);
+ /**
+ * Update invoice status
+ *
+ * @param invoiceId the invoice id
+ * @param newState the new invoice state
+ * @param context the tenant context
+ * @throws InvoiceApiException
+ */
+ void changeInvoiceStatus(UUID invoiceId, InvoiceStatus newState, InternalCallContext context) throws InvoiceApiException;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index f6b6619..64eef23 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -38,6 +38,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.tag.TagInternalApi;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
import org.killbill.billing.util.tag.ControlTagType;
@@ -171,7 +172,7 @@ public class InvoiceDaoHelper {
final Collection<InvoiceModelDao> unpaidInvoices = Collections2.filter(invoices, new Predicate<InvoiceModelDao>() {
@Override
public boolean apply(final InvoiceModelDao in) {
- return (InvoiceModelDaoHelper.getBalance(in).compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
+ return (InvoiceStatus.COMMITTED.equals(in.getStatus())) && (InvoiceModelDaoHelper.getBalance(in).compareTo(BigDecimal.ZERO) >= 1) && (upToDate == null || !in.getTargetDate().isAfter(upToDate));
}
});
return new ArrayList<InvoiceModelDao>(unpaidInvoices);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index 0316d8d..b25388d 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -27,6 +27,7 @@ import org.joda.time.LocalDate;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.dao.TableName;
import org.killbill.billing.util.entity.dao.EntityModelDao;
@@ -40,6 +41,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
private LocalDate targetDate;
private Currency currency;
private boolean migrated;
+ private InvoiceStatus status;
// Not in the database, for convenience only
private List<InvoiceItemModelDao> invoiceItems = new LinkedList<InvoiceItemModelDao>();
@@ -52,7 +54,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
public InvoiceModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
@Nullable final Integer invoiceNumber, final LocalDate invoiceDate, final LocalDate targetDate,
- final Currency currency, final boolean migrated) {
+ final Currency currency, final boolean migrated, final InvoiceStatus status) {
super(id, createdDate, createdDate);
this.accountId = accountId;
this.invoiceNumber = invoiceNumber;
@@ -61,19 +63,24 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
this.currency = currency;
this.migrated = migrated;
this.isWrittenOff = false;
+ this.status = status;
}
public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
- this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated);
+ this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated, InvoiceStatus.COMMITTED);
+ }
+
+ public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated, final InvoiceStatus status) {
+ this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, migrated, status);
}
public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
- this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false);
+ this(UUIDs.randomUUID(), null, accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED);
}
public InvoiceModelDao(final Invoice invoice) {
this(invoice.getId(), invoice.getCreatedDate(), invoice.getAccountId(), invoice.getInvoiceNumber(), invoice.getInvoiceDate(),
- invoice.getTargetDate(), invoice.getCurrency(), invoice.isMigrationInvoice());
+ invoice.getTargetDate(), invoice.getCurrency(), invoice.isMigrationInvoice(), invoice.getStatus());
}
public void addInvoiceItems(final List<InvoiceItemModelDao> invoiceItems) {
@@ -168,6 +175,14 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
this.isWrittenOff = isWrittenOff;
}
+ public InvoiceStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(final InvoiceStatus status) {
+ this.status = status;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@@ -178,6 +193,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
sb.append(", targetDate=").append(targetDate);
sb.append(", currency=").append(currency);
sb.append(", migrated=").append(migrated);
+ sb.append(", status=").append(status);
sb.append('}');
return sb.toString();
}
@@ -214,6 +230,9 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
if (targetDate != null ? !targetDate.equals(that.targetDate) : that.targetDate != null) {
return false;
}
+ if (status != null ? !status.equals(that.status) : that.status != null) {
+ return false;
+ }
return true;
}
@@ -227,6 +246,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
result = 31 * result + (targetDate != null ? targetDate.hashCode() : 0);
result = 31 * result + (currency != null ? currency.hashCode() : 0);
result = 31 * result + (migrated ? 1 : 0);
+ result = 31 * result + (status != null ? status.hashCode() : 0);
return result;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
index 3b46cf6..ffecc24 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceSqlDao.java
@@ -19,13 +19,17 @@ package org.killbill.billing.invoice.dao;
import java.util.List;
import java.util.UUID;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.util.audit.ChangeType;
+import org.killbill.billing.util.entity.dao.Audited;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
import org.killbill.billing.util.entity.dao.EntitySqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@EntitySqlDaoStringTemplate
public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
@@ -38,5 +42,10 @@ public interface InvoiceSqlDao extends EntitySqlDao<InvoiceModelDao, Invoice> {
UUID getInvoiceIdByPaymentId(@Bind("paymentId") final String paymentId,
@BindBean final InternalTenantContext context);
+ @SqlUpdate
+ @Audited(ChangeType.UPDATE)
+ public void updateStatus(@Bind("id") String invoiceId,
+ @Bind("status") String status,
+ @BindBean final InternalCallContext context);
}
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..4253d46 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,10 +369,12 @@ 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 invoice on disk in commitInvoice
+ postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
+ notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
+ }
- notifyAccountIfEnabled(account, invoice, isRealInvoiceWithNonEmptyItems, context);
}
return invoice;
} catch (final AccountApiException e) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index 514e368..640936d 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -32,6 +32,7 @@ import org.killbill.billing.entity.EntityBase;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -54,17 +55,21 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
private final boolean isWrittenOff;
private final Currency processedCurrency;
-
+ private final InvoiceStatus status;
// Used to create a new invoice
public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
- this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+ this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED);
+ }
+
+ public DefaultInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final InvoiceStatus status) {
+ this(UUIDs.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, status);
}
public DefaultInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
- final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
- this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false);
+ final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice, final InvoiceStatus status) {
+ this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false, status);
}
@@ -72,7 +77,8 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
public DefaultInvoice(final InvoiceModelDao invoiceModelDao) {
this(invoiceModelDao.getId(), invoiceModelDao.getCreatedDate(), invoiceModelDao.getAccountId(),
invoiceModelDao.getInvoiceNumber(), invoiceModelDao.getInvoiceDate(), invoiceModelDao.getTargetDate(),
- invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(), invoiceModelDao.isWrittenOff());
+ invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(),
+ invoiceModelDao.isWrittenOff(), invoiceModelDao.getStatus());
addInvoiceItems(Collections2.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
@Override
public InvoiceItem apply(final InvoiceItemModelDao input) {
@@ -90,7 +96,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
private DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
@Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
final LocalDate targetDate, final Currency currency, final Currency processedCurrency,
- final boolean isMigrationInvoice, final boolean isWrittenOff) {
+ final boolean isMigrationInvoice, final boolean isWrittenOff, final InvoiceStatus status) {
super(invoiceId, createdDate, createdDate);
this.accountId = accountId;
this.invoiceNumber = invoiceNumber;
@@ -102,13 +108,14 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
this.isWrittenOff = isWrittenOff;
this.invoiceItems = new ArrayList<InvoiceItem>();
this.payments = new ArrayList<InvoicePayment>();
+ this.status = status;
}
// Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
@Override
public Object clone() {
- final Invoice clonedInvoice = new DefaultInvoice(getId(), getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff());
+ final Invoice clonedInvoice = new DefaultInvoice(getId(), getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff(), getStatus());
clonedInvoice.getInvoiceItems().addAll(getInvoiceItems());
clonedInvoice.getPayments().addAll(getPayments());
return clonedInvoice;
@@ -239,8 +246,13 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
}
@Override
+ public InvoiceStatus getStatus() {
+ return status;
+ }
+
+ @Override
public String toString() {
- return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + "]";
+ return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + ", Status=" + status + "]";
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index 7ebb8d2..70afdec 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -44,6 +44,7 @@ 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.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
@@ -341,6 +342,11 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
return invoice.getUpdatedDate();
}
+ @Override
+ public InvoiceStatus getStatus() {
+ return invoice.getStatus();
+ }
+
// Expose the fields for children classes. This is useful for further customization of the invoices
@SuppressWarnings("UnusedDeclaration")
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 8daa8b1..5fe6558 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -7,6 +7,7 @@ tableFields(prefix) ::= <<
, <prefix>invoice_date
, <prefix>target_date
, <prefix>currency
+, <prefix>status
, <prefix>migrated
, <prefix>created_by
, <prefix>created_date
@@ -17,6 +18,7 @@ tableValues() ::= <<
, :invoiceDate
, :targetDate
, :currency
+, :status
, :migrated
, :createdBy
, :createdDate
@@ -50,3 +52,10 @@ getInvoiceIdByPaymentId() ::= <<
<AND_CHECK_TENANT("i.")>
<AND_CHECK_TENANT("ip.")>
>>
+
+updateStatus() ::= <<
+ UPDATE <tableName()>
+ SET status = :status
+ WHERE id = :id
+ <AND_CHECK_TENANT()>;
+>>
\ No newline at end of file
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 1a3907c..b81af02 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -40,6 +40,7 @@ CREATE TABLE invoices (
invoice_date date NOT NULL,
target_date date NOT NULL,
currency varchar(3) NOT NULL,
+ status varchar(15) NOT NULL DEFAULT 'COMMITTED',
migrated bool NOT NULL,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index a572def..ebd73d3 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -32,6 +32,7 @@ import org.killbill.billing.invoice.api.Invoice;
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.InvoiceStatus;
import org.killbill.billing.invoice.model.ExternalChargeInvoiceItem;
import org.killbill.billing.util.api.TagApiException;
import org.killbill.billing.util.callcontext.CallContext;
@@ -71,7 +72,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Post an external charge
final BigDecimal externalChargeAmount = BigDecimal.TEN;
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
- final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
verifyExternalChargeOnNewInvoice(accountBalance, null, externalChargeAmount, externalChargeInvoiceItem);
}
@@ -84,7 +85,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
final BigDecimal externalChargeAmount = BigDecimal.TEN;
final UUID bundleId = UUID.randomUUID();
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(null, accountId, bundleId, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
- final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
verifyExternalChargeOnNewInvoice(accountBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem);
}
@@ -120,7 +121,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Post an external charge
final BigDecimal externalChargeAmount = BigDecimal.TEN;
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
- final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
verifyExternalChargeOnExistingInvoice(invoiceBalance, null, externalChargeAmount, externalChargeInvoiceItem);
}
@@ -139,7 +140,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Post an external charge
final BigDecimal externalChargeAmount = BigDecimal.TEN;
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, null, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
- final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), newCallContextLater).get(0);
+ final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, newCallContextLater).get(0);
final Invoice newInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
final BigDecimal newOriginalAmountCharged = newInvoice.getOriginalChargedAmount();
@@ -164,7 +165,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
final BigDecimal externalChargeAmount = BigDecimal.TEN;
final UUID bundleId = UUID.randomUUID();
final InvoiceItem externalCharge = new ExternalChargeInvoiceItem(invoiceId, accountId, bundleId, UUID.randomUUID().toString(), clock.getUTCToday(), externalChargeAmount, accountCurrency);
- final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), callContext).get(0);
+ final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharges(accountId, clock.getUTCToday(), ImmutableList.<InvoiceItem>of(externalCharge), true, callContext).get(0);
verifyExternalChargeOnExistingInvoice(invoiceBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem);
}
@@ -187,8 +188,8 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
}
- @Test(groups = "slow")
- public void testAdjustFullInvoice() throws Exception {
+ @Test(groups = "slow", expectedExceptions = InvoiceApiException.class, expectedExceptionsMessageRegExp = ".*it is already in COMMITTED status")
+ public void testAdjustCommittedInvoice() throws Exception {
// Verify the initial invoice balance
final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
@@ -200,50 +201,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
// Adjust the invoice for the full amount
final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, invoiceBalance,
clock.getUTCToday(), accountCurrency, callContext);
- Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
- Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
- Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(invoiceBalance.negate()), 0);
- Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
- Assert.assertNull(creditInvoiceItem.getLinkedItemId());
-
- // Verify the adjusted invoice balance
- final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
- Assert.assertEquals(adjustedInvoiceBalance.compareTo(BigDecimal.ZERO), 0);
- // Verify the adjusted account balance
- final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
- Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
- }
-
- @Test(groups = "slow")
- public void testAdjustPartialInvoice() throws Exception {
- // Verify the initial invoice balance
- final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
- Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
-
- // Verify the initial account balance
- final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
- Assert.assertEquals(accountBalance, invoiceBalance);
-
- // Adjust the invoice for a fraction of the balance
- final BigDecimal creditAmount = invoiceBalance.divide(BigDecimal.TEN);
- final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, creditAmount,
- clock.getUTCToday(), accountCurrency, callContext);
- Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId);
- Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ);
- Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId);
- Assert.assertEquals(creditInvoiceItem.getAmount().compareTo(creditAmount.negate()), 0);
- Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency);
- Assert.assertNull(creditInvoiceItem.getLinkedItemId());
-
- // Verify the adjusted invoice balance
- final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
- verifyAdjustedInvoiceBalance(invoiceBalance, creditAmount, accountCurrency, adjustedInvoiceBalance);
-
- // Verify the adjusted account balance
- final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
- Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance);
}
@Test(groups = "slow")
@@ -365,4 +323,31 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
final Invoice invoiceAfterTagRemoval = invoiceUserApi.getInvoice(invoiceId, callContext);
assertEquals(invoiceAfterTagRemoval.getBalance().compareTo(BigDecimal.ZERO), 1);
}
+
+ @Test(groups = "slow")
+ public void testCommitInvoice() throws Exception {
+ // Verify the initial invoice balance
+ final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance();
+ Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1);
+
+ // Verify the initial account balance
+ final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext);
+ Assert.assertEquals(accountBalance, invoiceBalance);
+
+ // Adjust the invoice for the full amount
+ final BigDecimal creditAmount = BigDecimal.TEN;
+ final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, null, creditAmount,
+ clock.getUTCToday(), accountCurrency, callContext);
+
+ final UUID invoiceId = creditInvoiceItem.getInvoiceId();
+ Invoice creditInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
+ Assert.assertEquals(creditInvoice.getStatus(), InvoiceStatus.DRAFT);
+ Assert.assertEquals(creditInvoiceItem.getInvoiceId(), creditInvoice.getId());
+
+ // move invoice from DRAFT to COMMITTED
+ invoiceUserApi.commitInvoice(creditInvoice.getId(), callContext);
+ creditInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
+ Assert.assertEquals(creditInvoice.getStatus(), InvoiceStatus.COMMITTED);
+
+ }
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
index 65e3b0e..d3a841c 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/MockInvoiceDao.java
@@ -34,6 +34,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.user.DefaultInvoiceCreationEvent;
import org.killbill.billing.util.entity.DefaultPagination;
import org.killbill.billing.util.entity.Pagination;
@@ -362,4 +363,8 @@ public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice,
throw new UnsupportedOperationException();
}
+ @Override
+ public void changeInvoiceStatus(final UUID invoiceId, final InvoiceStatus newState, final InternalCallContext context) throws InvoiceApiException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
index 74d938e..3e387b7 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceDao.java
@@ -32,7 +32,6 @@ import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.Account;
-import org.killbill.billing.account.api.DefaultAccount;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.DefaultPrice;
import org.killbill.billing.catalog.MockInternationalPrice;
@@ -54,6 +53,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.generator.InvoiceWithMetadata;
import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
@@ -1083,6 +1083,54 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
assertEquals(invoices.size(), 2);
}
+ @Test(groups = "slow")
+ public void testGetUnpaidInvoicesByAccountIdWithDraftInvoice() throws EntityPersistenceException {
+ final UUID accountId = account.getId();
+ final UUID bundleId = UUID.randomUUID();
+ final LocalDate targetDate1 = new LocalDate(2011, 10, 6);
+ final Invoice invoice1 = new DefaultInvoice(accountId, clock.getUTCToday(), targetDate1, Currency.USD);
+ invoiceUtil.createInvoice(invoice1, true, context);
+
+ final LocalDate startDate = new LocalDate(2011, 3, 1);
+ final LocalDate endDate = startDate.plusMonths(1);
+
+ final BigDecimal rate1 = new BigDecimal("17.0");
+ final BigDecimal rate2 = new BigDecimal("42.0");
+
+ final RecurringInvoiceItem item1 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase A", startDate, endDate,
+ rate1, rate1, Currency.USD);
+ invoiceUtil.createInvoiceItem(item1, context);
+
+ final RecurringInvoiceItem item2 = new RecurringInvoiceItem(invoice1.getId(), accountId, bundleId, UUID.randomUUID(), "test plan", "test phase B", startDate, endDate,
+ rate2, rate2, Currency.USD);
+ invoiceUtil.createInvoiceItem(item2, context);
+
+ LocalDate upToDate;
+ Collection<InvoiceModelDao> invoices;
+
+ upToDate = new LocalDate(2011, 1, 1);
+ invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+ assertEquals(invoices.size(), 0);
+
+ upToDate = new LocalDate(2012, 1, 1);
+ invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+ assertEquals(invoices.size(), 1);
+
+ List<InvoiceModelDao> allInvoicesByAccount = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
+ assertEquals(allInvoicesByAccount.size(), 1);
+
+ // insert DRAFT invoice
+ createCredit(accountId, new LocalDate(2011, 12, 31), BigDecimal.TEN);
+
+ allInvoicesByAccount = invoiceDao.getInvoicesByAccount(new LocalDate(2011, 1, 1), context);
+ assertEquals(allInvoicesByAccount.size(), 2);
+ assertEquals(allInvoicesByAccount.get(0).getStatus(), InvoiceStatus.COMMITTED);
+ assertEquals(allInvoicesByAccount.get(1).getStatus(), InvoiceStatus.DRAFT);
+
+ upToDate = new LocalDate(2012, 1, 1);
+ invoices = invoiceDao.getUnpaidInvoicesByAccountId(accountId, upToDate, context);
+ assertEquals(invoices.size(), 1);
+ }
/*
*
@@ -1659,7 +1707,7 @@ public class TestInvoiceDao extends InvoiceTestSuiteWithEmbeddedDB {
private void createCredit(final UUID accountId, @Nullable final UUID invoiceId, final LocalDate effectiveDate, final BigDecimal creditAmount) {
final InvoiceModelDao invoiceModelDao;
if (invoiceId == null) {
- invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD);
+ invoiceModelDao = new InvoiceModelDao(accountId, effectiveDate, effectiveDate, Currency.USD, false, InvoiceStatus.DRAFT);
} else {
invoiceModelDao = invoiceDao.getById(invoiceId, context);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
index 4b8be24..19778f5 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/generator/TestDefaultInvoiceGenerator.java
@@ -53,6 +53,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.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.invoice.model.DefaultInvoicePayment;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
@@ -960,7 +961,7 @@ public class TestDefaultInvoiceGenerator extends InvoiceTestSuiteNoDB {
final LocalDate targetDate = new LocalDate(2013, 10, 30);
- final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), targetDate, currency, false);
+ final Invoice existingInvoice = new DefaultInvoice(UUID.randomUUID(), accountId, null, clock.getUTCToday(), targetDate, currency, false, InvoiceStatus.COMMITTED);
// Set the existing recurring invoice item 2013/06/15 - 2013/07/15
final LocalDate startDate = new LocalDate(2013, 06, 15);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
index 0f90be1..87a72de 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
@@ -38,6 +38,7 @@ import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -96,6 +97,7 @@ public class InvoiceTestUtils {
Mockito.when(invoice.getTargetDate()).thenReturn(clock.getUTCToday());
Mockito.when(invoice.getCurrency()).thenReturn(currency);
Mockito.when(invoice.isMigrationInvoice()).thenReturn(false);
+ Mockito.when(invoice.getStatus()).thenReturn(InvoiceStatus.COMMITTED);
final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
final List<InvoiceItemModelDao> invoiceModelItems = new ArrayList<InvoiceItemModelDao>();
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index 725d6d7..a6a5359 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -33,6 +33,7 @@ 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.api.InvoicePaymentType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory.ResourceBundleType;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -81,7 +82,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
fixedItem.getStartDate(), fixedItem.getAmount().negate(),
fixedItem.getCurrency());
final Invoice invoice = new DefaultInvoice(fixedItem.getInvoiceId(), fixedItem.getAccountId(), null,
- new LocalDate(), new LocalDate(), Currency.USD, false);
+ new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
invoice.addInvoiceItem(fixedItem);
invoice.addInvoiceItem(creditBalanceAdjInvoiceItem);
invoice.addInvoiceItem(creditBalanceAdjInvoiceItem2);
@@ -128,7 +129,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
final RefundAdjInvoiceItem refundAdjInvoiceItem = new RefundAdjInvoiceItem(fixedItem.getInvoiceId(), fixedItem.getAccountId(),
fixedItem.getStartDate(), BigDecimal.ONE.negate(), fixedItem.getCurrency());
final DefaultInvoice invoice = new DefaultInvoice(fixedItem.getInvoiceId(), fixedItem.getAccountId(), null,
- new LocalDate(), new LocalDate(), Currency.USD, false);
+ new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
invoice.addInvoiceItem(fixedItem);
invoice.addInvoiceItem(repairAdjInvoiceItem);
invoice.addInvoiceItem(creditBalanceAdjInvoiceItem);
@@ -392,7 +393,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
@Test(groups = "fast")
public void testProcessedCurrencyDoesNotExist() throws Exception {
- final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.USD, false);
+ final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.USD, false, InvoiceStatus.COMMITTED);
checkOutput(invoice,
"{{#invoice.processedCurrency}}" +
@@ -412,7 +413,7 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
new LocalDate(), new BigDecimal("1499.95"), Currency.BRL);
- final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.BRL, false);
+ final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.BRL, false, InvoiceStatus.COMMITTED);
invoice.addInvoiceItem(fixedItemBRL);
final String template = "<html>\n" +
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
index 639d2c9..4fa9335 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/InvoiceJson.java
@@ -50,10 +50,12 @@ public class InvoiceJson extends JsonBase {
private final List<InvoiceItemJson> items;
private final String bundleKeys;
private final List<CreditJson> credits;
+ private final String status;
@JsonCreator
public InvoiceJson(@JsonProperty("amount") final BigDecimal amount,
@JsonProperty("currency") final String currency,
+ @JsonProperty("status") final String status,
@JsonProperty("creditAdj") final BigDecimal creditAdj,
@JsonProperty("refundAdj") final BigDecimal refundAdj,
@JsonProperty("invoiceId") final String invoiceId,
@@ -69,6 +71,7 @@ public class InvoiceJson extends JsonBase {
super(auditLogs);
this.amount = amount;
this.currency = currency;
+ this.status = status;
this.creditAdj = creditAdj;
this.refundAdj = refundAdj;
this.invoiceId = invoiceId;
@@ -87,7 +90,7 @@ public class InvoiceJson extends JsonBase {
}
public InvoiceJson(final Invoice input, final String bundleKeys, final List<CreditJson> credits, final List<AuditLog> auditLogs) {
- this(input.getChargedAmount(), input.getCurrency().toString(), input.getCreditedAmount(), input.getRefundedAmount(),
+ this(input.getChargedAmount(), input.getCurrency().toString(), input.getStatus().toString(), input.getCreditedAmount(), input.getRefundedAmount(),
input.getId().toString(), input.getInvoiceDate(), input.getTargetDate(), String.valueOf(input.getInvoiceNumber()),
input.getBalance(), input.getAccountId().toString(), bundleKeys, credits, null, toAuditLogJson(auditLogs));
}
@@ -102,6 +105,7 @@ public class InvoiceJson extends JsonBase {
}
this.amount = input.getChargedAmount();
this.currency = input.getCurrency().toString();
+ this.status = input.getStatus().toString();
this.creditAdj = input.getCreditedAmount();
this.refundAdj = input.getRefundedAmount();
this.invoiceId = input.getId().toString();
@@ -166,11 +170,16 @@ public class InvoiceJson extends JsonBase {
return credits;
}
+ public String getStatus() {
+ return status;
+ }
+
@Override
public String toString() {
return "InvoiceJson{" +
"amount=" + amount +
", currency='" + currency + '\'' +
+ ", status='" + status + '\'' +
", invoiceId='" + invoiceId + '\'' +
", invoiceDate=" + invoiceDate +
", targetDate=" + targetDate +
@@ -235,6 +244,9 @@ public class InvoiceJson extends JsonBase {
if (targetDate != null ? targetDate.compareTo(that.targetDate) != 0 : that.targetDate != null) {
return false;
}
+ if (status != null ? !status.equals(that.status) : that.status != null) {
+ return false;
+ }
return true;
}
@@ -243,6 +255,7 @@ public class InvoiceJson extends JsonBase {
public int hashCode() {
int result = amount != null ? amount.hashCode() : 0;
result = 31 * result + (currency != null ? currency.hashCode() : 0);
+ result = 31 * result + (status != null ? status.hashCode() : 0);
result = 31 * result + (invoiceId != null ? invoiceId.hashCode() : 0);
result = 31 * result + (invoiceDate != null ? invoiceDate.hashCode() : 0);
result = 31 * result + (targetDate != null ? targetDate.hashCode() : 0);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index ef929a2..ee73b60 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -689,7 +689,7 @@ public class AccountResource extends JaxRsResourceBase {
// then we apply some credit on the account.
//
if (externalPayment && remainingRequestPayment.compareTo(BigDecimal.ZERO) > 0) {
- invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), callContext);
+ invoiceApi.insertCredit(account.getId(), remainingRequestPayment, clock.getUTCToday(), account.getCurrency(), true, callContext);
}
return Response.status(Status.OK).build();
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
index bfad1b1..d9f2585 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CreditResource.java
@@ -20,12 +20,14 @@ import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -103,6 +105,7 @@ public class CreditResource extends JaxRsResourceBase {
@ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied"),
@ApiResponse(code = 404, message = "Account not found")})
public Response createCredit(final CreditJson json,
+ @QueryParam(QUERY_AUTO_COMMIT) @DefaultValue("false") final Boolean autoCommit,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@@ -125,7 +128,7 @@ public class CreditResource extends JaxRsResourceBase {
} else {
// Apply a account level credit
credit = invoiceUserApi.insertCredit(account.getId(), json.getCreditAmount(), effectiveDate,
- account.getCurrency(), callContext);
+ account.getCurrency(), autoCommit, callContext);
}
return uriBuilder.buildResponse(uriInfo, CreditResource.class, "getCredit", credit.getId());
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 845bfd0..2f94dd6 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -43,6 +43,7 @@ import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@@ -460,6 +461,7 @@ public class InvoiceResource extends JaxRsResourceBase {
@QueryParam(QUERY_REQUESTED_DT) final String requestedDateTimeString,
@QueryParam(QUERY_PAY_INVOICE) @DefaultValue("false") final Boolean payInvoice,
@QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+ @QueryParam(QUERY_AUTO_COMMIT) @DefaultValue("false") final Boolean autoCommit,
@HeaderParam(HDR_CREATED_BY) final String createdBy,
@HeaderParam(HDR_REASON) final String reason,
@HeaderParam(HDR_COMMENT) final String comment,
@@ -482,7 +484,7 @@ public class InvoiceResource extends JaxRsResourceBase {
}
}
);
- final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, externalCharges, callContext);
+ final List<InvoiceItem> createdExternalCharges = invoiceApi.insertExternalCharges(account.getId(), requestedDate, externalCharges, autoCommit, callContext);
if (payInvoice) {
final Collection<UUID> paidInvoices = new HashSet<UUID>();
@@ -940,6 +942,26 @@ public class InvoiceResource extends JaxRsResourceBase {
context.createContext(createdBy, reason, comment, request));
}
+ @TimedResource
+ @PUT
+ @Path("/{invoiceId:" + UUID_PATTERN + "}/" + COMMIT_INVOICE)
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Perform the invoice status transition from DRAFT to COMMITTED")
+ @ApiResponses(value = {@ApiResponse(code = 404, message = "Invoice not found")})
+ public Response commitInvoice(@PathParam("invoiceId") final String invoiceIdString,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final HttpServletRequest request,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws InvoiceApiException {
+
+ final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+ final UUID invoiceId = UUID.fromString(invoiceIdString);
+ invoiceApi.commitInvoice(invoiceId, callContext);
+ return Response.status(Response.Status.OK).build();
+ }
+
@Override
protected ObjectType getObjectType() {
return ObjectType.INVOICE;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index f97c91a..d9de6ef 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -121,6 +121,8 @@ public interface JaxrsResource {
public static final String QUERY_PARALLEL = "parallel";
+ public static final String QUERY_AUTO_COMMIT = "autoCommit";
+
public static final String QUERY_NOTIFICATION_CALLBACK = "cb";
public static final String PAGINATION = "pagination";
@@ -236,6 +238,7 @@ public interface JaxrsResource {
public static final String INVOICE_MP_TEMPLATE = "manualPayTemplate";
public static final String INVOICE_TRANSLATION = "translation";
public static final String INVOICE_CATALOG_TRANSLATION = "catalogTranslation";
+ public static final String COMMIT_INVOICE = "commitInvoice";
public static final String COMBO = "combo";
diff --git a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
index 2a38928..e473117 100644
--- a/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/test/java/org/killbill/billing/jaxrs/json/TestInvoiceJsonWithBundleKeys.java
@@ -21,6 +21,7 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.LocalDate;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -50,9 +51,10 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
final CreditJson creditJson = createCreditJson();
final List<CreditJson> credits = ImmutableList.<CreditJson>of(creditJson);
final List<AuditLogJson> auditLogs = createAuditLogsJson(clock.getUTCNow());
- final InvoiceJson invoiceJsonSimple = new InvoiceJson(amount, Currency.USD.toString(), creditAdj, refundAdj, invoiceId, invoiceDate,
- targetDate, invoiceNumber, balance, accountId, bundleKeys,
- credits, null, auditLogs);
+ final InvoiceJson invoiceJsonSimple = new InvoiceJson(amount, Currency.USD.toString(), InvoiceStatus.COMMITTED.toString(),
+ creditAdj, refundAdj, invoiceId, invoiceDate,
+ targetDate, invoiceNumber, balance, accountId, bundleKeys,
+ credits, null, auditLogs);
Assert.assertEquals(invoiceJsonSimple.getAmount(), amount);
Assert.assertEquals(invoiceJsonSimple.getCreditAdj(), creditAdj);
Assert.assertEquals(invoiceJsonSimple.getRefundAdj(), refundAdj);
@@ -65,6 +67,7 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
Assert.assertEquals(invoiceJsonSimple.getBundleKeys(), bundleKeys);
Assert.assertEquals(invoiceJsonSimple.getCredits(), credits);
Assert.assertEquals(invoiceJsonSimple.getAuditLogs(), auditLogs);
+ Assert.assertEquals(invoiceJsonSimple.getStatus(), InvoiceStatus.COMMITTED.toString());
final String asJson = mapper.writeValueAsString(invoiceJsonSimple);
final InvoiceJson fromJson = mapper.readValue(asJson, InvoiceJson.class);
@@ -84,6 +87,7 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
Mockito.when(invoice.getBalance()).thenReturn(BigDecimal.ZERO);
Mockito.when(invoice.getAccountId()).thenReturn(UUID.randomUUID());
Mockito.when(invoice.getCurrency()).thenReturn(Currency.MXN);
+ Mockito.when(invoice.getStatus()).thenReturn(InvoiceStatus.COMMITTED);
final String bundleKeys = UUID.randomUUID().toString();
@@ -102,6 +106,7 @@ public class TestInvoiceJsonWithBundleKeys extends JaxrsTestSuiteNoDB {
Assert.assertEquals(invoiceJson.getBundleKeys(), bundleKeys);
Assert.assertEquals(invoiceJson.getCredits(), credits);
Assert.assertNull(invoiceJson.getAuditLogs());
+ Assert.assertEquals(invoiceJson.getStatus(), InvoiceStatus.COMMITTED.toString());
}
private CreditJson createCreditJson() {
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
index ce2913e..33d9233 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentControlPluginApi.java
@@ -45,6 +45,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceInternalApi;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoicePayment;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.api.PaymentApiException;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
@@ -250,6 +251,12 @@ public final class InvoicePaymentControlPluginApi implements PaymentControlPlugi
try {
final UUID invoiceId = getInvoiceId(pluginProperties);
final Invoice invoice = rebalanceAndGetInvoice(invoiceId, internalContext);
+
+ if (!InvoiceStatus.COMMITTED.equals(invoice.getStatus())) {
+ // abort payment if the invoice status is not COMMITTED
+ return new DefaultPriorPaymentControlResult(true);
+ }
+
final BigDecimal requestedAmount = validateAndComputePaymentAmount(invoice, paymentControlPluginContext.getAmount(), paymentControlPluginContext.isApiPayment());
final boolean isAborted = requestedAmount.compareTo(BigDecimal.ZERO) == 0;
diff --git a/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java b/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
index 981e974..8bf8640 100644
--- a/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
+++ b/payment/src/test/java/org/killbill/billing/payment/MockInvoice.java
@@ -32,6 +32,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.entity.EntityBase;
+import org.killbill.billing.invoice.api.InvoiceStatus;
public class MockInvoice extends EntityBase implements Invoice {
private final List<InvoiceItem> invoiceItems = new ArrayList<InvoiceItem>();
@@ -42,15 +43,16 @@ public class MockInvoice extends EntityBase implements Invoice {
private final LocalDate targetDate;
private final Currency currency;
private final boolean migrationInvoice;
+ private final InvoiceStatus status;
// used to create a new invoice
public MockInvoice(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency) {
- this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false);
+ this(UUID.randomUUID(), accountId, null, invoiceDate, targetDate, currency, false, InvoiceStatus.COMMITTED);
}
// used to hydrate invoice from persistence layer
public MockInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
- final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
+ final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice, InvoiceStatus status) {
super(invoiceId);
this.accountId = accountId;
this.invoiceNumber = invoiceNumber;
@@ -58,6 +60,7 @@ public class MockInvoice extends EntityBase implements Invoice {
this.targetDate = targetDate;
this.currency = currency;
this.migrationInvoice = isMigrationInvoice;
+ this.status = status;
}
@Override
@@ -194,8 +197,13 @@ public class MockInvoice extends EntityBase implements Invoice {
}
@Override
+ public InvoiceStatus getStatus() {
+ return status;
+ }
+
+ @Override
public String toString() {
- return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + "]";
+ return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + ", status=" + status + "]";
}
@Override
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
index e61057a..5b498cf 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/KillbillClient.java
@@ -173,7 +173,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
final String phone = "81 53 26 56";
// Note: the accountId payload is ignored on account creation
- return new Account(accountId, name, length, externalKey, email, null, currency, null, null, null, timeZone,
+ return new Account(accountId, name, length, externalKey, email, null, currency, null, false, null, timeZone,
address1, address2, postalCode, company, city, state, country, locale, phone, false, false, null, null);
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
index 62dd8b9..66eb996 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestAccountTimeline.java
@@ -42,10 +42,6 @@ import org.killbill.billing.util.audit.ChangeType;
import org.testng.Assert;
import org.testng.annotations.Test;
-import com.google.common.collect.ImmutableList;
-
-import static org.testng.Assert.assertEquals;
-
public class TestAccountTimeline extends TestJaxrsBase {
private static final String PAYMENT_REQUEST_PROCESSOR = "PaymentRequestProcessor";
@@ -83,9 +79,8 @@ public class TestAccountTimeline extends TestJaxrsBase {
final BigDecimal creditAmount = BigDecimal.ONE;
final Credit credit = new Credit();
credit.setAccountId(accountJson.getAccountId());
- credit.setInvoiceId(invoice.getInvoiceId());
credit.setCreditAmount(creditAmount);
- killBillClient.createCredit(credit, createdBy, reason, comment);
+ killBillClient.createCredit(credit, true, createdBy, reason, comment);
// Add refund
final Payment postedPayment = killBillClient.getPaymentsForAccount(accountJson.getAccountId()).get(0);
@@ -191,19 +186,23 @@ public class TestAccountTimeline extends TestJaxrsBase {
final AccountTimeline timeline = killBillClient.getAccountTimeline(accountId, auditLevel);
// Verify invoices
- Assert.assertEquals(timeline.getInvoices().size(), 2);
+ Assert.assertEquals(timeline.getInvoices().size(), 3);
// Verify audits
final List<AuditLog> firstInvoiceAuditLogs = timeline.getInvoices().get(0).getAuditLogs();
final List<AuditLog> secondInvoiceAuditLogs = timeline.getInvoices().get(1).getAuditLogs();
+ final List<AuditLog> thirdInvoiceAuditLogs = timeline.getInvoices().get(2).getAuditLogs();
if (AuditLevel.NONE.equals(auditLevel)) {
Assert.assertEquals(firstInvoiceAuditLogs.size(), 0);
Assert.assertEquals(secondInvoiceAuditLogs.size(), 0);
+ Assert.assertEquals(thirdInvoiceAuditLogs.size(), 0);
} else {
Assert.assertEquals(firstInvoiceAuditLogs.size(), 1);
verifyAuditLog(firstInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
Assert.assertEquals(secondInvoiceAuditLogs.size(), 1);
verifyAuditLog(secondInvoiceAuditLogs.get(0), ChangeType.INSERT, null, null, TRANSITION, startTime, endTime);
+ Assert.assertEquals(thirdInvoiceAuditLogs.size(), 1);
+ verifyAuditLog(thirdInvoiceAuditLogs.get(0), ChangeType.INSERT, reason, comment, createdBy, startTime, endTime);
}
}
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
index 5bc85f0..4ccc863 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCredit.java
@@ -26,6 +26,7 @@ import org.killbill.billing.client.KillBillClientException;
import org.killbill.billing.client.model.Account;
import org.killbill.billing.client.model.Credit;
import org.killbill.billing.client.model.Invoice;
+import org.killbill.billing.invoice.api.InvoiceApiException;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -45,23 +46,40 @@ public class TestCredit extends TestJaxrsBase {
@Test(groups = "slow", description = "Can add a credit to an existing invoice")
public void testAddCreditToInvoice() throws Exception {
- final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
+ //final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
final DateTime effectiveDate = clock.getUTCNow();
final BigDecimal creditAmount = BigDecimal.ONE;
final Credit credit = new Credit();
credit.setAccountId(accountJson.getAccountId());
- credit.setInvoiceId(invoice.getInvoiceId());
credit.setCreditAmount(creditAmount);
- final Credit objFromJson = killBillClient.createCredit(credit, createdBy, reason, comment);
+ Credit objFromJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+
+ final UUID invoiceId = objFromJson.getInvoiceId();
+ credit.setInvoiceId(invoiceId);
+ objFromJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
// We can't just compare the object via .equals() due e.g. to the invoice id
assertEquals(objFromJson.getAccountId(), accountJson.getAccountId());
- assertEquals(objFromJson.getInvoiceId(), invoice.getInvoiceId());
+ assertEquals(objFromJson.getInvoiceId(), invoiceId);
assertEquals(objFromJson.getCreditAmount().compareTo(creditAmount), 0);
assertEquals(objFromJson.getEffectiveDate().compareTo(effectiveDate.toLocalDate()), 0);
}
+ @Test(groups = "slow", description = "Can add a credit to an existing account",
+ expectedExceptions = KillBillClientException.class, expectedExceptionsMessageRegExp = ".*it is already in COMMITTED status")
+ public void testAddCreditToCommittedInvoice() throws Exception {
+ final Invoice invoice = killBillClient.getInvoicesForAccount(accountJson.getAccountId()).get(1);
+
+ final DateTime effectiveDate = clock.getUTCNow();
+ final BigDecimal creditAmount = BigDecimal.ONE;
+ final Credit credit = new Credit();
+ credit.setAccountId(accountJson.getAccountId());
+ credit.setInvoiceId(invoice.getInvoiceId());
+ credit.setCreditAmount(creditAmount);
+ final Credit objFromJson = killBillClient.createCredit(credit, true, createdBy, reason, comment);
+ }
+
@Test(groups = "slow", description = "Cannot add a credit if the account doesn't exist")
public void testAccountDoesNotExist() throws Exception {
final Credit credit = new Credit();
@@ -69,7 +87,7 @@ public class TestCredit extends TestJaxrsBase {
credit.setCreditAmount(BigDecimal.TEN);
// Try to create the credit
- assertNull(killBillClient.createCredit(credit, createdBy, reason, comment));
+ assertNull(killBillClient.createCredit(credit, true, createdBy, reason, comment));
}
@Test(groups = "slow", description = "Cannot credit a badly formatted credit")
@@ -80,7 +98,7 @@ public class TestCredit extends TestJaxrsBase {
// Try to create the credit
try {
- killBillClient.createCredit(credit, createdBy, reason, comment);
+ killBillClient.createCredit(credit, true, createdBy, reason, comment);
fail();
} catch (final KillBillClientException e) {
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
index 4457006..6580618 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestInvoice.java
@@ -32,6 +32,7 @@ import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.client.model.Account;
import org.killbill.billing.client.model.AuditLog;
+import org.killbill.billing.client.model.Credit;
import org.killbill.billing.client.model.Invoice;
import org.killbill.billing.client.model.InvoiceDryRun;
import org.killbill.billing.client.model.InvoiceItem;
@@ -41,6 +42,7 @@ import org.killbill.billing.client.model.Invoices;
import org.killbill.billing.client.model.PaymentMethod;
import org.killbill.billing.entitlement.api.SubscriptionEventType;
import org.killbill.billing.invoice.api.DryRunType;
+import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.payment.provider.ExternalPaymentProviderPlugin;
import org.killbill.billing.util.api.AuditLevel;
import org.testng.Assert;
@@ -340,7 +342,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setDescription(UUID.randomUUID().toString());
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -377,7 +379,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge2.setDescription(UUID.randomUUID().toString());
externalCharges.add(externalCharge2);
- final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCNow(), false, createdBy, reason, comment);
+ final List<InvoiceItem> createdExternalCharges = killBillClient.createExternalCharges(externalCharges, clock.getUTCNow(), false, true, createdBy, reason, comment);
assertEquals(createdExternalCharges.size(), 2);
assertEquals(createdExternalCharges.get(0).getCurrency().toString(), accountJson.getCurrency());
assertEquals(createdExternalCharges.get(1).getCurrency().toString(), accountJson.getCurrency());
@@ -399,7 +401,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAccountId(accountJson.getAccountId());
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(BigDecimal.ZERO), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -424,7 +426,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setBundleId(bundleId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
assertEquals(invoiceWithItems.getItems().size(), 1);
@@ -453,7 +455,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -483,7 +485,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setAmount(chargeAmount);
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), true, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertNull(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId());
@@ -514,7 +516,7 @@ public class TestInvoice extends TestJaxrsBase {
externalCharge.setCurrency(Currency.valueOf(accountJson.getCurrency()));
externalCharge.setInvoiceId(invoiceId);
externalCharge.setBundleId(bundleId);
- final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, createdBy, reason, comment);
+ final InvoiceItem createdExternalCharge = killBillClient.createExternalCharge(externalCharge, clock.getUTCNow(), false, true, createdBy, reason, comment);
final Invoice invoiceWithItems = killBillClient.getInvoice(createdExternalCharge.getInvoiceId(), true);
assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
assertEquals(invoiceWithItems.getItems().get(originalNumberOfItemsForInvoice).getBundleId(), bundleId);
@@ -553,4 +555,28 @@ public class TestInvoice extends TestJaxrsBase {
}
Assert.assertNull(page);
}
+
+ @Test(groups = "slow", description = "Can add a credit to a new invoice")
+ public void testCreateCreditInvoiceAndMoveStatus() throws Exception {
+
+ final Account account = createAccountWithDefaultPaymentMethod();
+
+ final DateTime effectiveDate = clock.getUTCNow();
+ final BigDecimal creditAmount = BigDecimal.TEN;
+ final Credit credit = new Credit();
+ credit.setAccountId(account.getAccountId());
+ credit.setInvoiceId(null);
+ credit.setCreditAmount(creditAmount);
+ final Credit creditJson = killBillClient.createCredit(credit, false, createdBy, reason, comment);
+
+ Invoice invoice = killBillClient.getInvoice(creditJson.getInvoiceId());
+ Assert.assertEquals(invoice.getStatus(), InvoiceStatus.DRAFT.toString());
+
+ killBillClient.commitInvoice(invoice.getInvoiceId(), createdBy, reason, comment);
+
+ invoice = killBillClient.getInvoice(creditJson.getInvoiceId());
+ Assert.assertEquals(invoice.getStatus(), InvoiceStatus.COMMITTED.toString());
+
+ }
+
}