killbill-aplcache
Changes
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java 9(+7 -2)
invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java 6(+6 -0)
Details
diff --git a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
index 9f21b11..bf7fd7b 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/Invoice.java
@@ -57,7 +57,9 @@ public interface Invoice extends Entity {
BigDecimal getAmountPaid();
- BigDecimal getTotalAmount();
+ BigDecimal getAmountCharged();
+
+ BigDecimal getAmountCredited();
BigDecimal getBalance();
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
index cd7decd..9f898c7 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItem.java
@@ -46,5 +46,7 @@ public interface InvoiceItem extends Entity, Comparable<InvoiceItem> {
Currency getCurrency();
+ InvoiceItemType getInvoiceItemType();
+
InvoiceItem asReversingItem();
}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
index ce066a4..aaae9df 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceItemType.java
@@ -23,6 +23,5 @@ public enum InvoiceItemType {
MIGRATION,
REFUND,
CHARGE_BACK,
- ADD_CREDIT,
- USE_CREDIT
+ CREDIT
}
diff --git a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
index 66016df..e0f6ed0 100644
--- a/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
+++ b/api/src/main/java/com/ning/billing/invoice/api/InvoiceUserApi.java
@@ -41,5 +41,5 @@ public interface InvoiceUserApi {
public void tagInvoiceAsWrittenOff(UUID invoiceId, CallContext context);
- public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context);
+ public void tagInvoiceAsNotWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException;
}
diff --git a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
index 16b8a30..fed7517 100644
--- a/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
+++ b/api/src/main/java/com/ning/billing/util/api/TagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.dao.ObjectType;
diff --git a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
index c122766..65d32c7 100644
--- a/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
+++ b/api/src/main/java/com/ning/billing/util/tag/ControlTagType.java
@@ -48,7 +48,9 @@ public enum ControlTagType {
public TagDefinition toTagDefinition() {
return new TagDefinition() {
- @Override public String getName() {return this.toString();}
+ @Override public String getName() {
+ return name();
+ }
@Override public String getDescription() {return description;}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 1e45646..ebb1216 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -85,7 +85,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
- public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) {
+ public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
dao.removeWrittenOff(invoiceId, context);
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
new file mode 100644
index 0000000..d60e813
--- /dev/null
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.invoice.dao;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.CallContextBinder;
+import com.ning.billing.util.entity.dao.EntitySqlDao;
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.BinderFactory;
+import org.skife.jdbi.v2.sqlobject.BindingAnnotation;
+import org.skife.jdbi.v2.sqlobject.SqlBatch;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+
+@ExternalizedSqlViaStringTemplate3
+@RegisterMapper(CreditInvoiceItemSqlDao.CreditInvoiceItemMapper.class)
+public interface CreditInvoiceItemSqlDao extends EntitySqlDao<InvoiceItem> {
+ @SqlQuery
+ List<Long> getRecordIds(@Bind("invoiceId") final String invoiceId);
+
+ @SqlQuery
+ List<InvoiceItem> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId);
+
+ @SqlQuery
+ List<InvoiceItem> getInvoiceItemsByAccount(@Bind("accountId") final String accountId);
+
+ @Override
+ @SqlUpdate
+ void create(@CreditInvoiceItemBinder final InvoiceItem invoiceItem, @CallContextBinder final CallContext context);
+
+ @SqlBatch
+ void create(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+ @SqlBatch(transactional=false)
+ void batchCreateFromTransaction(@CreditInvoiceItemBinder final List<InvoiceItem> items, @CallContextBinder final CallContext context);
+
+ @BindingAnnotation(CreditInvoiceItemBinder.CreditInvoiceItemBinderFactory.class)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.PARAMETER})
+ public @interface CreditInvoiceItemBinder {
+ public static class CreditInvoiceItemBinderFactory implements BinderFactory {
+ public Binder build(Annotation annotation) {
+ return new Binder<CreditInvoiceItemBinder, CreditInvoiceItem>() {
+ public void bind(SQLStatement q, CreditInvoiceItemBinder bind, CreditInvoiceItem item) {
+ q.bind("id", item.getId().toString());
+ q.bind("invoiceId", item.getInvoiceId().toString());
+ q.bind("accountId", item.getAccountId().toString());
+ q.bind("creditDate", item.getStartDate().toDate());
+ q.bind("amount", item.getAmount());
+ q.bind("currency", item.getCurrency().toString());
+ }
+ };
+ }
+ }
+ }
+ public class CreditInvoiceItemMapper implements ResultSetMapper<InvoiceItem> {
+ @Override
+ public InvoiceItem map(int index, ResultSet result, StatementContext ctx) throws SQLException {
+ UUID id = UUID.fromString(result.getString("id"));
+ UUID invoiceId = UUID.fromString(result.getString("invoice_id"));
+ UUID accountId = UUID.fromString(result.getString("account_id"));
+ DateTime creditDate = new DateTime(result.getTimestamp("credit_date"));
+ BigDecimal amount = result.getBigDecimal("amount");
+ Currency currency = Currency.valueOf(result.getString("currency"));
+ return new CreditInvoiceItem(id, invoiceId, accountId, creditDate, amount, currency);
+ }
+ }
+}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 4b262db..3a4ffe2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -21,6 +21,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
import org.joda.time.DateTime;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.Transaction;
@@ -117,16 +119,16 @@ public class DefaultInvoiceDao implements InvoiceDao {
@Override
public Invoice getById(final UUID invoiceId) {
return invoiceSqlDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
- @Override
- public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
- Invoice invoice = invoiceDao.getById(invoiceId.toString());
+ @Override
+ public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
+ Invoice invoice = invoiceDao.getById(invoiceId.toString());
- if (invoice != null) {
- populateChildren(invoice, invoiceDao);
- }
+ if (invoice != null) {
+ populateChildren(invoice, invoiceDao);
+ }
- return invoice;
- }
+ return invoice;
+ }
});
}
@@ -163,6 +165,12 @@ public class DefaultInvoiceDao implements InvoiceDao {
recordIdList = fixedPriceInvoiceItemDao.getRecordIds(invoice.getId().toString());
audits.addAll(createAudits(TableName.FIXED_INVOICE_ITEMS, recordIdList));
+ List<InvoiceItem> creditInvoiceItems = invoice.getInvoiceItems(CreditInvoiceItem.class);
+ CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = transactional.become(CreditInvoiceItemSqlDao.class);
+ creditInvoiceItemSqlDao.batchCreateFromTransaction(creditInvoiceItems, context);
+ recordIdList = creditInvoiceItemSqlDao.getRecordIds(invoice.getId().toString());
+ audits.addAll(createAudits(TableName.CREDIT_INVOICE_ITEMS, recordIdList));
+
List<InvoicePayment> invoicePayments = invoice.getPayments();
InvoicePaymentSqlDao invoicePaymentSqlDao = transactional.become(InvoicePaymentSqlDao.class);
invoicePaymentSqlDao.batchCreateFromTransaction(invoicePayments, context);
@@ -252,7 +260,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
@Override
- public void removeWrittenOff(UUID invoiceId, CallContext context) {
+ public void removeWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException {
tagDao.deleteTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.toTagDefinition(), context);
}
@@ -278,13 +286,19 @@ public class DefaultInvoiceDao implements InvoiceDao {
}
private void getInvoiceItemsWithinTransaction(final Invoice invoice, final InvoiceSqlDao invoiceDao) {
+ String invoiceId = invoice.getId().toString();
+
RecurringInvoiceItemSqlDao recurringInvoiceItemDao = invoiceDao.become(RecurringInvoiceItemSqlDao.class);
- List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+ List<InvoiceItem> recurringInvoiceItems = recurringInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
invoice.addInvoiceItems(recurringInvoiceItems);
FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemDao = invoiceDao.become(FixedPriceInvoiceItemSqlDao.class);
- List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoice.getId().toString());
+ List<InvoiceItem> fixedPriceInvoiceItems = fixedPriceInvoiceItemDao.getInvoiceItemsByInvoice(invoiceId);
invoice.addInvoiceItems(fixedPriceInvoiceItems);
+
+ CreditInvoiceItemSqlDao creditInvoiceItemSqlDao = invoiceDao.become(CreditInvoiceItemSqlDao.class);
+ List<InvoiceItem> creditInvoiceItems = creditInvoiceItemSqlDao.getInvoiceItemsByInvoice(invoiceId);
+ invoice.addInvoiceItems(creditInvoiceItems);
}
private void getInvoicePaymentsWithinTransaction(final List<Invoice> invoices, final InvoiceSqlDao invoiceDao) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index 17006d3..59de677 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -17,6 +17,7 @@
package com.ning.billing.invoice.dao;
import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoicePayment;
import com.ning.billing.util.callcontext.CallContext;
import org.joda.time.DateTime;
@@ -54,6 +55,6 @@ public interface InvoiceDao {
void setWrittenOff(UUID invoiceId, CallContext context);
- void removeWrittenOff(UUID invoiceId, CallContext context);
+ void removeWrittenOff(UUID invoiceId, CallContext context) throws InvoiceApiException;
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
index ac800a7..651a717 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/CreditInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import org.joda.time.DateTime;
import java.math.BigDecimal;
@@ -29,7 +30,7 @@ public class CreditInvoiceItem extends InvoiceItemBase {
}
public CreditInvoiceItem(UUID id, UUID invoiceId, UUID accountId, DateTime date, BigDecimal amount, Currency currency) {
- super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency);
+ super(id, invoiceId, accountId, null, null, null, null, date, date, amount, currency, InvoiceItemType.CREDIT);
}
@Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 0de9aa4..952ff60 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -173,18 +173,24 @@ public class DefaultInvoice extends EntityBase implements Invoice {
}
@Override
- public BigDecimal getTotalAmount() {
- return invoiceItems.getTotalAmount();
+ public BigDecimal getAmountCharged() {
+ return invoiceItems.getAmountCharged();
+ }
+
+ @Override
+ public BigDecimal getAmountCredited() {
+ return invoiceItems.getAmountCredited();
}
@Override
public BigDecimal getBalance() {
- return getTotalAmount().subtract(getAmountPaid());
+ // credits offset payments
+ return getAmountCharged().subtract(getAmountPaid().subtract(getAmountCredited()));
}
@Override
public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
- if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+ if (getBalance().compareTo(BigDecimal.ZERO) == 0) {
return false;
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index f27f4a3..08e19c0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -90,21 +90,16 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
targetDate = adjustTargetDate(existingInvoices, targetDate);
- DefaultInvoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, targetCurrency);
+ Invoice invoice = new DefaultInvoice(accountId, clock.getUTCNow(), targetDate, targetCurrency);
UUID invoiceId = invoice.getId();
List<InvoiceItem> proposedItems = generateInvoiceItems(invoiceId, accountId, events, targetDate, targetCurrency);
removeCancellingInvoiceItems(existingItems);
removeDuplicatedInvoiceItems(proposedItems, existingItems);
- for (InvoiceItem existingItem : existingItems) {
- if (existingItem instanceof RecurringInvoiceItem) {
- RecurringInvoiceItem recurringItem = (RecurringInvoiceItem) existingItem;
- proposedItems.add(recurringItem.asReversingItem());
- }
- }
-
- //addCreditItems(accountId, proposedItems, existingInvoices, targetCurrency);
+ addReversingItems(existingItems, proposedItems);
+ generateCreditsForPastRepairedInvoices(accountId, existingInvoices, proposedItems, targetCurrency);
+ consumeExistingCredit(invoiceId, accountId, existingItems, proposedItems, targetCurrency);
if (proposedItems == null || proposedItems.size() == 0) {
return null;
@@ -115,75 +110,75 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
}
}
- /*
- * ensures that the balance of all invoices are zero or positive, adding an adjusting credit item if needed
- */
- private void addCreditItems(UUID accountId, List<InvoiceItem> invoiceItems, List<Invoice> invoices, Currency currency) {
- Map<UUID, BigDecimal> invoiceBalances = new HashMap<UUID, BigDecimal>();
-
- updateInvoiceBalance(invoiceItems, invoiceBalances);
-
- // add all existing items and payments
- if (invoices != null) {
- for (Invoice invoice : invoices) {
- updateInvoiceBalance(invoice.getInvoiceItems(), invoiceBalances);
- }
+ private void generateCreditsForPastRepairedInvoices(UUID accountId, List<Invoice> existingInvoices, List<InvoiceItem> proposedItems, Currency currency) {
+ // determine most accurate invoice balances up to this point
+ Map<UUID, BigDecimal> amountOwedByInvoice = new HashMap<UUID, BigDecimal>();
- for (Invoice invoice : invoices) {
- UUID invoiceId = invoice.getId();
- invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).subtract(invoice.getAmountPaid()));
+ if (existingInvoices != null) {
+ for (Invoice invoice : existingInvoices) {
+ amountOwedByInvoice.put(invoice.getId(), invoice.getBalance());
}
}
- BigDecimal creditTotal = BigDecimal.ZERO;
-
- for (UUID invoiceId : invoiceBalances.keySet()) {
- BigDecimal balance = invoiceBalances.get(invoiceId);
- if (balance.compareTo(BigDecimal.ZERO) < 0) {
- creditTotal = creditTotal.add(balance.negate());
- invoiceItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), balance, currency));
+ for (InvoiceItem item : proposedItems) {
+ UUID invoiceId = item.getInvoiceId();
+ if (amountOwedByInvoice.containsKey(invoiceId)) {
+ amountOwedByInvoice.put(invoiceId, amountOwedByInvoice.get(invoiceId).add(item.getAmount()));
+ } else {
+ amountOwedByInvoice.put(invoiceId, item.getAmount());
}
}
- if (creditTotal.compareTo(BigDecimal.ZERO) != 0) {
- // create a single credit item to cover all credits
- //invoiceItems.add(new CreditInvoiceItem());
+ for (UUID invoiceId : amountOwedByInvoice.keySet()) {
+ BigDecimal invoiceBalance = amountOwedByInvoice.get(invoiceId);
+ if (invoiceBalance.compareTo(BigDecimal.ZERO) < 0) {
+ proposedItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), invoiceBalance.negate(), currency));
+ }
}
}
- private void updateInvoiceBalance(List<InvoiceItem> items, Map<UUID, BigDecimal> invoiceBalances) {
- for (InvoiceItem item : items) {
- UUID invoiceId = item.getInvoiceId();
-
- if (!invoiceBalances.containsKey(invoiceId)) {
- invoiceBalances.put(invoiceId, BigDecimal.ZERO);
+ private void addReversingItems(List<InvoiceItem> existingItems, List<InvoiceItem> proposedItems) {
+ for (InvoiceItem existingItem : existingItems) {
+ if (existingItem instanceof RecurringInvoiceItem) {
+ RecurringInvoiceItem recurringItem = (RecurringInvoiceItem) existingItem;
+ proposedItems.add(recurringItem.asReversingItem());
}
-
- invoiceBalances.put(invoiceId, invoiceBalances.get(invoiceId).add(item.getAmount()));
}
}
- @Override
- public void distributeItems(List<Invoice> invoices) {
- Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
+ private void consumeExistingCredit(UUID invoiceId, UUID accountId, List<InvoiceItem> existingItems,
+ List<InvoiceItem> proposedItems, Currency targetCurrency) {
+ BigDecimal totalUnusedCreditAmount = BigDecimal.ZERO;
+ BigDecimal totalAmountOwed = BigDecimal.ZERO;
- for (Invoice invoice : invoices) {
- invoiceMap.put(invoice.getId(), invoice);
+ for (InvoiceItem item : existingItems) {
+ if (item instanceof CreditInvoiceItem) {
+ totalUnusedCreditAmount = totalUnusedCreditAmount.add(item.getAmount());
+ }
}
- for (final Invoice invoice: invoices) {
- Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
- final UUID invoiceId = invoice.getId();
-
- while (itemIterator.hasNext()) {
- InvoiceItem item = itemIterator.next();
+ for (InvoiceItem item : proposedItems) {
+ if (item instanceof CreditInvoiceItem) {
+ totalUnusedCreditAmount = totalUnusedCreditAmount.add(item.getAmount());
+ } else {
+ totalAmountOwed = totalAmountOwed.add(item.getAmount());
+ }
+ }
- if (!item.getInvoiceId().equals(invoiceId)) {
- invoiceMap.get(item.getInvoiceId()).addInvoiceItem(item);
- itemIterator.remove();
- }
+ // credits are positive when they reduce the amount owed (since they offset payment)
+ // the credit balance should never be negative
+ BigDecimal creditAmount = BigDecimal.ZERO;
+ if (totalUnusedCreditAmount.compareTo(BigDecimal.ZERO) > 0) {
+ if (totalAmountOwed.abs().compareTo(totalUnusedCreditAmount.abs()) > 0) {
+ creditAmount = totalUnusedCreditAmount.negate();
+ } else {
+ creditAmount = totalAmountOwed;
}
}
+
+ if (creditAmount.compareTo(BigDecimal.ZERO) < 0) {
+ proposedItems.add(new CreditInvoiceItem(invoiceId, accountId, clock.getUTCNow(), creditAmount, targetCurrency));
+ }
}
private void validateTargetDate(DateTime targetDate) throws InvoiceApiException {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
index b5a79ab..60a39a6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/FixedPriceInvoiceItem.java
@@ -19,6 +19,7 @@ package com.ning.billing.invoice.model;
import java.math.BigDecimal;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceItemType;
import org.joda.time.DateTime;
import com.ning.billing.catalog.api.Currency;
@@ -28,12 +29,12 @@ public class FixedPriceInvoiceItem extends InvoiceItemBase {
public FixedPriceInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
- super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+ super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
}
public FixedPriceInvoiceItem(UUID id, UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
- super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+ super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.FIXED);
}
@Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
index e1c7e00..0b47099 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceGenerator.java
@@ -31,6 +31,4 @@ import com.ning.billing.junction.api.BillingEventSet;
public interface InvoiceGenerator {
public Invoice generateInvoice(UUID accountId, @Nullable BillingEventSet events, @Nullable List<Invoice> existingInvoices,
DateTime targetDate, Currency targetCurrency) throws InvoiceApiException;
-
- public void distributeItems(List<Invoice> invoices);
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
index 5ccaa8c..016184d 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemBase.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.util.entity.EntityBase;
import org.joda.time.DateTime;
@@ -36,16 +37,18 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem
protected final DateTime endDate;
protected final BigDecimal amount;
protected final Currency currency;
+ protected InvoiceItemType invoiceItemType;
public InvoiceItemBase(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
- DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+ DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency, InvoiceItemType invoiceItemType) {
this(UUID.randomUUID(), invoiceId, accountId, bundleId, subscriptionId, planName, phaseName,
- startDate, endDate, amount, currency);
+ startDate, endDate, amount, currency, invoiceItemType);
}
public InvoiceItemBase(UUID id, UUID invoiceId, UUID accountId, @Nullable UUID bundleId,
@Nullable UUID subscriptionId, String planName, String phaseName,
- DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency) {
+ DateTime startDate, DateTime endDate, BigDecimal amount, Currency currency,
+ InvoiceItemType invoiceItemType) {
super(id);
this.invoiceId = invoiceId;
this.accountId = accountId;
@@ -57,6 +60,7 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem
this.endDate = endDate;
this.amount = amount;
this.currency = currency;
+ this.invoiceItemType = invoiceItemType;
}
@Override
@@ -109,6 +113,11 @@ public abstract class InvoiceItemBase extends EntityBase implements InvoiceItem
}
@Override
+ public InvoiceItemType getInvoiceItemType() {
+ return invoiceItemType;
+ }
+
+ @Override
public abstract InvoiceItem asReversingItem();
@Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
index 683bbaf..357c7a8 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/InvoiceItemList.java
@@ -34,55 +34,33 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
this.addAll(invoiceItems);
}
- public BigDecimal getTotalAmount() {
+ public BigDecimal getAmountCharged() {
// naive implementation, assumes all invoice items share the same currency
BigDecimal total = BigDecimal.ZERO.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
for (final InvoiceItem item : this) {
- if (item.getAmount() != null) {
- total = total.add(item.getAmount());
+ if (!(item instanceof CreditInvoiceItem)) {
+ if (item.getAmount() != null) {
+ total = total.add(item.getAmount());
+ }
}
}
return total.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
}
-// public void removeCancellingPairs() {
-// List<InvoiceItem> itemsToRemove = new ArrayList<InvoiceItem>();
-//
-// for (int firstItemIndex = 0; firstItemIndex < this.size(); firstItemIndex++) {
-// for (int secondItemIndex = firstItemIndex + 1; secondItemIndex < this.size(); secondItemIndex++) {
-// InvoiceItem firstItem = this.get(firstItemIndex);
-// InvoiceItem secondItem = this.get(secondItemIndex);
-// if (firstItem.cancels(secondItem)) {
-// itemsToRemove.add(firstItem);
-// itemsToRemove.add(secondItem);
-// }
-// }
-// }
-//
-// this.removeAll(itemsToRemove);
-// }
+ public BigDecimal getAmountCredited() {
+ // naive implementation, assumes all invoice items share the same currency
+ BigDecimal total = BigDecimal.ZERO.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+
+ for (final InvoiceItem item : this) {
+ if (item instanceof CreditInvoiceItem) {
+ if (item.getAmount() != null) {
+ total = total.add(item.getAmount());
+ }
+ }
+ }
-// /*
-// * removes recurring items from the list that have a recurring amount of zero, but a recurring rate that is not zero
-// */
-// public void cleanupDuplicatedItems() {
-// Iterator<InvoiceItem> iterator = this.iterator();
-// while (iterator.hasNext()) {
-// InvoiceItem item = iterator.next();
-//
-// if (item instanceof RecurringInvoiceItem) {
-// RecurringInvoiceItem that = (RecurringInvoiceItem) item;
-// boolean recurringRateNull = (that.getRate() == null);
-// boolean recurringAmountZero = (that.getAmount() !=null) && (that.getAmount().compareTo(BigDecimal.ZERO) == 0);
-//
-// if (recurringRateNull || recurringAmountZero) {
-// iterator.remove();
-// } else if (that.getEndDate() != null && that.getStartDate().compareTo(that.getEndDate()) == 0) {
-// iterator.remove();
-// }
-// }
-// }
-// }
+ return total.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
+ }
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
index bc813fc..9dbbae0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/RecurringInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.model;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import org.joda.time.DateTime;
import java.math.BigDecimal;
@@ -30,8 +31,8 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
public RecurringInvoiceItem(UUID invoiceId, UUID accountId, UUID bundleId, UUID subscriptionId, String planName, String phaseName,
DateTime startDate, DateTime endDate,
BigDecimal amount, BigDecimal rate,
- Currency currency) {
- super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+ Currency currency) {
+ super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
this.rate = rate;
this.reversedItemId = null;
}
@@ -41,7 +42,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
BigDecimal amount, BigDecimal rate,
Currency currency, UUID reversedItemId) {
super(invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate,
- amount, currency);
+ amount, currency, InvoiceItemType.REVERSAL);
this.rate = rate;
this.reversedItemId = reversedItemId;
}
@@ -51,7 +52,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
DateTime startDate, DateTime endDate,
BigDecimal amount, BigDecimal rate,
Currency currency) {
- super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+ super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.RECURRING);
this.rate = rate;
this.reversedItemId = null;
}
@@ -61,7 +62,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
DateTime startDate, DateTime endDate,
BigDecimal amount, BigDecimal rate,
Currency currency, UUID reversedItemId) {
- super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency);
+ super(id, invoiceId, accountId, bundleId, subscriptionId, planName, phaseName, startDate, endDate, amount, currency, InvoiceItemType.REVERSAL);
this.rate = rate;
this.reversedItemId = reversedItemId;
}
@@ -132,6 +133,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
RecurringInvoiceItem that = (RecurringInvoiceItem) o;
+ // do not include invoice item type, since a reversing item can be equal to the original item
if (accountId.compareTo(that.accountId) != 0) return false;
if (amount.compareTo(that.amount) != 0) return false;
if (currency != that.currency) return false;
@@ -162,6 +164,7 @@ public class RecurringInvoiceItem extends InvoiceItemBase {
result = 31 * result + amount.hashCode();
result = 31 * result + rate.hashCode();
result = 31 * result + currency.hashCode();
+ result = 31 * result + invoiceItemType.hashCode();
result = 31 * result + (reversedItemId != null ? reversedItemId.hashCode() : 0);
return result;
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
index ed50d2e..1ca7824 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceFormatter.java
@@ -120,8 +120,13 @@ public class DefaultInvoiceFormatter implements InvoiceFormatter {
}
@Override
- public BigDecimal getTotalAmount() {
- return invoice.getTotalAmount();
+ public BigDecimal getAmountCharged() {
+ return invoice.getAmountCharged();
+ }
+
+ @Override
+ public BigDecimal getAmountCredited() {
+ return invoice.getAmountCredited();
}
@Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
index f1a2e8d..9f49eb5 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/template/formatters/DefaultInvoiceItemFormatter.java
@@ -18,6 +18,7 @@ package com.ning.billing.invoice.template.formatters;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.invoice.api.formatters.InvoiceItemFormatter;
import com.ning.billing.util.template.translation.DefaultCatalogTranslator;
import com.ning.billing.util.template.translation.Translator;
@@ -55,6 +56,11 @@ public class DefaultInvoiceItemFormatter implements InvoiceItemFormatter {
}
@Override
+ public InvoiceItemType getInvoiceItemType() {
+ return item.getInvoiceItemType();
+ }
+
+ @Override
public InvoiceItem asReversingItem() {
return item.asReversingItem();
}
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
new file mode 100644
index 0000000..d3bef75
--- /dev/null
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/CreditInvoiceItemSqlDao.sql.stg
@@ -0,0 +1,69 @@
+group CreditInvoiceItemSqlDao;
+
+fields(prefix) ::= <<
+ <prefix>id,
+ <prefix>invoice_id,
+ <prefix>account_id,
+ <prefix>credit_date,
+ <prefix>amount,
+ <prefix>currency,
+ <prefix>created_by,
+ <prefix>created_date
+>>
+
+getById() ::= <<
+ SELECT <fields()>
+ FROM credit_invoice_items
+ WHERE id = :id;
+>>
+
+getInvoiceItemsByInvoice() ::= <<
+ SELECT <fields()>
+ FROM credit_invoice_items
+ WHERE invoice_id = :invoiceId;
+>>
+
+getInvoiceItemsByAccount() ::= <<
+ SELECT <fields("cii.")>
+ FROM credit_invoice_items cii
+ INNER JOIN invoices i ON i.id = cii.invoice_id
+ WHERE i.account_id = :accountId;
+>>
+
+create() ::= <<
+ INSERT INTO credit_invoice_items(<fields()>)
+ VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+batchCreateFromTransaction() ::= <<
+ INSERT INTO credit_invoice_items(<fields()>)
+ VALUES(:id, :invoiceId, :accountId, :creditDate, :amount, :currency, :userName, :createdDate);
+>>
+
+getRecordIds() ::= <<
+ SELECT record_id, id
+ FROM credit_invoice_items
+ WHERE invoice_id = :invoiceId;
+>>
+
+auditFields(prefix) ::= <<
+ <prefix>table_name,
+ <prefix>record_id,
+ <prefix>change_type,
+ <prefix>change_date,
+ <prefix>changed_by,
+ <prefix>reason_code,
+ <prefix>comments,
+ <prefix>user_token
+>>
+
+insertAuditFromTransaction() ::= <<
+ INSERT INTO audit_log(<auditFields()>)
+ VALUES(:tableName, :recordId, :changeType, :createdDate, :userName, :reasonCode, :comment, :userToken);
+>>
+
+test() ::= <<
+ SELECT 1
+ FROM credit_invoice_items;
+>>
+;
\ No newline at end of file
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 6db5db5..fe942a8 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -39,11 +39,11 @@ getInvoicesByAccountAfterDate() ::= <<
>>
getInvoicesBySubscription() ::= <<
- SELECT record_id as invoice_number, <invoiceFields("i.")>
+ SELECT i.record_id as invoice_number, <invoiceFields("i.")>
FROM invoices i
LEFT JOIN recurring_invoice_items rii ON i.id = rii.invoice_id
- WHERE rii.subscription_id = :subscriptionId AND migrated = 'FALSE'
- GROUP BY record_id as invoice_number, <invoiceFields("i.")>;
+ WHERE rii.subscription_id = :subscriptionId AND migrated = 'FALSE'
+ GROUP BY i.record_id, <invoiceFields("i.")>;
>>
getById() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
index 8bda35c..061d8a0 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -45,6 +45,22 @@ CREATE UNIQUE INDEX fixed_invoice_items_id ON fixed_invoice_items(id);
CREATE INDEX fixed_invoice_items_subscription_id ON fixed_invoice_items(subscription_id ASC);
CREATE INDEX fixed_invoice_items_invoice_id ON fixed_invoice_items(invoice_id ASC);
+DROP TABLE IF EXISTS credit_invoice_items;
+CREATE TABLE credit_invoice_items (
+ record_id int(11) unsigned NOT NULL AUTO_INCREMENT,
+ id char(36) NOT NULL,
+ invoice_id char(36) NOT NULL,
+ account_id char(36) NOT NULL,
+ credit_date datetime NOT NULL,
+ amount numeric(10,4) NULL,
+ currency char(3) NOT NULL,
+ created_by varchar(50) NOT NULL,
+ created_date datetime NOT NULL,
+ PRIMARY KEY(record_id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX credit_invoice_items_id ON credit_invoice_items(id);
+CREATE INDEX credit_invoice_items_invoice_id ON credit_invoice_items(invoice_id ASC);
+
DROP TABLE IF EXISTS invoice_locking;
DROP TABLE IF EXISTS invoices;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
index 0e99dab..afba58b 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTestBase.java
@@ -20,6 +20,14 @@ import static org.testng.Assert.assertTrue;
import java.io.IOException;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.invoice.notification.MockNextBillingDatePoster;
+import com.ning.billing.invoice.notification.NextBillingDatePoster;
+import com.ning.billing.util.callcontext.TestCallContext;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.tag.dao.AuditedTagDao;
+import com.ning.billing.util.tag.dao.MockTagDao;
+import com.ning.billing.util.tag.dao.TagDao;
import org.apache.commons.io.IOUtils;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
@@ -29,28 +37,21 @@ import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Stage;
import com.ning.billing.config.InvoiceConfig;
-import com.ning.billing.invoice.glue.InvoiceModuleWithEmbeddedDb;
import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
import com.ning.billing.invoice.model.InvoiceGenerator;
import com.ning.billing.invoice.tests.InvoicingTestBase;
-import com.ning.billing.util.bus.BusService;
-import com.ning.billing.util.bus.DefaultBusService;
import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.util.callcontext.CallOrigin;
-import com.ning.billing.util.callcontext.DefaultCallContextFactory;
-import com.ning.billing.util.callcontext.UserType;
import com.ning.billing.util.clock.Clock;
public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
protected IDBI dbi;
+ private MysqlTestingHelper mysqlTestingHelper;
protected InvoiceDao invoiceDao;
protected RecurringInvoiceItemSqlDao recurringInvoiceItemDao;
+ protected FixedPriceInvoiceItemSqlDao fixedPriceInvoiceItemSqlDao;
+ protected CreditInvoiceItemSqlDao creditInvoiceItemSqlDao;
protected InvoicePaymentSqlDao invoicePaymentDao;
- protected InvoiceModuleWithEmbeddedDb module;
protected Clock clock;
protected CallContext context;
protected InvoiceGenerator generator;
@@ -66,44 +67,44 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
@BeforeClass(alwaysRun = true)
protected void setup() throws IOException {
- module = new InvoiceModuleWithEmbeddedDb();
- dbi = module.getDbi();
+ mysqlTestingHelper = new MysqlTestingHelper();
+ dbi = mysqlTestingHelper.getDBI();
final String invoiceDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
final String utilDdl = IOUtils.toString(DefaultInvoiceDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
- module.startDb();
- module.initDb(invoiceDdl);
- module.initDb(utilDdl);
+ mysqlTestingHelper.startMysql();
+ mysqlTestingHelper.initDb(invoiceDdl);
+ mysqlTestingHelper.initDb(utilDdl);
- final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
-
- invoiceDao = injector.getInstance(InvoiceDao.class);
+ NextBillingDatePoster nextBillingDatePoster = new MockNextBillingDatePoster();
+ TagDao tagDao = new AuditedTagDao(dbi);
+ invoiceDao = new DefaultInvoiceDao(dbi, nextBillingDatePoster, tagDao);
invoiceDao.test();
- recurringInvoiceItemDao = module.getInvoiceItemSqlDao();
+ recurringInvoiceItemDao = dbi.onDemand(RecurringInvoiceItemSqlDao.class);
+ fixedPriceInvoiceItemSqlDao = dbi.onDemand(FixedPriceInvoiceItemSqlDao.class);
+ creditInvoiceItemSqlDao = dbi.onDemand(CreditInvoiceItemSqlDao.class);
+ invoicePaymentDao = dbi.onDemand(InvoicePaymentSqlDao.class);
- invoicePaymentDao = module.getInvoicePaymentSqlDao();
- clock = injector.getInstance(Clock.class);
- context = new DefaultCallContextFactory(clock).createCallContext("Count Rogan", CallOrigin.TEST, UserType.TEST);
+ clock = new ClockMock();
+ context = new TestCallContext("Invoice Dao Tests");
generator = new DefaultInvoiceGenerator(clock, invoiceConfig);
- BusService busService = injector.getInstance(BusService.class);
- ((DefaultBusService) busService).startBus();
-
assertTrue(true);
-
}
@BeforeMethod(alwaysRun = true)
public void cleanupData() {
- module.getDbi().inTransaction(new TransactionCallback<Void>() {
+ dbi.inTransaction(new TransactionCallback<Void>() {
@Override
public Void inTransaction(Handle h, TransactionStatus status)
throws Exception {
h.execute("truncate table invoices");
h.execute("truncate table fixed_invoice_items");
h.execute("truncate table recurring_invoice_items");
+ h.execute("truncate table credit_invoice_items");
+ h.execute("truncate table invoice_payments");
return null;
}
@@ -112,7 +113,7 @@ public abstract class InvoiceDaoTestBase extends InvoicingTestBase {
@AfterClass(alwaysRun = true)
protected void tearDown() {
- module.stopDb();
+ mysqlTestingHelper.stopMysql();
assertTrue(true);
}
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index e47e152..97d7532 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -80,7 +80,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
assertTrue(thisInvoice.getInvoiceDate().compareTo(invoiceDate) == 0);
assertEquals(thisInvoice.getCurrency(), Currency.USD);
assertEquals(thisInvoice.getNumberOfItems(), 0);
- assertTrue(thisInvoice.getTotalAmount().compareTo(BigDecimal.ZERO) == 0);
+ assertTrue(thisInvoice.getBalance().compareTo(BigDecimal.ZERO) == 0);
}
@Test
@@ -100,7 +100,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice savedInvoice = invoiceDao.getById(invoiceId);
assertNotNull(savedInvoice);
- assertEquals(savedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
+ assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
assertEquals(savedInvoice.getBalance().compareTo(new BigDecimal("21.00")), 0);
assertEquals(savedInvoice.getAmountPaid(), BigDecimal.ZERO);
assertEquals(savedInvoice.getInvoiceItems().size(), 1);
@@ -113,7 +113,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice retrievedInvoice = invoiceDao.getById(invoiceId);
assertNotNull(retrievedInvoice);
assertEquals(retrievedInvoice.getInvoiceItems().size(), 1);
- assertEquals(retrievedInvoice.getTotalAmount().compareTo(new BigDecimal("21.00")), 0);
+ assertEquals(retrievedInvoice.getAmountCharged().compareTo(new BigDecimal("21.00")), 0);
assertEquals(retrievedInvoice.getBalance().compareTo(new BigDecimal("10.00")), 0);
assertEquals(retrievedInvoice.getAmountPaid().compareTo(new BigDecimal("11.00")), 0);
}
@@ -447,10 +447,10 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
invoiceDao.create(invoice2, context);
Invoice savedInvoice1 = invoiceDao.getById(invoice1.getId());
- assertEquals(savedInvoice1.getTotalAmount(), ZERO);
+ assertEquals(savedInvoice1.getBalance(), ZERO);
Invoice savedInvoice2 = invoiceDao.getById(invoice2.getId());
- assertEquals(savedInvoice2.getTotalAmount(), FIFTEEN);
+ assertEquals(savedInvoice2.getBalance(), FIFTEEN);
}
@Test
@@ -475,7 +475,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
// expect one pro-ration item and one full-period item
assertEquals(invoice.getNumberOfItems(), 2);
- assertEquals(invoice.getTotalAmount().compareTo(ZERO), 0);
+ assertEquals(invoice.getBalance().compareTo(ZERO), 0);
}
private Subscription getZombieSubscription() {
@@ -513,7 +513,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice invoice1 = generator.generateInvoice(accountId, events, null, effectiveDate1, Currency.USD);
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
- assertEquals(invoice1.getTotalAmount().compareTo(ZERO), 0);
+ assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
@@ -527,7 +527,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate2, Currency.USD);
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
- assertEquals(invoice2.getTotalAmount().compareTo(cheapAmount), 0);
+ assertEquals(invoice2.getBalance().compareTo(cheapAmount), 0);
invoiceList.add(invoice2);
@@ -535,7 +535,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice invoice3 = generator.generateInvoice(accountId, events, invoiceList, effectiveDate3, Currency.USD);
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 1);
- assertEquals(invoice3.getTotalAmount().compareTo(cheapAmount), 0);
+ assertEquals(invoice3.getBalance().compareTo(cheapAmount), 0);
}
@Test
@@ -578,14 +578,14 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
Invoice invoice = generator.generateInvoice(UUID.randomUUID(), events, null, effectiveDate2, Currency.USD);
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
- assertEquals(invoice.getTotalAmount().compareTo(cheapAmount), 0);
+ assertEquals(invoice.getBalance().compareTo(cheapAmount), 0);
invoiceDao.create(invoice, context);
Invoice savedInvoice = invoiceDao.getById(invoice.getId());
assertNotNull(savedInvoice);
assertEquals(savedInvoice.getNumberOfItems(), 2);
- assertEquals(savedInvoice.getTotalAmount().compareTo(cheapAmount), 0);
+ assertEquals(savedInvoice.getBalance().compareTo(cheapAmount), 0);
}
@Test
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 9acde8e..c97e775 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -18,11 +18,15 @@ package com.ning.billing.invoice.dao;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
+import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
@@ -31,9 +35,9 @@ import com.ning.billing.invoice.api.InvoiceItem;
import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
-@Test(groups = {"invoicing", "invoicing-invoiceDao"})
+@Test(groups = {"slow", "invoicing", "invoicing-invoiceDao"})
public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
- @Test(groups = "slow")
+ @Test
public void testInvoiceItemCreation() {
UUID accountId = UUID.randomUUID();
UUID invoiceId = UUID.randomUUID();
@@ -52,8 +56,8 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
assertEquals(thisItem.getId(), item.getId());
assertEquals(thisItem.getInvoiceId(), item.getInvoiceId());
assertEquals(thisItem.getSubscriptionId(), item.getSubscriptionId());
- assertEquals(thisItem.getStartDate(), item.getStartDate());
- assertEquals(thisItem.getEndDate(), item.getEndDate());
+ assertTrue(thisItem.getStartDate().compareTo(item.getStartDate()) == 0);
+ assertTrue(thisItem.getEndDate().compareTo(item.getEndDate()) == 0);
assertEquals(thisItem.getAmount().compareTo(item.getRate()), 0);
assertEquals(thisItem.getRate().compareTo(item.getRate()), 0);
assertEquals(thisItem.getCurrency(), item.getCurrency());
@@ -61,7 +65,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
// assertEquals(thisItem.getCreatedDate().compareTo(item.getCreatedDate()), 0);
}
- @Test(groups = "slow")
+ @Test
public void testGetInvoiceItemsBySubscriptionId() {
UUID accountId = UUID.randomUUID();
UUID subscriptionId = UUID.randomUUID();
@@ -82,7 +86,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
assertEquals(items.size(), 3);
}
- @Test(groups = "slow")
+ @Test
public void testGetInvoiceItemsByInvoiceId() {
UUID accountId = UUID.randomUUID();
UUID invoiceId = UUID.randomUUID();
@@ -104,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
assertEquals(items.size(), 5);
}
- @Test(groups = "slow")
+ @Test
public void testGetInvoiceItemsByAccountId() {
UUID accountId = UUID.randomUUID();
UUID bundleId = UUID.randomUUID();
@@ -127,4 +131,31 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
List<InvoiceItem> items = recurringInvoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
assertEquals(items.size(), 1);
}
+
+ @Test
+ public void testCreditInvoiceSqlDao() {
+ UUID invoiceId = UUID.randomUUID();
+ UUID accountId = UUID.randomUUID();
+ DateTime creditDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+ InvoiceItem creditInvoiceItem = new CreditInvoiceItem(invoiceId, accountId, creditDate, TEN, Currency.USD);
+ creditInvoiceItemSqlDao.create(creditInvoiceItem, context);
+
+ InvoiceItem savedItem = creditInvoiceItemSqlDao.getById(creditInvoiceItem.getId().toString());
+ assertEquals(savedItem, creditInvoiceItem);
+ }
+
+ @Test
+ public void testFixedPriceInvoiceSqlDao() {
+ UUID invoiceId = UUID.randomUUID();
+ UUID accountId = UUID.randomUUID();
+ DateTime startDate = new DateTime(2012, 4, 1, 0, 10, 22, 0);
+
+ InvoiceItem fixedPriceInvoiceItem = new FixedPriceInvoiceItem(invoiceId, accountId, UUID.randomUUID(),
+ UUID.randomUUID(), "test plan", "test phase", startDate, startDate.plusMonths(1), TEN, Currency.USD);
+ fixedPriceInvoiceItemSqlDao.create(fixedPriceInvoiceItem, context);
+
+ InvoiceItem savedItem = fixedPriceInvoiceItemSqlDao.getById(fixedPriceInvoiceItem.getId().toString());
+ assertEquals(savedItem, fixedPriceInvoiceItem);
+ }
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
index 95f39c9..144fb13 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/HtmlInvoiceGeneratorTest.java
@@ -90,7 +90,7 @@ public class HtmlInvoiceGeneratorTest {
zombie.addResult("getInvoiceDate", startDate);
zombie.addResult("getInvoiceNumber", 42);
zombie.addResult("getCurrency", Currency.USD);
- zombie.addResult("getTotalAmount", price1.add(price2));
+ zombie.addResult("getAmountCharged", price1.add(price2));
zombie.addResult("getAmountPaid", BigDecimal.ZERO);
zombie.addResult("getBalance", price1.add(price2));
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
index f1a9550..e8002f0 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
@@ -30,7 +30,11 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
private boolean isAccountInvoiceOff;
private List<UUID> subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
-
+
+ public void addSubscriptionWithAutoInvoiceOff(UUID subscriptionId) {
+ subscriptionIdsWithAutoInvoiceOff.add(subscriptionId);
+ }
+
@Override
public boolean isLast(BillingEvent event) {
return event == last();
@@ -54,4 +58,7 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
}
+ public void clearSubscriptionsWithAutoInvoiceOff() {
+ subscriptionIdsWithAutoInvoiceOff.clear();
+ }
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index b01473f..8f381bc 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -19,11 +19,14 @@ package com.ning.billing.invoice.tests;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
-import static org.testng.Assert.fail;
+import static org.testng.Assert.assertTrue;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.annotation.Nullable;
@@ -49,7 +52,10 @@ import com.ning.billing.entitlement.api.user.Subscription;
import com.ning.billing.invoice.MockBillingEventSet;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.model.CreditInvoiceItem;
import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
+import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.DefaultInvoicePayment;
import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
import com.ning.billing.invoice.model.InvoiceGenerator;
@@ -97,7 +103,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@Test
public void testWithEmptyEventSet() throws InvoiceApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
UUID accountId = UUID.randomUUID();
Invoice invoice = generator.generateInvoice(accountId, events, null, new DateTime(), Currency.USD);
@@ -107,7 +113,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@Test
public void testWithSingleMonthlyEvent() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
@@ -125,7 +131,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
- assertEquals(invoice.getTotalAmount(), TWENTY);
+ assertEquals(invoice.getBalance(), TWENTY);
assertEquals(invoice.getInvoiceItems().get(0).getSubscriptionId(), sub.getId());
}
@@ -143,7 +149,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@Test
public void testWithSingleMonthlyEventWithLeadingProRation() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
@@ -164,12 +170,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal expectedNumberOfBillingCycles;
expectedNumberOfBillingCycles = ONE.add(FOURTEEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD));
BigDecimal expectedAmount = expectedNumberOfBillingCycles.multiply(rate).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
- assertEquals(invoice.getTotalAmount(), expectedAmount);
+ assertEquals(invoice.getBalance(), expectedAmount);
}
@Test
public void testTwoMonthlySubscriptionsWithAlignedBillingDates() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Plan plan1 = new MockPlan();
BigDecimal rate1 = FIVE;
@@ -193,12 +199,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 2);
- assertEquals(invoice.getTotalAmount(), rate1.add(rate2).setScale(NUMBER_OF_DECIMALS));
+ assertEquals(invoice.getBalance(), rate1.add(rate2).setScale(NUMBER_OF_DECIMALS));
}
@Test
public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Plan plan1 = new MockPlan();
BigDecimal rate1 = FIVE;
@@ -230,12 +236,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
expectedValue = expectedValue.add(numberOfCyclesEvent2.multiply(rate2));
expectedValue = expectedValue.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
- assertEquals(invoice.getTotalAmount(), expectedValue);
+ assertEquals(invoice.getBalance(), expectedValue);
}
@Test
public void testOnePlan_ThreeMonthlyPhases_ChangeEOT() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Plan plan1 = new MockPlan();
BigDecimal rate1 = FIVE;
@@ -261,12 +267,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), 4);
- assertEquals(invoice.getTotalAmount(), rate1.add(rate2).add(TWO.multiply(rate3)).setScale(NUMBER_OF_DECIMALS));
+ assertEquals(invoice.getBalance(), rate1.add(rate2).add(TWO.multiply(rate3)).setScale(NUMBER_OF_DECIMALS));
}
@Test
public void testSingleEventWithExistingInvoice() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Subscription sub = createZombieSubscription();
DateTime startDate = buildDateTime(2011, 9, 1);
@@ -290,6 +296,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNull(invoice2);
}
+ // TODO: modify this test to keep a running total of expected invoice amount over time
@Test
public void testMultiplePlansWithUtterChaos() throws InvoiceApiException, CatalogApiException {
// plan 1: change of phase from trial to discount followed by immediate cancellation; (covers phase change, cancel, pro-ration)
@@ -342,7 +349,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal expectedAmount;
List<Invoice> invoices = new ArrayList<Invoice>();
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
// on 1/5/2011, create subscription 1 (trial)
events.add(createBillingEvent(subscriptionId1, plan1StartDate, plan1, plan1Phase1, 5));
@@ -373,7 +380,8 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
// on 4/29/2011, cancel subscription 1
events.add(createBillingEvent(subscriptionId1, plan1CancelDate, plan1, plan1Phase3, 5));
- expectedAmount = TWELVE.multiply(SIX.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).negate().setScale(NUMBER_OF_DECIMALS);
+ // previous invoices are adjusted; this is the pro-ration amount only
+ expectedAmount = TWELVE.multiply(TWENTY_FOUR.divide(THIRTY, NUMBER_OF_DECIMALS, ROUNDING_METHOD)).setScale(NUMBER_OF_DECIMALS);
testInvoiceGeneration(accountId, events, invoices, plan1CancelDate, 2, expectedAmount);
// on 5/10/2011, invoice subscription 2 (trial)
@@ -418,8 +426,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
// on 7/31/2011, convert subscription 3 to annual
events.add(createBillingEvent(subscriptionId3, plan3UpgradeToAnnualDate, plan3, plan3Phase2, 31));
- expectedAmount = ONE_HUNDRED.subtract(TEN);
- expectedAmount = expectedAmount.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
+ expectedAmount = ONE_HUNDRED.add(TEN.multiply(ELEVEN.divide(THIRTY_ONE, 2 * NUMBER_OF_DECIMALS, ROUNDING_METHOD)));
expectedAmount = expectedAmount.setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD);
testInvoiceGeneration(accountId, events, invoices, plan3UpgradeToAnnualDate, 3, expectedAmount);
@@ -443,7 +450,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
// on 10/7/2011, invoice subscription 4 (plan 2), cancel subscription 5
events.add(createBillingEvent(subscriptionId5, plan5CancelDate, plan5, plan5Phase2, 10));
- expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(THREE.divide(THIRTY)).negate().setScale(NUMBER_OF_DECIMALS));
+ expectedAmount = TWENTY_FOUR.add(TWENTY.multiply(TWENTY_SEVEN.divide(THIRTY)).setScale(NUMBER_OF_DECIMALS));
testInvoiceGeneration(accountId, events, invoices, plan5CancelDate, 3, expectedAmount);
// on 10/10/2011, invoice plan 2 (evergreen)
@@ -455,7 +462,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
public void testZeroDollarEvents() throws InvoiceApiException, CatalogApiException {
Plan plan = new MockPlan();
PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
DateTime targetDate = buildDateTime(2011, 1, 1);
events.add(createBillingEvent(UUID.randomUUID(), targetDate, plan, planPhase, 1));
@@ -468,7 +475,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
public void testEndDateIsCorrect() throws InvoiceApiException, CatalogApiException {
Plan plan = new MockPlan();
PlanPhase planPhase = createMockMonthlyPlanPhase(ZERO);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
DateTime targetDate = new DateTime();
events.add(createBillingEvent(UUID.randomUUID(), targetDate, plan, planPhase, targetDate.getDayOfMonth()));
@@ -493,7 +500,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
DateTime changeDate = new DateTime("2012-04-1T00:00:00.000-08:00");
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
BillingEvent event1 = createMockBillingEvent(null, subscription, new DateTime("2012-01-1T00:00:00.000-08:00"),
plan, phase1,
@@ -530,7 +537,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BigDecimal fixedCost = TEN;
PlanPhase phase1 = createMockMonthlyPlanPhase(monthlyRate, fixedCost, PhaseType.TRIAL);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
UUID subscriptionId = UUID.randomUUID();
UUID accountId = UUID.randomUUID();
@@ -542,7 +549,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 2);
- assertEquals(invoice1.getTotalAmount(), FIFTEEN);
+ assertEquals(invoice1.getBalance(), FIFTEEN);
List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
@@ -554,7 +561,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, currentDate, Currency.USD);
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
- assertEquals(invoice2.getTotalAmount(), FIVE);
+ assertEquals(invoice2.getBalance(), FIVE);
}
@Test
@@ -566,7 +573,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
PlanPhase phase1 = createMockMonthlyPlanPhase(null, fixedCost1, PhaseType.TRIAL);
PlanPhase phase2 = createMockMonthlyPlanPhase(null, fixedCost2, PhaseType.EVERGREEN);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
UUID subscriptionId = UUID.randomUUID();
UUID accountId = UUID.randomUUID();
@@ -578,7 +585,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
Invoice invoice1 = generator.generateInvoice(accountId, events, null, startDate, Currency.USD);
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
- assertEquals(invoice1.getTotalAmount(), fixedCost1);
+ assertEquals(invoice1.getBalance(), fixedCost1);
List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
@@ -592,12 +599,12 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
Invoice invoice2 = generator.generateInvoice(accountId, events, invoiceList, phaseChangeDate, Currency.USD);
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
- assertEquals(invoice2.getTotalAmount(), fixedCost2);
+ assertEquals(invoice2.getBalance(), fixedCost2);
}
@Test
- public void testNutsFailure() throws InvoiceApiException, CatalogApiException {
- MockBillingEventSet events = new MockBillingEventSet();
+ public void testInvoiceGenerationFailureScenario() throws InvoiceApiException, CatalogApiException {
+ BillingEventSet events = new MockBillingEventSet();
UUID subscriptionId = UUID.randomUUID();
UUID accountId = UUID.randomUUID();
final int BILL_CYCLE_DAY = 15;
@@ -624,7 +631,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
Invoice invoice1 = generator.generateInvoice(accountId, events, null, creationDate, Currency.USD);
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
- assertEquals(invoice1.getTotalAmount().compareTo(ZERO), 0);
+ assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
List<Invoice> invoiceList = new ArrayList<Invoice>();
invoiceList.add(invoice1);
@@ -633,7 +640,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 1);
assertEquals(invoice2.getInvoiceItems().get(0).getStartDate().compareTo(trialPhaseEndDate), 0);
- assertEquals(invoice2.getTotalAmount().compareTo(new BigDecimal("3.21")), 0);
+ assertEquals(invoice2.getBalance().compareTo(new BigDecimal("3.21")), 0);
invoiceList.add(invoice2);
DateTime targetDate = trialPhaseEndDate.toMutableDateTime().dayOfMonth().set(BILL_CYCLE_DAY).toDateTime();
@@ -641,7 +648,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 1);
assertEquals(invoice3.getInvoiceItems().get(0).getStartDate().compareTo(targetDate), 0);
- assertEquals(invoice3.getTotalAmount().compareTo(DISCOUNT_PRICE), 0);
+ assertEquals(invoice3.getBalance().compareTo(DISCOUNT_PRICE), 0);
invoiceList.add(invoice3);
targetDate = targetDate.plusMonths(6);
@@ -653,7 +660,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@Test(expectedExceptions = {InvoiceApiException.class})
public void testTargetDateRestrictionFailure() throws InvoiceApiException, CatalogApiException {
DateTime targetDate = DateTime.now().plusMonths(60);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
Plan plan1 = new MockPlan();
PlanPhase phase1 = createMockMonthlyPlanPhase(null, ZERO, PhaseType.TRIAL);
events.add(createBillingEvent(UUID.randomUUID(), DateTime.now(), plan1, phase1, 1));
@@ -700,16 +707,17 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
billCycleDay, BillingModeType.IN_ADVANCE, "Test", 1L, SubscriptionTransitionType.CREATE);
}
- private void testInvoiceGeneration(final UUID accountId, final MockBillingEventSet events, final List<Invoice> existingInvoices,
+ private void testInvoiceGeneration(final UUID accountId, final BillingEventSet events, final List<Invoice> existingInvoices,
final DateTime targetDate, final int expectedNumberOfItems,
final BigDecimal expectedAmount) throws InvoiceApiException {
Currency currency = Currency.USD;
Invoice invoice = generator.generateInvoice(accountId, events, existingInvoices, targetDate, currency);
assertNotNull(invoice);
assertEquals(invoice.getNumberOfItems(), expectedNumberOfItems);
-
existingInvoices.add(invoice);
- assertEquals(invoice.getTotalAmount(), expectedAmount);
+
+ distributeItems(existingInvoices);
+ assertEquals(invoice.getBalance(), expectedAmount);
}
@Test
@@ -726,14 +734,14 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
MockInternationalPrice price20 = new MockInternationalPrice(new DefaultPrice(TWENTY, Currency.USD));
PlanPhase basePlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
events.add(createBillingEvent(baseSubscription.getId(), april25, basePlan, basePlanEvergreen, 25));
// generate invoice
Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
assertNotNull(invoice1);
assertEquals(invoice1.getNumberOfItems(), 1);
- assertEquals(invoice1.getTotalAmount().compareTo(TEN), 0);
+ assertEquals(invoice1.getBalance().compareTo(TEN), 0);
List<Invoice> invoices = new ArrayList<Invoice>();
invoices.add(invoice1);
@@ -755,7 +763,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
invoices.add(invoice2);
assertNotNull(invoice2);
assertEquals(invoice2.getNumberOfItems(), 2);
- assertEquals(invoice2.getTotalAmount().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
+ assertEquals(invoice2.getBalance().compareTo(TWENTY_FIVE.multiply(new BigDecimal("0.9")).setScale(NUMBER_OF_DECIMALS, ROUNDING_METHOD)), 0);
// perform a repair (change base plan; remove one add-on)
// event stream should include just two plans
@@ -772,7 +780,7 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNotNull(invoice3);
assertEquals(invoice3.getNumberOfItems(), 5);
// -4.50 -18 - 10 (to correct the previous 2 invoices) + 4.50 + 13
- assertEquals(invoice3.getTotalAmount().compareTo(FIFTEEN.negate()), 0);
+ assertEquals(invoice3.getBalance().compareTo(FIFTEEN.negate()), 0);
}
@Test
@@ -788,10 +796,11 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
MockInternationalPrice price10 = new MockInternationalPrice(new DefaultPrice(TEN, Currency.USD));
PlanPhase originalPlanEvergreen = new MockPlanPhase(price10, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
- MockBillingEventSet events = new MockBillingEventSet();
+ BillingEventSet events = new MockBillingEventSet();
events.add(createBillingEvent(originalSubscription.getId(), april25, originalPlan, originalPlanEvergreen, 25));
Invoice invoice1 = generator.generateInvoice(accountId, events, null, april25, Currency.USD);
+ assertEquals(invoice1.getNumberOfItems(), 1);
List<Invoice> invoices = new ArrayList<Invoice>();
invoices.add(invoice1);
@@ -809,31 +818,48 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
// generate a new invoice
Invoice invoice2 = generator.generateInvoice(accountId, events, invoices, april25, Currency.USD);
+ assertEquals(invoice2.getNumberOfItems(), 4);
invoices.add(invoice2);
// move items to the correct invoice (normally, the dao calls will sort that out)
- generator.distributeItems(invoices);
+ distributeItems(invoices);
// ensure that the original invoice balance is zero
-
assertEquals(invoice1.getBalance().compareTo(ZERO), 0);
// ensure that the account balance is correct
- assertEquals(invoice2.getBalance().compareTo(FIVE.negate()), 0);
+ assertEquals(invoice2.getBalance().compareTo(ZERO), 0);
+ // ensure that the account has a credit balance
+ BigDecimal creditBalance = invoice1.getAmountCredited().add(invoice2.getAmountCredited());
+ assertTrue(creditBalance.compareTo(FIVE) == 0);
}
- /*
- test scenario: create invoice, pay invoice, change entitlement (repair)
-
- test scenario: two subscriptions, one with auto_invoice_off
+ private void distributeItems(List<Invoice> invoices) {
+ Map<UUID, Invoice> invoiceMap = new HashMap<UUID, Invoice>();
- test scenario: create invoice, pay invoice, change entitlement, generate invoices to use up credits
+ for (Invoice invoice : invoices) {
+ invoiceMap.put(invoice.getId(), invoice);
+ }
- test scenario: create invoice, pay invoice, add account-level credit, generate invoice
+ for (final Invoice invoice: invoices) {
+ Iterator<InvoiceItem> itemIterator = invoice.getInvoiceItems().iterator();
+ final UUID invoiceId = invoice.getId();
+ while (itemIterator.hasNext()) {
+ InvoiceItem item = itemIterator.next();
- */
+ if (!item.getInvoiceId().equals(invoiceId)) {
+ Invoice thisInvoice = invoiceMap.get(item.getInvoiceId());
+ if (thisInvoice == null) {
+ throw new NullPointerException();
+ }
+ thisInvoice.addInvoiceItem(item);
+ itemIterator.remove();
+ }
+ }
+ }
+ }
@Test
public void testAutoInvoiceOffAccount() throws Exception {
@@ -856,8 +882,54 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
assertNull(invoice);
}
+
+ public void testAutoInvoiceOffWithCredits() throws CatalogApiException, InvoiceApiException {
+ Currency currency = Currency.USD;
+ List<Invoice> invoices = new ArrayList<Invoice>();
+ MockBillingEventSet eventSet = new MockBillingEventSet();
+ UUID accountId = UUID.randomUUID();
- @Test(enabled = false)
+ DateTime startDate = new DateTime(2012, 1, 1, 0, 12, 34, 0);
+
+ // add first subscription creation event
+ UUID subscriptionId1 = UUID.randomUUID();
+ Plan plan1 = new MockPlan();
+ PlanPhase plan1phase1 = createMockMonthlyPlanPhase(FIFTEEN, null, PhaseType.DISCOUNT);
+ BillingEvent subscription1creation = createBillingEvent(subscriptionId1, startDate, plan1, plan1phase1, 1);
+ eventSet.add(subscription1creation);
+
+ // add second subscription creation event
+ UUID subscriptionId2 = UUID.randomUUID();
+ Plan plan2 = new MockPlan();
+ PlanPhase plan2phase1 = createMockMonthlyPlanPhase(TWELVE, null, PhaseType.EVERGREEN);
+ eventSet.add(createBillingEvent(subscriptionId2, startDate, plan2, plan2phase1, 1));
+
+ // generate the first invoice
+ Invoice invoice1 = generator.generateInvoice(accountId, eventSet, invoices, startDate, currency);
+ assertNotNull(invoice1);
+ assertTrue(invoice1.getBalance().compareTo(FIFTEEN.add(TWELVE)) == 0);
+ invoices.add(invoice1);
+
+ // set auto invoice off for first subscription (i.e. remove event from BillingEventSet and add subscription id to the list
+ // generate invoice
+ eventSet.remove(subscription1creation);
+ eventSet.addSubscriptionWithAutoInvoiceOff(subscriptionId1);
+
+ DateTime targetDate2 = startDate.plusMonths(1);
+ Invoice invoice2 = generator.generateInvoice(accountId, eventSet, invoices, targetDate2, currency);
+ assertNotNull(invoice2);
+ assertTrue(invoice2.getBalance().compareTo(TWELVE) == 0);
+ invoices.add(invoice2);
+
+ DateTime targetDate3 = targetDate2.plusMonths(1);
+ eventSet.clearSubscriptionsWithAutoInvoiceOff();
+ eventSet.add(subscription1creation);
+ Invoice invoice3 = generator.generateInvoice(accountId, eventSet, invoices, targetDate3, currency);
+ assertNotNull(invoice3);
+ assertTrue(invoice3.getBalance().compareTo(FIFTEEN.multiply(TWO).add(TWELVE)) == 0);
+ }
+
+ @Test
public void testAccountCredit() throws CatalogApiException, InvoiceApiException {
BillingEventSet billingEventSet = new MockBillingEventSet();
@@ -870,9 +942,24 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
BillingEvent creation = createBillingEvent(subscriptionId, startDate, plan, planPhase, 1);
billingEventSet.add(creation);
- Invoice invoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
- assertNotNull(invoice);
- assertEquals(invoice.getNumberOfItems(), 1);
- assertEquals(invoice.getTotalAmount().compareTo(TEN), 0);
+ List<Invoice> invoices = new ArrayList<Invoice>();
+
+ Invoice initialInvoice = generator.generateInvoice(accountId, billingEventSet, null, startDate, Currency.USD);
+ assertNotNull(initialInvoice);
+ assertEquals(initialInvoice.getNumberOfItems(), 1);
+ assertEquals(initialInvoice.getBalance().compareTo(TEN), 0);
+ invoices.add(initialInvoice);
+
+ // add account-level credit
+ DateTime creditDate = startDate.plusDays(5);
+ Invoice invoiceWithCredit = new DefaultInvoice(accountId, creditDate, creditDate, Currency.USD);
+ InvoiceItem accountCredit = new CreditInvoiceItem(invoiceWithCredit.getId(), accountId, creditDate, FIVE, Currency.USD);
+ invoiceWithCredit.addInvoiceItem(accountCredit);
+ invoices.add(invoiceWithCredit);
+
+ // invoice one month after the initial subscription
+ Invoice finalInvoice = generator.generateInvoice(accountId, billingEventSet, invoices, startDate.plusMonths(1), Currency.USD);
+ assertEquals(finalInvoice.getBalance().compareTo(FIVE), 0);
+ assertEquals(finalInvoice.getNumberOfItems(), 2);
}
}
\ No newline at end of file
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
index 25be5e2..5beea5c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InvoicingTestBase.java
@@ -62,6 +62,7 @@ public abstract class InvoicingTestBase {
protected static final BigDecimal TWENTY_FOUR = new BigDecimal("24.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_FIVE = new BigDecimal("25.0").setScale(NUMBER_OF_DECIMALS);
+ protected static final BigDecimal TWENTY_SEVEN = new BigDecimal("27.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_EIGHT = new BigDecimal("28.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal TWENTY_NINE = new BigDecimal("29.0").setScale(NUMBER_OF_DECIMALS);
protected static final BigDecimal THIRTY = new BigDecimal("30.0").setScale(NUMBER_OF_DECIMALS);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
index c9f627a..204cf6a 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/AccountTimelineJson.java
@@ -96,7 +96,7 @@ public class AccountTimelineJson {
}
this.invoices = new LinkedList<InvoiceJsonWithBundleKeys>();
for (Invoice cur : invoices) {
- this.invoices.add(new InvoiceJsonWithBundleKeys(cur.getTotalAmount(), cur.getId().toString(), cur.getInvoiceDate(), cur.getTargetDate(),
+ this.invoices.add(new InvoiceJsonWithBundleKeys(cur.getAmountCharged(), cur.getAmountCredited(), cur.getId().toString(), cur.getInvoiceDate(), cur.getTargetDate(),
Integer.toString(cur.getInvoiceNumber()), cur.getBalance(),
getBundleExternalKey(cur, bundles)));
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
index e96179e..5680db1 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonSimple.java
@@ -26,44 +26,35 @@ import org.joda.time.DateTime;
import com.ning.billing.invoice.api.Invoice;
public class InvoiceJsonSimple {
-
- @JsonView(BundleTimelineViews.Base.class)
private final BigDecimal amount;
-
- @JsonView(BundleTimelineViews.Base.class)
+ private final BigDecimal credit;
private final String invoiceId;
-
- @JsonView(BundleTimelineViews.Base.class)
private final DateTime invoiceDate;
-
- @JsonView(BundleTimelineViews.Base.class)
private final DateTime targetDate;
-
- @JsonView(BundleTimelineViews.Base.class)
private final String invoiceNumber;
-
- @JsonView(BundleTimelineViews.Base.class)
private final BigDecimal balance;
-
public InvoiceJsonSimple() {
- this.amount = null;
+ this.amount = BigDecimal.ZERO;
+ this.credit = BigDecimal.ZERO;
this.invoiceId = null;
this.invoiceDate = null;
this.targetDate = null;
this.invoiceNumber = null;
- this.balance = null;
+ this.balance = BigDecimal.ZERO;
}
@JsonCreator
public InvoiceJsonSimple(@JsonProperty("amount") BigDecimal amount,
- @JsonProperty("invoiceId") String invoiceId,
- @JsonProperty("invoiceDate") DateTime invoiceDate,
- @JsonProperty("targetDate") DateTime targetDate,
- @JsonProperty("invoiceNumber") String invoiceNumber,
- @JsonProperty("balance") BigDecimal balance) {
+ @JsonProperty("credit") BigDecimal credit,
+ @JsonProperty("invoiceId") String invoiceId,
+ @JsonProperty("invoiceDate") DateTime invoiceDate,
+ @JsonProperty("targetDate") DateTime targetDate,
+ @JsonProperty("invoiceNumber") String invoiceNumber,
+ @JsonProperty("balance") BigDecimal balance) {
super();
this.amount = amount;
+ this.credit = credit;
this.invoiceId = invoiceId;
this.invoiceDate = invoiceDate;
this.targetDate = targetDate;
@@ -72,7 +63,8 @@ public class InvoiceJsonSimple {
}
public InvoiceJsonSimple(Invoice input) {
- this.amount = input.getTotalAmount();
+ this.amount = input.getAmountCharged();
+ this.credit = input.getAmountCredited();
this.invoiceId = input.getId().toString();
this.invoiceDate = input.getInvoiceDate();
this.targetDate = input.getTargetDate();
@@ -84,6 +76,10 @@ public class InvoiceJsonSimple {
return amount;
}
+ public BigDecimal getCredit() {
+ return credit;
+ }
+
public String getInvoiceId() {
return invoiceId;
}
@@ -110,6 +106,7 @@ public class InvoiceJsonSimple {
int result = 1;
result = prime * result + ((amount == null) ? 0 : amount.hashCode());
result = prime * result + ((balance == null) ? 0 : balance.hashCode());
+ result = prime * result + ((credit == null) ? 0 : credit.hashCode());
result = prime * result
+ ((invoiceDate == null) ? 0 : invoiceDate.hashCode());
result = prime * result
@@ -133,26 +130,37 @@ public class InvoiceJsonSimple {
return false;
} else if (!amount.equals(other.amount))
return false;
+
if (balance == null) {
if (other.balance != null)
return false;
} else if (!balance.equals(other.balance))
return false;
+
+ if (credit == null) {
+ if (other.credit != null)
+ return false;
+ } else if (!credit.equals(other.credit))
+ return false;
+
if (invoiceDate == null) {
if (other.invoiceDate != null)
return false;
} else if (!invoiceDate.equals(other.invoiceDate))
return false;
+
if (invoiceId == null) {
if (other.invoiceId != null)
return false;
} else if (!invoiceId.equals(other.invoiceId))
return false;
+
if (invoiceNumber == null) {
if (other.invoiceNumber != null)
return false;
} else if (!invoiceNumber.equals(other.invoiceNumber))
return false;
+
return true;
}
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
index 98e2844..f1b0b4a 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/json/InvoiceJsonWithBundleKeys.java
@@ -24,11 +24,8 @@ import com.ning.billing.invoice.api.Invoice;
*/
public class InvoiceJsonWithBundleKeys extends InvoiceJsonSimple {
-
-
private final String bundleKeys;
-
public InvoiceJsonWithBundleKeys() {
super();
this.bundleKeys = null;
@@ -36,13 +33,14 @@ public class InvoiceJsonWithBundleKeys extends InvoiceJsonSimple {
@JsonCreator
public InvoiceJsonWithBundleKeys(@JsonProperty("amount") BigDecimal amount,
- @JsonProperty("invoiceId") String invoiceId,
- @JsonProperty("invoiceDate") DateTime invoiceDate,
- @JsonProperty("targetDate") DateTime targetDate,
- @JsonProperty("invoiceNumber") String invoiceNumber,
- @JsonProperty("balance") BigDecimal balance,
- @JsonProperty("externalBundleKeys") String bundleKeys) {
- super(amount, invoiceId, invoiceDate, targetDate, invoiceNumber, balance);
+ @JsonProperty("credit") BigDecimal credit,
+ @JsonProperty("invoiceId") String invoiceId,
+ @JsonProperty("invoiceDate") DateTime invoiceDate,
+ @JsonProperty("targetDate") DateTime targetDate,
+ @JsonProperty("invoiceNumber") String invoiceNumber,
+ @JsonProperty("balance") BigDecimal balance,
+ @JsonProperty("externalBundleKeys") String bundleKeys) {
+ super(amount, credit, invoiceId, invoiceDate, targetDate, invoiceNumber, balance);
this.bundleKeys = bundleKeys;
}
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
index f3f274a..af47408 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/NoOpPaymentProviderPlugin.java
@@ -46,7 +46,10 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
@Override
public PaymentInfoPlugin processInvoice(final Account account, final Invoice invoice)
throws PaymentPluginApiException {
- PaymentInfoPlugin payment = new PaymentInfoPlugin() {
+ if (makeAllInvoicesFail) {
+ throw new PaymentPluginApiException("", "test error");
+ }
+ PaymentInfoPlugin payment = new PaymentInfoPlugin() {
@Override
public DateTime getUpdatedDate() {
return new DateTime(DateTimeZone.UTC);
@@ -102,7 +105,7 @@ public class NoOpPaymentProviderPlugin implements PaymentProviderPlugin {
@Override
public String getExternalPaymentId() {
- return null;
+ return "NoOpPayment";
}
@Override
diff --git a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
index d195b59..802b49a 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockInvoice.java
@@ -23,6 +23,7 @@ import java.util.UUID;
import javax.annotation.Nullable;
+import com.ning.billing.invoice.api.InvoiceItemType;
import org.joda.time.DateTime;
import com.ning.billing.catalog.api.Currency;
@@ -178,23 +179,37 @@ public class MockInvoice extends EntityBase implements Invoice {
}
@Override
- public BigDecimal getTotalAmount() {
+ public BigDecimal getAmountCharged() {
BigDecimal result = BigDecimal.ZERO;
for(InvoiceItem i : invoiceItems) {
- result = result.add(i.getAmount());
+ if (!i.getInvoiceItemType().equals(InvoiceItemType.CREDIT)) {
+ result = result.add(i.getAmount());
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public BigDecimal getAmountCredited() {
+ BigDecimal result = BigDecimal.ZERO;
+
+ for(InvoiceItem i : invoiceItems) {
+ if (i.getInvoiceItemType().equals(InvoiceItemType.CREDIT)) {
+ result = result.add(i.getAmount());
+ }
}
return result;
}
@Override
public BigDecimal getBalance() {
- return getTotalAmount().subtract(getAmountPaid());
+ return getAmountCharged().subtract(getAmountPaid().subtract(getAmountCredited()));
}
@Override
public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
- if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+ if (getBalance().compareTo(BigDecimal.ZERO) == 0) {
return false;
}
diff --git a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
index 93c90fa..e55c7e4 100644
--- a/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
+++ b/payment/src/test/java/com/ning/billing/payment/MockRecurringInvoiceItem.java
@@ -18,6 +18,7 @@ package com.ning.billing.payment;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.InvoiceItem;
+import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.util.entity.EntityBase;
import org.apache.commons.lang.NotImplementedException;
@@ -151,6 +152,12 @@ public class MockRecurringInvoiceItem extends EntityBase implements InvoiceItem
public Currency getCurrency() {
return currency;
}
+
+ @Override
+ public InvoiceItemType getInvoiceItemType() {
+ return InvoiceItemType.RECURRING;
+ }
+
@Override
public InvoiceItem asReversingItem() {
throw new NotImplementedException();
diff --git a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
index ba7084c..6c09c85 100644
--- a/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/dao/AuditedCollectionDaoBase.java
@@ -53,7 +53,7 @@ public abstract class AuditedCollectionDaoBase<T extends Entity> implements Audi
entityIterator.remove();
existingEntityIterator.remove();
- // if the entities have the same hashcode (e.g. same data), don't bother updating
+ // if the entities have the same hash code (e.g. same data), don't bother updating
if (entity.hashCode() != existingEntity.hashCode()) {
entitiesToUpdate.add(entity);
}
diff --git a/util/src/main/java/com/ning/billing/util/dao/TableName.java b/util/src/main/java/com/ning/billing/util/dao/TableName.java
index 9253107..03e2c11 100644
--- a/util/src/main/java/com/ning/billing/util/dao/TableName.java
+++ b/util/src/main/java/com/ning/billing/util/dao/TableName.java
@@ -21,6 +21,7 @@ public enum TableName {
ACCOUNT_HISTORY("account_history"),
ACCOUNT_EMAIL_HISTORY("account_email_history"),
BUNDLES("bundles"),
+ CREDIT_INVOICE_ITEMS("credit_invoice_items"),
CUSTOM_FIELD_HISTORY("custom_field_history"),
FIXED_INVOICE_ITEMS("fixed_invoice_items"),
INVOICE_PAYMENTS("invoice_payments"),
diff --git a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
index 6fe2ec6..475ae8d 100644
--- a/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/collection/dao/EntityCollectionSqlDao.java
@@ -58,7 +58,7 @@ public interface EntityCollectionSqlDao<T extends Entity> {
@RegisterMapper(RecordIdMapper.class)
@SqlQuery
public List<Mapper<UUID, Long>> getRecordIds(@Bind("objectId") final String objectId,
- @ObjectTypeBinder final ObjectType objectType);
+ @ObjectTypeBinder final ObjectType objectType);
@SqlUpdate
public void test();
diff --git a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
index 9b67ede..b988bde 100644
--- a/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/com/ning/billing/util/tag/api/DefaultTagUserApi.java
@@ -20,6 +20,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.dao.ObjectType;
import com.ning.billing.util.tag.dao.TagDao;
@@ -84,7 +85,7 @@ public class DefaultTagUserApi implements TagUserApi {
}
@Override
- public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context) {
+ public void removeTags(UUID objectId, ObjectType objectType, List<TagDefinition> tagDefinitions, CallContext context) {
// TODO: consider making this batch
for (TagDefinition tagDefinition : tagDefinitions) {
tagDao.deleteTag(objectId, objectType, tagDefinition, context);
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
index 91cac4d..4e29fc2 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDao.java
@@ -16,6 +16,7 @@
package com.ning.billing.util.tag.dao;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.dao.AuditedCollectionDao;
import com.ning.billing.util.dao.ObjectType;
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
index 986e49d..bf08dcd 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagMapper.java
@@ -42,12 +42,11 @@ public class TagMapper extends MapperBase implements ResultSetMapper<Tag> {
}
}
+ UUID id = UUID.fromString(result.getString("id"));
if (thisTagType == null) {
- UUID id = UUID.fromString(result.getString("id"));
-
return new DescriptiveTag(id, name);
} else {
- return new DefaultControlTag(thisTagType);
+ return new DefaultControlTag(id, thisTagType);
}
}
}
diff --git a/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
index 73c70b3..aef1dce 100644
--- a/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
+++ b/util/src/main/resources/com/ning/billing/util/email/templates/HtmlInvoiceTemplate.mustache
@@ -78,7 +78,7 @@
<tr>
<td colspan=2 />
<td align=right><strong>{{text.invoiceAmount}}</strong></td>
- <td align=right><strong>{{invoice.totalAmount}}</strong></td>
+ <td align=right><strong>{{invoice.amountCharged}}</strong></td>
</tr>
<tr>
<td colspan=2 />
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
index f37b636..e67ef75 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDao.java
@@ -22,6 +22,7 @@ import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.TagDefinition;
import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -77,6 +78,10 @@ public class MockTagDao implements TagDao {
}
};
+ if (tagStore.get(objectId) == null) {
+ tagStore.put(objectId, new ArrayList<Tag>());
+ }
+
tagStore.get(objectId).add(tag);
}
diff --git a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
index 1bded44..b452203 100644
--- a/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
+++ b/util/src/test/java/com/ning/billing/util/tag/TestTagStore.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
+import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallOrigin;
import com.ning.billing.util.callcontext.UserType;
@@ -417,7 +418,7 @@ public class TestTagStore {
Handle handle = dbi.open();
String query = String.format("select * from audit_log a inner join tag_history th on a.record_id = th.history_record_id where a.table_name = 'tag_history' and th.id='%s' and a.change_type='DELETE'",
- tag.getId().toString());
+ tag.getId().toString());
List<Map<String, Object>> result = handle.select(query);
handle.close();
@@ -440,7 +441,7 @@ public class TestTagStore {
}
@Test
- public void testRemoveTag() {
+ public void testRemoveTag() throws InvoiceApiException {
UUID objectId = UUID.randomUUID();
ObjectType objectType = ObjectType.INVOICE;
TagDefinition tagDefinition = new DefaultTagDefinition("test tag", "test", false);