killbill-uncached
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java 178(+131 -47)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestInArrearWithCatalogVersions.java 93(+58 -35)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java 2(+1 -1)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java 29(+14 -15)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java 71(+54 -17)
invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg 14(+14 -0)
invoice/src/main/resources/org/killbill/billing/invoice/migration/V20181129164135__tracking_ids.sql 1(+1 -0)
invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java 4(+2 -2)
Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
index a405fbe..a7140c3 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationBase.java
@@ -320,7 +320,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
return;
}
-
final InvoiceConfig defaultInvoiceConfig = new ConfigurationObjectFactory(skifeConfigSource).build(InvoiceConfig.class);
invoiceConfig = new ConfigurableInvoiceConfig(defaultInvoiceConfig);
final Injector g = Guice.createInjector(Stage.PRODUCTION, new BeatrixIntegrationModule(configSource, clock, invoiceConfig));
@@ -596,7 +595,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
}, events);
}
-
protected Payment refundPaymentWithInvoiceItemAdjAndCheckForCompletion(final Account account, final Payment payment, final Map<UUID, BigDecimal> iias, final NextEvent... events) {
return refundPaymentWithInvoiceItemAdjAndCheckForCompletion(account, payment, payment.getPurchasedAmount(), payment.getCurrency(), iias, events);
}
@@ -888,7 +886,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
remove_account_Tag(id, ControlTagType.AUTO_PAY_OFF, type, additionalEvents);
}
-
protected void remove_AUTO_INVOICING_OFF_Tag(final UUID id, final ObjectType type, final NextEvent... additionalEvents) throws TagDefinitionApiException, TagApiException {
remove_account_Tag(id, ControlTagType.AUTO_INVOICING_OFF, type, additionalEvents);
}
@@ -897,8 +894,7 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
remove_account_Tag(id, ControlTagType.AUTO_INVOICING_DRAFT, type, additionalEvents);
}
-
- private void remove_account_Tag(final UUID id, final ControlTagType controlTagType, final ObjectType type, final NextEvent... additionalEvents) throws TagDefinitionApiException, TagApiException {
+ private void remove_account_Tag(final UUID id, final ControlTagType controlTagType, final ObjectType type, final NextEvent... additionalEvents) throws TagDefinitionApiException, TagApiException {
busHandler.pushExpectedEvent(NextEvent.TAG);
busHandler.pushExpectedEvents(additionalEvents);
tagUserApi.removeTag(id, type, controlTagType.getId(), callContext);
@@ -918,16 +914,20 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
return result;
}
- protected void recordUsageData(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) throws UsageApiException {
+ protected void recordUsageData(final UUID subscriptionId, final String trackingId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) throws UsageApiException {
final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
usageRecords.add(new UsageRecord(startDate, amount));
final List<UnitUsageRecord> unitUsageRecords = new ArrayList<UnitUsageRecord>();
unitUsageRecords.add(new UnitUsageRecord(unitType, usageRecords));
- final SubscriptionUsageRecord record = new SubscriptionUsageRecord(subscriptionId, UUID.randomUUID().toString(), unitUsageRecords);
+ final SubscriptionUsageRecord record = new SubscriptionUsageRecord(subscriptionId, trackingId, unitUsageRecords);
usageUserApi.recordRolledUpUsage(record, context);
}
+ protected void recordUsageData(final SubscriptionUsageRecord usageRecord, final CallContext context) throws UsageApiException {
+ usageUserApi.recordRolledUpUsage(usageRecord, context);
+ }
+
protected void removeUsageData(final UUID subscriptionId, final String unitType, final LocalDate recordedDate) {
dbi.withHandle(new HandleCallback<Void>() {
@Override
@@ -939,7 +939,6 @@ public class TestIntegrationBase extends BeatrixTestSuiteWithEmbeddedDB implemen
});
}
-
protected static class TestDryRunArguments implements DryRunArguments {
private final DryRunType dryRunType;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
index 551822f..fa9ec41 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java
@@ -18,6 +18,8 @@
package org.killbill.billing.beatrix.integration.usage;
import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
@@ -30,12 +32,19 @@ import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UnitUsageRecord;
+import org.killbill.billing.usage.api.UsageRecord;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import static org.testng.Assert.assertEquals;
public class TestConsumableInArrear extends TestIntegrationBase {
@@ -62,44 +71,47 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-1", "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-2", "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("tracking-1", "tracking-2"), internalCallContext);
// $0 invoice
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of(), internalCallContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-3", "bullets", new LocalDate(2012, 6, 1), 50L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-4", "bullets", new LocalDate(2012, 6, 16), 300L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 4, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("tracking-3", "tracking-4"), internalCallContext);
// Should be ignored because this is outside of optimization range (org.killbill.invoice.readMaxRawUsagePreviousPeriod = 2) => we will only look for items > 2012-7-1 - 2 months = 2012-5-1
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 30), 100L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-5", "bullets", new LocalDate(2012, 4, 30), 100L, callContext);
// Should be invoiced from past period
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 5, 1), 199L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-6", "bullets", new LocalDate(2012, 5, 1), 199L, callContext);
// New usage for this past period
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 7, 1), 50L, callContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 7, 16), 300L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-7", "bullets", new LocalDate(2012, 7, 1), 50L, callContext);
+ recordUsageData(aoSubscription.getId(), "tracking-8", "bullets", new LocalDate(2012, 7, 16), 300L, callContext);
// Remove old data, should be ignored by the system because readMaxRawUsagePreviousPeriod = 2, so:
// * Last endDate invoiced is 2012-7-1 => Anything 2 period prior that will be ignored => Anything prior 2012-5-1 should be ignored
@@ -109,25 +121,26 @@ public class TestConsumableInArrear extends TestIntegrationBase {
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 5, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 1), new LocalDate(2012, 8, 1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
-
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 5, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 7, 1), new LocalDate(2012, 8, 1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("tracking-6", "tracking-7", "tracking-8"), internalCallContext);
// Add a few more month of usage data and check correctness of invoice: iterate 8 times until 2013-4-1 (prior ANNUAL renewal)
LocalDate startDate = new LocalDate(2012, 8, 1);
int currentInvoice = 6;
for (int i = 0; i < 8; i++) {
- recordUsageData(aoSubscription.getId(), "bullets", startDate.plusDays(15), 350L, callContext);
+ final String trackingId = "tracking-" + (9 + i);
+ recordUsageData(aoSubscription.getId(), trackingId, "bullets", startDate.plusDays(15), 350L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), currentInvoice, callContext,
- new ExpectedInvoiceItemCheck(startDate, startDate.plusMonths(1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
-
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), currentInvoice, callContext,
+ new ExpectedInvoiceItemCheck(startDate, startDate.plusMonths(1), InvoiceItemType.USAGE, new BigDecimal("11.80")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of(trackingId), internalCallContext);
startDate = startDate.plusMonths(1);
currentInvoice++;
@@ -157,29 +170,31 @@ public class TestConsumableInArrear extends TestIntegrationBase {
//
final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
+ recordUsageData(aoSubscription.getId(), "t1", "bullets", new LocalDate(2012, 4, 1), 99L, callContext);
+ recordUsageData(aoSubscription.getId(), "t2", "bullets", new LocalDate(2012, 4, 15), 100L, callContext);
busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.NULL_INVOICE, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addDays(30);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2013, 5, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 5, 3), 99L, callContext);
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 5, 5), 100L, callContext);
+ recordUsageData(aoSubscription.getId(), "t3", "bullets", new LocalDate(2012, 5, 3), 99L, callContext);
+ recordUsageData(aoSubscription.getId(), "t4", "bullets", new LocalDate(2012, 5, 5), 100L, callContext);
// This one should be ignored
- recordUsageData(aoSubscription.getId(), "bullets", new LocalDate(2012, 5, 29), 100L, callContext);
+ recordUsageData(aoSubscription.getId(), "t5", "bullets", new LocalDate(2012, 5, 29), 100L, callContext);
clock.addDays(27);
busHandler.pushExpectedEvents(NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
aoSubscription.cancelEntitlementWithDateOverrideBillingPolicy(new LocalDate(2012, 5, 28), BillingActionPolicy.IMMEDIATE, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 28), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 5, 28), InvoiceItemType.USAGE, new BigDecimal("5.90")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3", "t4"), internalCallContext);
busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE);
clock.addDays(4);
@@ -204,44 +219,113 @@ public class TestConsumableInArrear extends TestIntegrationBase {
Assert.assertNull(bpSubscription.getSubscriptionBase().getChargedThroughDate());
// Record usage for first month
- recordUsageData(bpSubscription.getId(), "stones", new LocalDate(2012, 4, 5), 85L, callContext);
- recordUsageData(bpSubscription.getId(), "stones", new LocalDate(2012, 4, 15), 150L, callContext);
+ recordUsageData(bpSubscription.getId(), "xxx-1", "stones", new LocalDate(2012, 4, 5), 85L, callContext);
+ recordUsageData(bpSubscription.getId(), "xxx-2", "stones", new LocalDate(2012, 4, 15), 150L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 1, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("1000")));
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("1000")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("xxx-1", "xxx-2"), internalCallContext);
final DateTime firstExpectedCTD = account.getReferenceTime().withMonthOfYear(5).withDayOfMonth(1);
- Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(firstExpectedCTD), 0);
+ assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(firstExpectedCTD), 0);
// $0 invoice
busHandler.pushExpectedEvents(NextEvent.INVOICE);
clock.addMonths(1);
assertListenerStatus();
-
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of(), internalCallContext);
final DateTime secondExpectedCTD = account.getReferenceTime().withMonthOfYear(6).withDayOfMonth(1);
- Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(secondExpectedCTD), 0);
+ assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(secondExpectedCTD), 0);
// Record usage for third month (verify invoicing resumes)
- recordUsageData(bpSubscription.getId(), "stones", new LocalDate(2012, 6, 5), 25L, callContext);
- recordUsageData(bpSubscription.getId(), "stones", new LocalDate(2012, 6, 15), 50L, callContext);
+ recordUsageData(bpSubscription.getId(), "xxx-3", "stones", new LocalDate(2012, 6, 5), 25L, callContext);
+ recordUsageData(bpSubscription.getId(), "xxx-4", "stones", new LocalDate(2012, 6, 15), 50L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.USAGE, new BigDecimal("100")));
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 6, 1), new LocalDate(2012, 7, 1), InvoiceItemType.USAGE, new BigDecimal("100")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("xxx-3", "xxx-4"), internalCallContext);
final DateTime thirdExpectedCTD = account.getReferenceTime().withMonthOfYear(7).withDayOfMonth(1);
- Assert.assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(thirdExpectedCTD), 0);
+ assertEquals(subscriptionBaseInternalApiApi.getSubscriptionFromId(bpSubscription.getId(), internalCallContext).getChargedThroughDate().compareTo(thirdExpectedCTD), 0);
+ }
+
+ @Test(groups = "slow")
+ public void testWithMultipleUsageSubscriptions() throws Exception {
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ // Create subscription
+ final DefaultEntitlement bp1 = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey1", "Trebuchet", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+ subscriptionChecker.checkSubscriptionCreated(bp1.getId(), internalCallContext);
+
+ final DefaultEntitlement bp2 = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey2", "Trebuchet", ProductCategory.BASE, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.NULL_INVOICE);
+ subscriptionChecker.checkSubscriptionCreated(bp2.getId(), internalCallContext);
+
+ final List<UsageRecord> bp1StoneRecords1 = new ArrayList();
+ bp1StoneRecords1.add(new UsageRecord(new LocalDate(2012, 4, 5), 5L));
+ bp1StoneRecords1.add(new UsageRecord(new LocalDate(2012, 4, 15), 10L));
+ bp1StoneRecords1.add(new UsageRecord(new LocalDate(2012, 4, 16), 15L));
+ final SubscriptionUsageRecord bp1UsageRecord1 = new SubscriptionUsageRecord(bp1.getId(), "bp1-tracking-1", ImmutableList.of(new UnitUsageRecord("stones", bp1StoneRecords1)));
+ recordUsageData(bp1UsageRecord1, callContext);
+
+ final List<UsageRecord> bp1StoneRecords2 = new ArrayList();
+ bp1StoneRecords2.add(new UsageRecord(new LocalDate(2012, 4, 23), 10L));
+ // Outside of range for this period -> It's tracking ID spreads across 2 invoices
+ bp1StoneRecords2.add(new UsageRecord(new LocalDate(2012, 5, 1), 101L));
+ final SubscriptionUsageRecord bp1UsageRecord2 = new SubscriptionUsageRecord(bp1.getId(), "bp1-tracking-2", ImmutableList.of(new UnitUsageRecord("stones", bp1StoneRecords2)));
+ recordUsageData(bp1UsageRecord2, callContext);
+
+
+
+ final List<UsageRecord> bp2StoneRecords = new ArrayList();
+ bp2StoneRecords.add(new UsageRecord(new LocalDate(2012, 4, 5), 85L));
+ bp2StoneRecords.add(new UsageRecord(new LocalDate(2012, 4, 15), 150L));
+ bp2StoneRecords.add(new UsageRecord(new LocalDate(2012, 4, 16), 39L));
+ final SubscriptionUsageRecord bp2UsageRecord = new SubscriptionUsageRecord(bp2.getId(), "bp2-tracking-1", ImmutableList.of(new UnitUsageRecord("stones", bp2StoneRecords)));
+ recordUsageData(bp2UsageRecord, callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("100")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), new LocalDate(2012, 5, 1), InvoiceItemType.USAGE, new BigDecimal("1000")));
+
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("bp1-tracking-1", "bp1-tracking-2", "bp2-tracking-1"), internalCallContext);
+
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, new BigDecimal("0")),
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.USAGE, new BigDecimal("1000")));
+
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("bp1-tracking-2"), internalCallContext);
+
+
}
+
+
+
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestInArrearWithCatalogVersions.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestInArrearWithCatalogVersions.java
index fd6c4dc..6f2971d 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestInArrearWithCatalogVersions.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestInArrearWithCatalogVersions.java
@@ -19,6 +19,7 @@ package org.killbill.billing.beatrix.integration.usage;
import java.math.BigDecimal;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -31,12 +32,16 @@ import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck
import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
import org.killbill.billing.entitlement.api.DefaultEntitlementSpecifier;
import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.usage.api.SubscriptionUsageRecord;
+import org.killbill.billing.usage.api.UnitUsageRecord;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
@@ -63,17 +68,19 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
+ recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
+ recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 1, callContext,
- new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("300.00")));
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("300.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
+
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext); // -> Uses v1 : $150
+ recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext); // -> Uses v1 : $150
// Catalog change with new price on 2016-05-08
// Schedule CHANGE_PLAN on 2016-05-09
@@ -86,17 +93,20 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
clock.addDays(8);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 9), InvoiceItemType.USAGE, new BigDecimal("150.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3"), internalCallContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext); // -> Uses v2 : $250
+ recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext); // -> Uses v2 : $250
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(23);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 9), new LocalDate(2016, 6, 1), InvoiceItemType.USAGE, new BigDecimal("250.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t4"), internalCallContext);
+
}
@@ -114,17 +124,18 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec1), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
+ recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 1), 143L, callContext);
+ recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 18), 57L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("300.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext); // -> Uses v1 : $150
+ recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext); // -> Uses v1 : $150
final Entitlement bp = entitlementApi.getEntitlementForId(entitlementId, callContext);
@@ -137,17 +148,20 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
clock.addDays(8);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 9), InvoiceItemType.USAGE, new BigDecimal("150.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3"), internalCallContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext); // -> Uses special plan : $100
+
+ recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 10), 100L, callContext); // -> Uses special plan : $100
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(23);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 9), new LocalDate(2016, 6, 1), InvoiceItemType.USAGE, new BigDecimal("100.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t4"), internalCallContext);
}
@@ -166,43 +180,47 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 5), 1L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 5), 99L, callContext);
+ recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 5), 1L, callContext);
+ recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 5), 99L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("150.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
+
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 5), 100L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 8), 900L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 9), 200L, callContext); // Move to tier 2.
+ recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 5), 100L, callContext);
+ recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 8), 900L, callContext);
+ recordUsageData(entitlementId, "t5", "kilowatt-hour", new LocalDate(2016, 5, 9), 200L, callContext); // Move to tier 2.
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 6, 1), InvoiceItemType.USAGE, new BigDecimal("1900.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3", "t4", "t5"), internalCallContext);
// Remove Usage data from period 2016-5-1 -> 2016-6-1 and verify there is no issue (readMaxRawUsagePreviousPeriod = 0 => We ignore any past invoiced period)
// Full deletion on the second tier
removeUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 9));
//
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 6, 5), 100L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 6, 8), 900L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 6, 12), 50L, callContext); // Move to tier 2.
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 6, 13), 50L, callContext);
+ recordUsageData(entitlementId, "t6", "kilowatt-hour", new LocalDate(2016, 6, 5), 100L, callContext);
+ recordUsageData(entitlementId, "t7", "kilowatt-hour", new LocalDate(2016, 6, 8), 900L, callContext);
+ recordUsageData(entitlementId, "t8", "kilowatt-hour", new LocalDate(2016, 6, 12), 50L, callContext); // Move to tier 2.
+ recordUsageData(entitlementId, "t9", "kilowatt-hour", new LocalDate(2016, 6, 13), 50L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 3, callContext, ImmutableList.<ExpectedInvoiceItemCheck>of(
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext, ImmutableList.<ExpectedInvoiceItemCheck>of(
new ExpectedInvoiceItemCheck(new LocalDate(2016, 6, 1), new LocalDate(2016, 7, 1), InvoiceItemType.USAGE, new BigDecimal("1700.00"))));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t6", "t7", "t8", "t9"), internalCallContext);
// Remove Usage data from period 2016-6-1 -> 2016-7-1 and verify there is no issue (readMaxRawUsagePreviousPeriod = 0 => We ignore any past invoiced period)
// Partial deletion on the second tier
@@ -214,8 +232,10 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
assertListenerStatus();
// Check invoicing occurred and - i.e system did not detect deletion of passed invoiced data.
- invoiceChecker.checkInvoice(account.getId(), 4, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 4, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 7, 1), new LocalDate(2016, 8, 1), InvoiceItemType.USAGE, BigDecimal.ZERO));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of(), internalCallContext);
+
}
@Test(groups = "slow")
@@ -232,31 +252,33 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
final UUID entitlementId = entitlementApi.createBaseEntitlement(account.getId(), new DefaultEntitlementSpecifier(spec), null, null, null, false, true, ImmutableList.<PluginProperty>of(), callContext);
assertListenerStatus();
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 5), 1L, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 4, 5), 99L, callContext);
+ recordUsageData(entitlementId, "t1", "kilowatt-hour", new LocalDate(2016, 4, 5), 1L, callContext);
+ recordUsageData(entitlementId, "t2", "kilowatt-hour", new LocalDate(2016, 4, 5), 99L, callContext);
busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addMonths(1);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 1, callContext,
+ Invoice curInvoice = invoiceChecker.checkInvoice(account.getId(), 1, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 4, 1), new LocalDate(2016, 5, 1), InvoiceItemType.USAGE, new BigDecimal("150.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t1", "t2"), internalCallContext);
final Entitlement bp = entitlementApi.getEntitlementForId(entitlementId, callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext);
+ recordUsageData(entitlementId, "t3", "kilowatt-hour", new LocalDate(2016, 5, 2), 100L, callContext);
// Update subscription BCD
bp.updateBCD(9, new LocalDate(2016, 5, 9), callContext);
- recordUsageData(entitlementId, "kilowatt-hour", new LocalDate(2016, 5, 10), 10L, callContext);
+ recordUsageData(entitlementId, "t4", "kilowatt-hour", new LocalDate(2016, 5, 10), 10L, callContext);
busHandler.pushExpectedEvents(NextEvent.BCD_CHANGE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
clock.addDays(8);
assertListenerStatus();
- invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 2, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 1), new LocalDate(2016, 5, 9), InvoiceItemType.USAGE, new BigDecimal("150.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t3"), internalCallContext);
// Original notification before we change BCD
busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE);
@@ -268,8 +290,9 @@ public class TestInArrearWithCatalogVersions extends TestIntegrationBase {
assertListenerStatus();
// NOTE: Is using the new version of the catalog Utility-v2 (effectiveDateForExistingSubscriptions = 2016-05-08T00:00:00+00:00) what we want or is this a bug?
- invoiceChecker.checkInvoice(account.getId(), 3, callContext,
+ curInvoice = invoiceChecker.checkInvoice(account.getId(), 3, callContext,
new ExpectedInvoiceItemCheck(new LocalDate(2016, 5, 9), new LocalDate(2016, 6, 9), InvoiceItemType.USAGE, new BigDecimal("25.00")));
+ invoiceChecker.checkTrackingIds(curInvoice, ImmutableSet.of("t4"), internalCallContext);
}
}
\ No newline at end of file
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 36b0ee7..bdfd46a 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -20,9 +20,11 @@ package org.killbill.billing.beatrix.util;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.entitlement.api.EntitlementApi;
import org.killbill.billing.entitlement.api.EntitlementApiException;
@@ -31,17 +33,23 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
import org.killbill.billing.invoice.api.InvoiceUserApi;
+import org.killbill.billing.invoice.dao.InvoiceTrackingModelDao;
+import org.killbill.billing.invoice.dao.InvoiceTrackingSqlDao;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.util.callcontext.CallContext;
+import org.skife.jdbi.v2.IDBI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
+import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@@ -53,12 +61,14 @@ public class InvoiceChecker {
private final InvoiceUserApi invoiceUserApi;
private final EntitlementApi entitlementApi;
private final AuditChecker auditChecker;
+ private final IDBI dbi;
@Inject
- public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementApi entitlementApi, final AuditChecker auditChecker) {
+ public InvoiceChecker(final InvoiceUserApi invoiceUserApi, final EntitlementApi entitlementApi, final AuditChecker auditChecker, final IDBI dbi) {
this.invoiceUserApi = invoiceUserApi;
this.entitlementApi = entitlementApi;
this.auditChecker = auditChecker;
+ this.dbi = dbi;
}
public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final ExpectedInvoiceItemCheck... expected) throws InvoiceApiException {
@@ -170,6 +180,26 @@ public class InvoiceChecker {
}
}
+
+ public void checkTrackingIds(final Invoice invoice, final Set<String> expectedTrackingIds, final InternalCallContext internalCallContext) {
+ final InvoiceTrackingSqlDao dao = dbi.onDemand(InvoiceTrackingSqlDao.class);
+ final List<InvoiceTrackingModelDao> result = dao.getTrackingsForInvoice(invoice.getId().toString(), internalCallContext);
+
+ final Set<String> existingTrackingIds = ImmutableSet.copyOf(Iterables.transform(result, new Function<InvoiceTrackingModelDao, String>() {
+ @Override
+ public String apply(final InvoiceTrackingModelDao input) {
+ return input.getTrackingId();
+ }
+ }));
+
+ assertEquals(existingTrackingIds.size(), expectedTrackingIds.size());
+ for (final String cur : existingTrackingIds) {
+ assertTrue(expectedTrackingIds.contains(cur));
+ }
+ }
+
+
+
public static class ExpectedInvoiceItemCheck {
private final boolean checkDates;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
index 764ef17..4991040 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
@@ -36,21 +36,23 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
private String trackingId;
private UUID invoiceId;
private UUID subscriptionId;
+ private String unitType;
private LocalDate recordDate;
public InvoiceTrackingModelDao() { /* For the DAO mapper */ }
- public InvoiceTrackingModelDao(final String trackingId,final UUID invoiceId, final UUID subscriptionId, final LocalDate recordDate) {
- this(UUIDs.randomUUID(), null, trackingId, invoiceId, subscriptionId, recordDate);
+ public InvoiceTrackingModelDao(final String trackingId,final UUID invoiceId, final UUID subscriptionId, final String unitType, final LocalDate recordDate) {
+ this(UUIDs.randomUUID(), null, trackingId, invoiceId, subscriptionId, unitType, recordDate);
}
public InvoiceTrackingModelDao(final UUID id, @Nullable final DateTime createdDate, final String trackingId,
- final UUID invoiceId, final UUID subscriptionId, final LocalDate recordDate) {
+ final UUID invoiceId, final UUID subscriptionId, final String unitType, final LocalDate recordDate) {
super(id, createdDate, createdDate);
this.trackingId = trackingId;
this.invoiceId = invoiceId;
this.subscriptionId = subscriptionId;
+ this.unitType = unitType;
this.recordDate = recordDate;
}
@@ -78,6 +80,14 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
this.subscriptionId = subscriptionId;
}
+ public String getUnitType() {
+ return unitType;
+ }
+
+ public void setUnitType(final String unitType) {
+ this.unitType = unitType;
+ }
+
public LocalDate getRecordDate() {
return recordDate;
}
@@ -101,12 +111,13 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
return Objects.equal(trackingId, that.trackingId) &&
Objects.equal(invoiceId, that.invoiceId) &&
Objects.equal(subscriptionId, that.subscriptionId) &&
+ Objects.equal(unitType, that.unitType) &&
Objects.equal(recordDate, that.recordDate);
}
@Override
public int hashCode() {
- return Objects.hashCode(super.hashCode(), trackingId, invoiceId, subscriptionId, recordDate);
+ return Objects.hashCode(super.hashCode(), trackingId, invoiceId, subscriptionId, unitType, recordDate);
}
@Override
@@ -115,6 +126,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
"trackingId='" + trackingId + '\'' +
", invoiceId=" + invoiceId +
", subscriptionId=" + subscriptionId +
+ ", unitType='" + unitType + '\'' +
", recordDate=" + recordDate +
'}';
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
index 469200c..00979f8 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
@@ -43,5 +43,10 @@ public interface InvoiceTrackingSqlDao extends EntitySqlDao<InvoiceTrackingModel
@Bind("endDate") final Date endDate,
@SmartBindBean final InternalTenantContext context);
+
+ @SqlQuery
+ List<InvoiceTrackingModelDao> getTrackingsForInvoice(@Bind("invoiceId") final String invoiceId,
+ @SmartBindBean final InternalTenantContext context);
+
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
index 4351fe8..aef78d8 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/InvoiceWithMetadata.java
@@ -91,12 +91,14 @@ public class InvoiceWithMetadata {
private final String trackingId;
private final UUID invoiceId;
private final UUID subscriptionId;
+ private final String unitType;
private final LocalDate recordDate;
- public TrackingIds(final String trackingId, final UUID invoiceId, final UUID subscriptionId, final LocalDate recordDate) {
+ public TrackingIds(final String trackingId, final UUID invoiceId, final UUID subscriptionId, final String unitType, final LocalDate recordDate) {
this.trackingId = trackingId;
this.invoiceId = invoiceId;
this.subscriptionId = subscriptionId;
+ this.unitType = unitType;
this.recordDate = recordDate;
}
@@ -116,6 +118,10 @@ public class InvoiceWithMetadata {
return recordDate;
}
+ public String getUnitType() {
+ return unitType;
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -125,15 +131,22 @@ public class InvoiceWithMetadata {
return false;
}
final TrackingIds that = (TrackingIds) o;
+ // !!! Exclude invoiceId on purpose.
+ //
+ // The Set methods (Sets.difference) is used to exclude usage record already invoiced (on a specified invoiceId),
+ // by comparing 2 TrackingIds items with different invoiceId
+ //
return Objects.equal(trackingId, that.trackingId) &&
- Objects.equal(invoiceId, that.invoiceId) &&
+ //Objects.equal(invoiceId, that.invoiceId) &&
Objects.equal(subscriptionId, that.subscriptionId) &&
+ Objects.equal(unitType, that.unitType) &&
Objects.equal(recordDate, that.recordDate);
}
@Override
public int hashCode() {
- return Objects.hashCode(trackingId, invoiceId, subscriptionId, recordDate);
+ // !!! Exclude invoiceId on purpose - see comment above
+ return Objects.hashCode(trackingId, subscriptionId, unitType, recordDate);
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index d461199..d6c43c5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -608,7 +608,7 @@ public class InvoiceDispatcher {
final Set<InvoiceTrackingModelDao> trackingIds = new HashSet<>();
for (InvoiceWithMetadata.TrackingIds cur : invoiceWithMetadata.getTrackingIds()) {
- trackingIds.add(new InvoiceTrackingModelDao(cur.getTrackingId(), cur.getInvoiceId(), cur.getSubscriptionId(), cur.getRecordDate()));
+ trackingIds.add(new InvoiceTrackingModelDao(cur.getTrackingId(), cur.getInvoiceId(), cur.getSubscriptionId(), cur.getUnitType(), cur.getRecordDate()));
}
// Commit invoice on disk
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
index 82e7040..92a9c63 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalCapacityUsageInArrear.java
@@ -56,7 +56,7 @@ public class ContiguousIntervalCapacityUsageInArrear extends ContiguousIntervalU
final UUID accountId,
final UUID invoiceId,
final List<RawUsage> rawSubscriptionUsage,
- final List<TrackingIds> existingTrackingId,
+ final Set<TrackingIds> existingTrackingId,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
final UsageDetailMode usageDetailMode,
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
index 43f2ecf..571a0b5 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableUsageInArrear.java
@@ -24,6 +24,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
@@ -65,7 +66,7 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
final UUID accountId,
final UUID invoiceId,
final List<RawUsage> rawSubscriptionUsage,
- final List<TrackingIds> existingTrackingId,
+ final Set<TrackingIds> existingTrackingId,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
final UsageDetailMode usageDetailMode,
@@ -145,27 +146,25 @@ public class ContiguousIntervalConsumableUsageInArrear extends ContiguousInterva
if (usageDetailMode == UsageDetailMode.DETAIL) {
final UsageConsumableInArrearTierUnitAggregate targetTierUnitDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearTierUnitAggregate>() {});
- if (!targetTierUnitDetail.getTierUnit().equals(unitType)) {
- continue;
+ if (targetTierUnitDetail.getTierUnit().equals(unitType)) {
+ tierDetails.add(new UsageConsumableInArrearTierUnitAggregate(targetTierUnitDetail.getTier(), targetTierUnitDetail.getTierUnit(), bi.getRate(), targetTierUnitDetail.getQuantity(), bi.getQuantity(), bi.getAmount()));
}
-
- tierDetails.add(new UsageConsumableInArrearTierUnitAggregate(targetTierUnitDetail.getTier(), targetTierUnitDetail.getTierUnit(), bi.getRate(), targetTierUnitDetail.getQuantity(), bi.getQuantity(), bi.getAmount()));
} else {
final UsageConsumableInArrearAggregate usageDetail = fromJson(bi.getItemDetails(), new TypeReference<UsageConsumableInArrearAggregate>() {});
- tierDetails.addAll(usageDetail.getTierDetails());
+ for (final UsageConsumableInArrearTierUnitAggregate unitAgg : usageDetail.getTierDetails()) {
+ if (unitAgg.getTierUnit().equals(unitType)) {
+ tierDetails.add(unitAgg);
+ }
+ }
}
}
for (final UsageConsumableInArrearTierUnitAggregate curDetail : tierDetails) {
-
- if (curDetail.getTierUnit().equals(unitType)) {
-
- if (!resultMap.containsKey(curDetail.getTier())) {
- resultMap.put(curDetail.getTier(), curDetail);
- } else {
- final UsageConsumableInArrearTierUnitAggregate perTierDetail = resultMap.get(curDetail.getTier());
- perTierDetail.updateQuantityAndAmount(curDetail.getQuantity());
- }
+ if (!resultMap.containsKey(curDetail.getTier())) {
+ resultMap.put(curDetail.getTier(), curDetail);
+ } else {
+ final UsageConsumableInArrearTierUnitAggregate perTierDetail = resultMap.get(curDetail.getTier());
+ perTierDetail.updateQuantityAndAmount(curDetail.getQuantity());
}
}
return ImmutableList.copyOf(resultMap.values());
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
index bb40b12..ac48286 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalUsageInArrear.java
@@ -61,6 +61,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import static org.killbill.billing.invoice.usage.UsageUtils.getCapacityInArrearUnitTypes;
import static org.killbill.billing.invoice.usage.UsageUtils.getConsumableInArrearUnitTypes;
@@ -79,7 +80,7 @@ public abstract class ContiguousIntervalUsageInArrear {
protected final Usage usage;
protected final Set<String> unitTypes;
protected final List<RawUsage> rawSubscriptionUsage;
- protected final List<TrackingIds> existingTrackingId;
+ protected final Set<TrackingIds> allExistingTrackingIds;
protected final LocalDate targetDate;
protected final UUID accountId;
protected final UUID invoiceId;
@@ -93,7 +94,7 @@ public abstract class ContiguousIntervalUsageInArrear {
final UUID accountId,
final UUID invoiceId,
final List<RawUsage> rawSubscriptionUsage,
- final List<TrackingIds> existingTrackingIds,
+ final Set<TrackingIds> existingTrackingIds,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
final UsageDetailMode usageDetailMode,
@@ -103,7 +104,7 @@ public abstract class ContiguousIntervalUsageInArrear {
this.invoiceId = invoiceId;
this.unitTypes = usage.getUsageType() == UsageType.CAPACITY ? getCapacityInArrearUnitTypes(usage) : getConsumableInArrearUnitTypes(usage);
this.rawSubscriptionUsage = filterInputRawUsage(rawSubscriptionUsage);
- this.existingTrackingId = existingTrackingIds;
+ this.allExistingTrackingIds = existingTrackingIds;
this.targetDate = targetDate;
this.rawUsageStartDate = rawUsageStartDate;
this.internalTenantContext = internalTenantContext;
@@ -113,7 +114,6 @@ public abstract class ContiguousIntervalUsageInArrear {
this.usageDetailMode = usageDetailMode;
}
-
/**
* Builds the transitionTimes associated to that usage section. Those are determined based on billing events for when to start and when to stop,
* the per usage billingPeriod and finally the targetDate.
@@ -175,9 +175,17 @@ public abstract class ContiguousIntervalUsageInArrear {
return new UsageInArrearItemsAndNextNotificationDate(ImmutableList.<InvoiceItem>of(), ImmutableSet.of(), computeNextNotificationDate());
}
- final Set<TrackingIds> trackingIds = new HashSet<TrackingIds>();
final List<InvoiceItem> result = Lists.newLinkedList();
- final List<RolledUpUsage> allUsage = getRolledUpUsage();
+
+ final RolledUpUnitsWithTracking allUsageWithTracking = getRolledUpUsage();
+ final List<RolledUpUsage> allUsage = allUsageWithTracking.getUsage();
+
+ final Set<TrackingIds> allTrackingIds = allUsageWithTracking.getTrackingIds();
+
+ final Set<TrackingIds> existingTrackingIds = extractTrackingIds(allExistingTrackingIds);
+
+ final Set<TrackingIds> newTrackingIds = Sets.difference(allTrackingIds, existingTrackingIds);
+
// Each RolledUpUsage 'ru' is for a specific time period and across all units
for (final RolledUpUsage ru : allUsage) {
@@ -198,9 +206,10 @@ public abstract class ContiguousIntervalUsageInArrear {
final UsageInArrearAggregate toBeBilledUsageDetails = getToBeBilledUsageDetails(rolledUpUnits, billedItems, areAllBilledItemsWithDetails);
final BigDecimal toBeBilledUsage = toBeBilledUsageDetails.getAmount();
populateResults(ru.getStart(), ru.getEnd(), billedUsage, toBeBilledUsage, toBeBilledUsageDetails, areAllBilledItemsWithDetails, isPeriodPreviouslyBilled, result);
+
}
final LocalDate nextNotificationDate = computeNextNotificationDate();
- return new UsageInArrearItemsAndNextNotificationDate(result, trackingIds, nextNotificationDate);
+ return new UsageInArrearItemsAndNextNotificationDate(result, newTrackingIds, nextNotificationDate);
}
protected abstract void populateResults(final LocalDate startDate, final LocalDate endDate, final BigDecimal billedUsage, final BigDecimal toBeBilledUsage, final UsageInArrearAggregate toBeBilledUsageDetails, final boolean areAllBilledItemsWithDetails, final boolean isPeriodPreviouslyBilled, final List<InvoiceItem> result) throws InvoiceApiException;
@@ -239,18 +248,17 @@ public abstract class ContiguousIntervalUsageInArrear {
return result;
}
-
@VisibleForTesting
- List<RolledUpUsage> getRolledUpUsage() {
+ RolledUpUnitsWithTracking getRolledUpUsage() {
final List<RolledUpUsage> result = new ArrayList<RolledUpUsage>();
+ final Set<TrackingIds> trackingIds = new HashSet<>();
final Iterator<RawUsage> rawUsageIterator = rawSubscriptionUsage.iterator();
if (!rawUsageIterator.hasNext()) {
- return getEmptyRolledUpUsage();
+ return new RolledUpUnitsWithTracking(getEmptyRolledUpUsage(), ImmutableSet.of());
}
-
//
// Skip all items before our first transition date
//
@@ -266,7 +274,7 @@ public abstract class ContiguousIntervalUsageInArrear {
// Optimize path where all raw usage items are outside or our transitionTimes range
if (prevRawUsage == null || prevRawUsage.getDate().compareTo(transitionTimes.get(transitionTimes.size() - 1)) >= 0) {
- return getEmptyRolledUpUsage();
+ return new RolledUpUnitsWithTracking(getEmptyRolledUpUsage(), ImmutableSet.of());
}
//
@@ -284,13 +292,13 @@ public abstract class ContiguousIntervalUsageInArrear {
perRangeUnitToAmount.put(unitType, 0L);
}
-
// Start consuming prevRawUsage element if it exists and falls into the range
if (prevRawUsage != null) {
if (prevRawUsage.getDate().compareTo(prevDate) >= 0 && prevRawUsage.getDate().compareTo(curDate) < 0) {
final Long currentAmount = perRangeUnitToAmount.get(prevRawUsage.getUnitType());
final Long updatedAmount = computeUpdatedAmount(currentAmount, prevRawUsage.getAmount());
perRangeUnitToAmount.put(prevRawUsage.getUnitType(), updatedAmount);
+ trackingIds.add(new TrackingIds(prevRawUsage.getTrackingId(), invoiceId, prevRawUsage.getSubscriptionId(), prevRawUsage.getUnitType(), prevRawUsage.getDate()));
prevRawUsage = null;
}
}
@@ -312,6 +320,7 @@ public abstract class ContiguousIntervalUsageInArrear {
final Long currentAmount = perRangeUnitToAmount.get(curRawUsage.getUnitType());
final Long updatedAmount = computeUpdatedAmount(currentAmount, curRawUsage.getAmount());
perRangeUnitToAmount.put(curRawUsage.getUnitType(), updatedAmount);
+ trackingIds.add(new TrackingIds(curRawUsage.getTrackingId(), invoiceId, curRawUsage.getSubscriptionId(), curRawUsage.getUnitType(), curRawUsage.getDate()));
}
}
@@ -326,7 +335,7 @@ public abstract class ContiguousIntervalUsageInArrear {
}
prevDate = curDate;
}
- return result;
+ return new RolledUpUnitsWithTracking(result, trackingIds);
}
private List<RolledUpUsage> getEmptyRolledUpUsage() {
@@ -370,7 +379,18 @@ public abstract class ContiguousIntervalUsageInArrear {
});
return ImmutableList.copyOf(filteredList);
}
-
+
+ private Set<TrackingIds> extractTrackingIds(final Set<TrackingIds> input) {
+
+ return ImmutableSet.copyOf(Iterables.filter(input, new Predicate<TrackingIds>() {
+ @Override
+ public boolean apply(final TrackingIds input) {
+ return input.getSubscriptionId().equals(getSubscriptionId());
+ }
+ }));
+
+ }
+
/**
* @param filteredUsageForInterval the list of invoiceItem to consider
* @return the price amount that was already billed for that period and usage section (across unitTypes)
@@ -449,14 +469,12 @@ public abstract class ContiguousIntervalUsageInArrear {
return billingEvents.get(0).getCurrency();
}
-
public class UsageInArrearItemsAndNextNotificationDate {
private final List<InvoiceItem> invoiceItems;
private final LocalDate nextNotificationDate;
private final Set<TrackingIds> trackingIds;
-
public UsageInArrearItemsAndNextNotificationDate(final List<InvoiceItem> invoiceItems, final Set<TrackingIds> trackingIds, final LocalDate nextNotificationDate) {
this.invoiceItems = invoiceItems;
this.nextNotificationDate = nextNotificationDate;
@@ -476,6 +494,25 @@ public abstract class ContiguousIntervalUsageInArrear {
}
}
+ public static class RolledUpUnitsWithTracking {
+
+ private final List<RolledUpUsage> usage;
+ private final Set<TrackingIds> trackingIds;
+
+ public RolledUpUnitsWithTracking(final List<RolledUpUsage> usage, final Set<TrackingIds> trackingIds) {
+ this.usage = usage;
+ this.trackingIds = trackingIds;
+ }
+
+ public List<RolledUpUsage> getUsage() {
+ return usage;
+ }
+
+ public Set<TrackingIds> getTrackingIds() {
+ return trackingIds;
+ }
+ }
+
protected String toJson(final Object usageInArrearAggregate) {
try {
return objectMapper.writeValueAsString(usageInArrearAggregate);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
index e05e498..d888e63 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/RawUsageOptimizer.java
@@ -79,10 +79,10 @@ public class RawUsageOptimizer {
final List<RawUsage> rawUsageData = usageApi.getRawUsageForAccount(targetStartDate, targetDate, internalCallContext);
final List<InvoiceTrackingModelDao> trackingIds = invoiceDao.getTrackingsByDateRange(targetStartDate, targetDate, internalCallContext);
- final List<TrackingIds> existingTrackingIds = ImmutableList.copyOf(Iterables.transform(trackingIds, new Function<InvoiceTrackingModelDao, TrackingIds>() {
+ final Set<TrackingIds> existingTrackingIds = ImmutableSet.copyOf(Iterables.transform(trackingIds, new Function<InvoiceTrackingModelDao, TrackingIds>() {
@Override
public TrackingIds apply(final InvoiceTrackingModelDao input) {
- return new TrackingIds(input.getTrackingId(), input.getInvoiceId(), input.getSubscriptionId(), input.getRecordDate());
+ return new TrackingIds(input.getTrackingId(), input.getInvoiceId(), input.getSubscriptionId(), input.getUnitType(), input.getRecordDate());
}
}));
return new RawUsageOptimizerResult(targetStartDate, rawUsageData, existingTrackingIds);
@@ -167,9 +167,9 @@ public class RawUsageOptimizer {
private final LocalDate rawUsageStartDate;
private final List<RawUsage> rawUsage;
- private final List<TrackingIds> existingTrackingIds;
+ private final Set<TrackingIds> existingTrackingIds;
- public RawUsageOptimizerResult(final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage, final List<TrackingIds> existingTrackingIds) {
+ public RawUsageOptimizerResult(final LocalDate rawUsageStartDate, final List<RawUsage> rawUsage, final Set<TrackingIds> existingTrackingIds) {
this.rawUsageStartDate = rawUsageStartDate;
this.rawUsage = rawUsage;
this.existingTrackingIds = existingTrackingIds;
@@ -183,7 +183,7 @@ public class RawUsageOptimizer {
return rawUsage;
}
- public List<TrackingIds> getExistingTrackingIds() {
+ public Set<TrackingIds> getExistingTrackingIds() {
return existingTrackingIds;
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
index af2b98a..86d0784 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionUsageInArrear.java
@@ -80,7 +80,7 @@ public class SubscriptionUsageInArrear {
private final List<BillingEvent> subscriptionBillingEvents;
private final LocalDate targetDate;
private final List<RawUsage> rawSubscriptionUsage;
- private final List<TrackingIds> existingTrackingIds;
+ private final Set<TrackingIds> existingTrackingIds;
private final LocalDate rawUsageStartDate;
private final InternalTenantContext internalTenantContext;
private final UsageDetailMode usageDetailMode;
@@ -89,7 +89,7 @@ public class SubscriptionUsageInArrear {
final UUID invoiceId,
final List<BillingEvent> subscriptionBillingEvents,
final List<RawUsage> rawUsage,
- final List<TrackingIds> existingTrackingIds,
+ final Set<TrackingIds> existingTrackingIds,
final LocalDate targetDate,
final LocalDate rawUsageStartDate,
final UsageDetailMode usageDetailMode,
@@ -209,46 +209,36 @@ public class SubscriptionUsageInArrear {
public class SubscriptionUsageInArrearItemsAndNextNotificationDate {
- private List<InvoiceItem> invoiceItems;
- private Map<String, LocalDate> perUsageNotificationDates;
- private Set<TrackingIds> trackingIds;
+ private final List<InvoiceItem> invoiceItems;
+ private final Map<String, LocalDate> perUsageNotificationDates;
+ private final Set<TrackingIds> trackingIds;
public SubscriptionUsageInArrearItemsAndNextNotificationDate() {
- this.invoiceItems = null;
- this.perUsageNotificationDates = null;
+ this.invoiceItems = new LinkedList<InvoiceItem>();
+ this.perUsageNotificationDates = new HashMap<String, LocalDate>();
+ this.trackingIds = new HashSet<>();
}
public void addUsageInArrearItemsAndNextNotificationDate(final String usageName, final UsageInArrearItemsAndNextNotificationDate input) {
if (!input.getInvoiceItems().isEmpty()) {
- if (invoiceItems == null) {
- invoiceItems = new LinkedList<InvoiceItem>();
- }
invoiceItems.addAll(input.getInvoiceItems());
-
}
if (input.getNextNotificationDate() != null) {
- if (perUsageNotificationDates == null) {
- perUsageNotificationDates = new HashMap<String, LocalDate>();
- }
perUsageNotificationDates.put(usageName, input.getNextNotificationDate());
}
}
public void addTrackingIds(final Set<TrackingIds> input) {
- if (trackingIds == null) {
- trackingIds = new HashSet<>();
- }
trackingIds.addAll(input);
-
}
public List<InvoiceItem> getInvoiceItems() {
- return invoiceItems != null ? invoiceItems : ImmutableList.<InvoiceItem>of();
+ return invoiceItems;
}
public Map<String, LocalDate> getPerUsageNotificationDates() {
- return perUsageNotificationDates != null ? perUsageNotificationDates : ImmutableMap.<String, LocalDate>of();
+ return perUsageNotificationDates;
}
public Set<TrackingIds> getTrackingIds() {
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
index 1cd5116..faf56bb 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
@@ -6,6 +6,7 @@ tableFields(prefix) ::= <<
<prefix>tracking_id
, <prefix>invoice_id
, <prefix>subscription_id
+, <prefix>unit_type
, <prefix>record_date
, <prefix>created_by
, <prefix>created_date
@@ -15,6 +16,7 @@ tableValues() ::= <<
:trackingId
, :invoiceId
, :subscriptionId
+, :unitType
, :recordDate
, :userName
, :createdDate
@@ -32,3 +34,15 @@ and <accountRecordIdField("")> = :accountRecordId
<defaultOrderBy("")>
;
>>
+
+getTrackingsForInvoice() ::= <<
+select
+ <allTableFields("")>
+from <tableName()>
+where
+invoice_id = :invoiceId
+and <accountRecordIdField("")> = :accountRecordId
+<AND_CHECK_TENANT("")>
+<defaultOrderBy("")>
+;
+>>
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 3719c53..4fc7087 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -7,6 +7,7 @@ CREATE TABLE invoice_tracking_ids (
tracking_id varchar(128) NOT NULL,
invoice_id varchar(36) NOT NULL,
subscription_id varchar(36),
+ unit_type varchar(255) NOT NULL,
record_date date NOT NULL,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20181129164135__tracking_ids.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20181129164135__tracking_ids.sql
index d540f70..d7b7c18 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20181129164135__tracking_ids.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20181129164135__tracking_ids.sql
@@ -5,6 +5,7 @@ CREATE TABLE invoice_tracking_ids (
tracking_id varchar(128) NOT NULL,
invoice_id varchar(36) NOT NULL,
subscription_id varchar(36),
+ unit_type varchar(255) NOT NULL,
record_date date NOT NULL,
created_by varchar(50) NOT NULL,
created_date datetime NOT NULL,
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
index 82d390c..dbcef7e 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
@@ -40,14 +40,14 @@ public class TestInvoiceTrackingSqlDao extends InvoiceTestSuiteWithEmbeddedDB {
final UUID subscriptionId = UUID.randomUUID();
// Before desired range
- final InvoiceTrackingModelDao input0 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId0", invoiceId1, subscriptionId, startRange.minusDays(1));
+ final InvoiceTrackingModelDao input0 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId0", invoiceId1, subscriptionId, "unit", startRange.minusDays(1));
- final InvoiceTrackingModelDao input1 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId1", invoiceId1, subscriptionId, startRange);
- final InvoiceTrackingModelDao input2 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId2", invoiceId1, subscriptionId, new LocalDate(2018, 8, 5));
- final InvoiceTrackingModelDao input3 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId3", invoiceId2, subscriptionId, new LocalDate(2018, 9, 1));
+ final InvoiceTrackingModelDao input1 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId1", invoiceId1, subscriptionId, "unit", startRange);
+ final InvoiceTrackingModelDao input2 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId2", invoiceId1, subscriptionId, "unit", new LocalDate(2018, 8, 5));
+ final InvoiceTrackingModelDao input3 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId3", invoiceId2, subscriptionId, "unit", new LocalDate(2018, 9, 1));
// After desired range
- final InvoiceTrackingModelDao input4 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId4", invoiceId1, subscriptionId, endRange);
+ final InvoiceTrackingModelDao input4 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId4", invoiceId1, subscriptionId, "unit", endRange);
final List<InvoiceTrackingModelDao> inputs = new ArrayList<>();
inputs.add(input0);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
index 25bce46..4f93d3b 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestContiguousIntervalConsumableInArrear.java
@@ -465,7 +465,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, rawUsage, targetDate, true, eventT0, eventT1, eventT2, eventT3);
- final List<RolledUpUsage> unsortedRolledUpUsage = intervalConsumableInArrear.getRolledUpUsage();
+ final List<RolledUpUsage> unsortedRolledUpUsage = intervalConsumableInArrear.getRolledUpUsage().getUsage();
Assert.assertEquals(unsortedRolledUpUsage.size(), 3);
final List<RolledUpUsage> rolledUpUsage = TEST_ROLLED_UP_FIRST_USAGE_ORDERING.sortedCopy(unsortedRolledUpUsage);
@@ -1029,7 +1029,7 @@ public class TestContiguousIntervalConsumableInArrear extends TestUsageInArrearB
final ContiguousIntervalUsageInArrear intervalConsumableInArrear = createContiguousIntervalConsumableInArrear(usage, rawUsage, targetDate, true, eventT0, eventT1);
- final List<RolledUpUsage> unsortedRolledUpUsage = intervalConsumableInArrear.getRolledUpUsage();
+ final List<RolledUpUsage> unsortedRolledUpUsage = intervalConsumableInArrear.getRolledUpUsage().getUsage();
assertEquals(unsortedRolledUpUsage.size(), 2);
assertEquals(unsortedRolledUpUsage.get(0).getRolledUpUnits().size(), 1);
assertEquals(unsortedRolledUpUsage.get(0).getRolledUpUnits().get(0).getAmount().longValue(), 0L);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
index 43612e6..c9925af 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java
@@ -34,6 +34,7 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import static org.testng.Assert.assertEquals;
@@ -70,7 +71,7 @@ public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
LocalDate targetDate = new LocalDate(2013, 6, 23);
- final SubscriptionUsageInArrear foo = new SubscriptionUsageInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), ImmutableList.of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), usageDetailMode, internalCallContext);
+ final SubscriptionUsageInArrear foo = new SubscriptionUsageInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), ImmutableSet.of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), usageDetailMode, internalCallContext);
final List<ContiguousIntervalUsageInArrear> result = foo.computeInArrearUsageInterval();
assertEquals(result.size(), 3);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
index 0ef0910..a6dd58d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/usage/TestUsageInArrearBase.java
@@ -19,6 +19,7 @@ package org.killbill.billing.invoice.usage;
import java.math.BigDecimal;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import org.joda.time.DateTime;
@@ -51,12 +52,13 @@ import org.mockito.Mockito;
import org.testng.annotations.BeforeClass;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
protected static final String DEFAULT_TRACKING_ID = "_tracking_id_missing";
- protected static final List<TrackingIds> EMPTY_EXISTING_TRACKING_IDS = ImmutableList.of();
+ protected static final Set<TrackingIds> EMPTY_EXISTING_TRACKING_IDS = ImmutableSet.of();
protected int BCD;
protected UUID accountId;