Details
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
index 7c89c41..9665d8d 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
@@ -169,26 +169,11 @@ public class TestBasic {
return ctd;
}
-
- @Test(groups = "fast", enabled = true)
- public void testBasePlanCompleteWithBillingDayInPast() throws Exception {
- testBasePlanComplete(clock.getUTCNow().minusDays(1).getDayOfMonth());
- }
-
- @Test(groups = "fast", enabled = true)
- public void testBasePlanCompleteWithBillingDayPresent() throws Exception {
- testBasePlanComplete(clock.getUTCNow().getDayOfMonth());
- }
-
@Test(groups = "fast", enabled = true)
- public void testBasePlanCompleteWithBillingDayInFuture() throws Exception {
- testBasePlanComplete(clock.getUTCNow().plusDays(1).getDayOfMonth());
- }
-
- private void testBasePlanComplete(int billingDay) throws Exception {
+ public void testBasePlanComplete() throws Exception {
long DELAY = 5000;
- Account account = accountUserApi.createAccount(getAccountData(billingDay), null, null);
+ Account account = accountUserApi.createAccount(getAccountData(), null, null);
assertNotNull(account);
SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(account.getId(), "whatever");
@@ -252,8 +237,8 @@ public class TestBasic {
//
busHandler.pushExpectedEvent(NextEvent.CHANGE);
busHandler.pushExpectedEvent(NextEvent.INVOICE);
-
clock.addDeltaFromReality(ctd.getMillis() - clock.getUTCNow().getMillis());
+ //clock.setDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
assertTrue(busHandler.isCompleted(DELAY));
log.info("testSimple passed fourth busHandler checkpoint.");
@@ -290,7 +275,7 @@ public class TestBasic {
}
- protected AccountData getAccountData(final int billCycleDay) {
+ protected AccountData getAccountData() {
AccountData accountData = new AccountData() {
@Override
public String getName() {
@@ -314,7 +299,7 @@ public class TestBasic {
}
@Override
public int getBillCycleDay() {
- return billCycleDay;
+ return 1;
}
@Override
public Currency getCurrency() {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index 84a21ef..95308be 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -83,9 +83,9 @@ public interface InvoiceItemSqlDao extends EntityDao<InvoiceItem> {
q.bind("phaseName", item.getPhaseName());
q.bind("startDate", item.getStartDate().toDate());
q.bind("endDate", item.getEndDate() == null ? null : item.getEndDate().toDate());
- q.bind("recurringAmount", item.getRecurringAmount() == null ? BigDecimal.ZERO : item.getRecurringAmount());
- q.bind("recurringRate", item.getRecurringRate() == null ? BigDecimal.ZERO : item.getRecurringRate());
- q.bind("fixedAmount", item.getFixedAmount() == null ? BigDecimal.ZERO : item.getFixedAmount());
+ q.bind("recurringAmount", item.getRecurringAmount());
+ q.bind("recurringRate", item.getRecurringRate());
+ q.bind("fixedAmount", item.getFixedAmount());
q.bind("currency", item.getCurrency().toString());
}
};
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index 7b730ab..4c50391 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -128,18 +128,18 @@ public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<Invoice
public static class BalanceMapper implements ResultSetMapper<BigDecimal> {
@Override
public BigDecimal map(final int index, final ResultSet result, final StatementContext context) throws SQLException {
- BigDecimal amount_invoiced = result.getBigDecimal("amount_invoiced");
- BigDecimal amount_paid = result.getBigDecimal("amount_paid");
+ BigDecimal amountInvoiced = result.getBigDecimal("amount_invoiced");
+ BigDecimal amountPaid = result.getBigDecimal("amount_paid");
- if (amount_invoiced == null) {
- amount_invoiced = BigDecimal.ZERO;
+ if (amountInvoiced == null) {
+ amountInvoiced = BigDecimal.ZERO;
}
- if (amount_paid == null) {
- amount_paid = BigDecimal.ZERO;
+ if (amountPaid == null) {
+ amountPaid = BigDecimal.ZERO;
}
- return amount_invoiced.subtract(amount_paid);
+ return amountInvoiced.subtract(amountPaid);
}
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
index 948638c..0de628c 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceListener.java
@@ -51,7 +51,7 @@ public class InvoiceListener {
private final AccountUserApi accountUserApi;
private final InvoiceDao invoiceDao;
- private final static boolean VERBOSE_OUTPUT = true;
+ private final static boolean VERBOSE_OUTPUT = false;
@Inject
public InvoiceListener(final InvoiceGenerator generator, final AccountUserApi accountUserApi,
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DateRange.java b/invoice/src/main/java/com/ning/billing/invoice/model/DateRange.java
index 9f21ca6..9a4c13b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DateRange.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DateRange.java
@@ -33,7 +33,11 @@ public class DateRange {
* @return whether the DateRange contains (inclusively) the DateTime in question
*/
public boolean contains(DateTime date) {
- return (!date.isBefore(startDate)) && (!date.isAfter(endDate));
+ if (endDate == null) {
+ return date.compareTo(startDate) == 0;
+ }
+
+ return !date.isBefore(startDate) && !date.isAfter(endDate);
}
public boolean overlaps(DateRange range) {
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 9bbc0c7..1467249 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
@@ -139,18 +139,17 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
BigDecimal numberOfBillingPeriods;
BigDecimal recurringAmount = null;
+ DateTime billThroughDate = null;
if (recurringRate != null) {
numberOfBillingPeriods = calculateNumberOfBillingPeriods(event, targetDate);
recurringAmount = numberOfBillingPeriods.multiply(recurringRate);
+ BillingMode billingMode = getBillingMode(event.getBillingMode());
+ billThroughDate = billingMode.calculateEffectiveEndDate(event.getEffectiveDate(), targetDate, event.getBillCycleDay(), event.getBillingPeriod());
}
- BillingMode billingMode = getBillingMode(event.getBillingMode());
- DateTime billThroughDate = billingMode.calculateEffectiveEndDate(event.getEffectiveDate(), targetDate, event.getBillCycleDay(), event.getBillingPeriod());
- if ((event.getBillingPeriod() == BillingPeriod.NO_BILLING_PERIOD) || (!billThroughDate.isAfter(targetDate.plusMonths(event.getBillingPeriod().getNumberOfMonths())))) {
- BigDecimal effectiveFixedPrice = items.hasInvoiceItemForPhase(event.getPlanPhase().getName()) ? null : fixedPrice;
- addInvoiceItem(invoiceId, items, event, billThroughDate, recurringAmount, recurringRate, effectiveFixedPrice, targetCurrency);
- }
+ BigDecimal effectiveFixedPrice = items.hasInvoiceItemForPhase(event.getPlanPhase().getName()) ? null : fixedPrice;
+ addInvoiceItem(invoiceId, items, event, billThroughDate, recurringAmount, recurringRate, effectiveFixedPrice, targetCurrency);
} catch (CatalogApiException e) {
log.error(String.format("Encountered a catalog error processing invoice %s for billing event on date %s",
invoiceId.toString(),
@@ -166,15 +165,15 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
BigDecimal numberOfBillingPeriods;
BigDecimal recurringAmount = null;
+ DateTime billThroughDate = null;
if (recurringRate != null) {
numberOfBillingPeriods = calculateNumberOfBillingPeriods(firstEvent, secondEvent, targetDate);
recurringAmount = numberOfBillingPeriods.multiply(recurringRate);
+ BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
+ billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
}
- BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
- DateTime billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
-
BigDecimal effectiveFixedPrice = items.hasInvoiceItemForPhase(firstEvent.getPlanPhase().getName()) ? null : fixedPrice;
addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, recurringAmount, recurringRate, effectiveFixedPrice, targetCurrency);
} catch (CatalogApiException e) {
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceItem.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceItem.java
index 508fab8..b65907e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceItem.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceItem.java
@@ -165,6 +165,15 @@ public class DefaultInvoiceItem implements InvoiceItem {
// TODO: deal with error cases
@Override
public void subtract(InvoiceItem that) {
+ if (this.endDate == null) {
+ // this is a fixed price item; set the fixed amount to null
+ if (this.fixedAmount.compareTo(that.getFixedAmount()) == 0) {
+ this.fixedAmount = null;
+ }
+
+ return;
+ }
+
if (this.startDate.equals(that.getStartDate()) && this.endDate.equals(that.getEndDate())) {
this.startDate = this.endDate;
this.recurringAmount = safeSubtract(this.recurringAmount, that.getRecurringAmount());
@@ -210,8 +219,12 @@ public class DefaultInvoiceItem implements InvoiceItem {
if (!this.getCurrency().equals(that.getCurrency())) {return false;}
+ if ((this.endDate == null) && (that.getEndDate() == null) && (this.startDate.compareTo(that.getStartDate()) == 0)) {
+ return true;
+ }
+
DateRange thisDateRange = new DateRange(this.getStartDate(), this.getEndDate());
- return thisDateRange.contains(that.getStartDate()) && thisDateRange.contains(that.getEndDate());
+ return thisDateRange.contains(that.getStartDate()) && (that.getEndDate() == null || thisDateRange.contains(that.getEndDate()));
}
private boolean compareNullableBigDecimal(@Nullable BigDecimal value1, @Nullable BigDecimal value2) {
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 13ccb60..f35f8b1 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
@@ -83,7 +83,9 @@ public class InvoiceItemList extends ArrayList<InvoiceItem> {
if (fixedAmountNull && (recurringRateNull || recurringAmountZero)) {
iterator.remove();
- } else if (item.getStartDate().compareTo(item.getEndDate()) == 0) {
+ } else if (item.getEndDate() != null && item.getStartDate().compareTo(item.getEndDate()) == 0) {
+ iterator.remove();
+ } else if (item.getFixedAmount() == null && item.getRecurringAmount() == null) {
iterator.remove();
}
}
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 f483773..c6d4bb6 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
@@ -42,8 +42,8 @@ getInvoicesForPayment() ::= <<
LEFT JOIN invoice_payment_summary ips ON ips.invoice_id = i.id
LEFT JOIN invoice_item_summary iis ON iis.invoice_id = i.id
WHERE ((ips.last_payment_date IS NULL) OR (DATEDIFF(:targetDate, ips.last_payment_date) >= :numberOfDays))
- AND ((ips.total_paid IS NULL) OR (iis.total_amount >= ips.total_paid))
- AND ((iis.total_amount IS NOT NULL) AND (iis.total_amount > 0))
+ AND ((ips.total_paid IS NULL) OR (iis.amount_invoiced >= ips.total_paid))
+ AND ((iis.amount_invoiced IS NOT NULL) AND (iis.amount_invoiced > 0))
GROUP BY <invoiceFields("i.")>;
>>
@@ -54,7 +54,8 @@ getById() ::= <<
>>
getAccountBalance() ::= <<
- SELECT SUM(iis.total_amount) AS amount_invoiced, SUM(ips.total_paid) AS amount_paid
+ SELECT SUM(iis.amount_invoiced) AS amount_invoiced,
+ SUM(ips.total_paid) AS amount_paid
FROM invoices i
LEFT JOIN invoice_payment_summary ips ON i.id = ips.invoice_id
LEFT JOIN invoice_item_summary iis ON i.id = iis.invoice_id
@@ -87,7 +88,7 @@ getUnpaidInvoicesByAccountId() ::= <<
LEFT JOIN invoice_item_summary iis ON i.id = iis.invoice_id
WHERE i.account_id = :accountId AND NOT (i.target_date > :upToDate)
GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency
- HAVING (SUM(iis.total_amount) > SUM(ips.total_paid)) OR (SUM(ips.total_paid) IS NULL)
+ HAVING (SUM(iis.amount_invoiced) > SUM(ips.total_paid)) OR (SUM(ips.total_paid) IS NULL)
ORDER BY i.target_date ASC;
>>
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 1cf6f23..59920c0 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/com/ning/billing/invoice/ddl.sql
@@ -7,9 +7,9 @@ CREATE TABLE invoice_items (
phase_name varchar(50) NOT NULL,
start_date datetime NOT NULL,
end_date datetime NULL,
- recurring_amount numeric(10,4) NOT NULL,
- recurring_rate numeric(10,4) NOT NULL,
- fixed_amount numeric(10,4) NOT NULL,
+ recurring_amount numeric(10,4) NULL,
+ recurring_rate numeric(10,4) NULL,
+ fixed_amount numeric(10,4) NULL,
currency char(3) NOT NULL,
PRIMARY KEY(id)
) ENGINE=innodb;
@@ -41,12 +41,16 @@ CREATE UNIQUE INDEX invoice_payments_unique ON invoice_payments(invoice_id, paym
DROP VIEW IF EXISTS invoice_payment_summary;
CREATE VIEW invoice_payment_summary AS
-SELECT invoice_id, SUM(amount) AS total_paid, MAX(payment_attempt_date) AS last_payment_date
+SELECT invoice_id,
+ CASE WHEN SUM(amount) IS NULL THEN 0 ELSE SUM(amount) END AS total_paid,
+ MAX(payment_attempt_date) AS last_payment_date
FROM invoice_payments
GROUP BY invoice_id;
DROP VIEW IF EXISTS invoice_item_summary;
CREATE VIEW invoice_item_summary AS
-select invoice_id, sum(recurring_amount) + SUM(fixed_amount) AS total_amount
-from invoice_items
-group by invoice_id;
+SELECT invoice_id,
+ CASE WHEN SUM(recurring_amount) IS NULL THEN 0 ELSE SUM(recurring_amount) END
+ + CASE WHEN SUM(fixed_amount) IS NULL THEN 0 ELSE SUM(fixed_amount) END AS amount_invoiced
+FROM invoice_items
+GROUP BY invoice_id;