killbill-uncached
Changes
account/pom.xml 2(+1 -1)
api/pom.xml 2(+1 -1)
beatrix/pom.xml 2(+1 -1)
beatrix/src/test/java/org/killbill/billing/beatrix/integration/usage/TestConsumableInArrear.java 74(+74 -0)
catalog/pom.xml 2(+1 -1)
catalog/src/test/resources/catalogTest.xml 30(+30 -0)
currency/pom.xml 2(+1 -1)
entitlement/pom.xml 2(+1 -1)
invoice/pom.xml 2(+1 -1)
invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java 4(+2 -2)
invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java 18(+10 -8)
invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java 8(+4 -4)
invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java 18(+8 -10)
invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java 23(+16 -7)
invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java 14(+12 -2)
invoice/src/test/java/org/killbill/billing/invoice/usage/TestSubscriptionConsumableInArrear.java 6(+5 -1)
jaxrs/pom.xml 2(+1 -1)
junction/pom.xml 2(+1 -1)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java 63(+45 -18)
junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java 17(+8 -9)
NEWS 3(+3 -0)
overdue/pom.xml 2(+1 -1)
payment/pom.xml 2(+1 -1)
pom.xml 4(+2 -2)
profiles/killbill/pom.xml 2(+1 -1)
profiles/killpay/pom.xml 2(+1 -1)
profiles/pom.xml 2(+1 -1)
subscription/pom.xml 2(+1 -1)
tenant/pom.xml 2(+1 -1)
usage/pom.xml 2(+1 -1)
util/pom.xml 2(+1 -1)
Details
account/pom.xml 2(+1 -1)
diff --git a/account/pom.xml b/account/pom.xml
index 19763f2..09a34d0 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-account</artifactId>
api/pom.xml 2(+1 -1)
diff --git a/api/pom.xml b/api/pom.xml
index d902b22..8da04fe 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java b/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
index 432ecb5..6e1828d 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingEventSet.java
@@ -23,6 +23,7 @@ import java.util.UUID;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.Usage;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
public interface BillingEventSet extends SortedSet<BillingEvent> {
@@ -32,5 +33,7 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
public List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
+ public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext();
+
public Map<String, Usage> getUsages();
}
diff --git a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
index f6ea2cd..0265dc1 100644
--- a/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/junction/BillingInternalApi.java
@@ -18,6 +18,7 @@ package org.killbill.billing.junction;
import java.util.UUID;
+import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.invoice.api.DryRunArguments;
@@ -27,5 +28,5 @@ public interface BillingInternalApi {
/**
* @return an ordered list of billing event for the given accounts
*/
- public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, DryRunArguments dryRunArguments, InternalCallContext context) throws CatalogApiException;
+ public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(UUID accountId, DryRunArguments dryRunArguments, InternalCallContext context) throws CatalogApiException, AccountApiException;
}
diff --git a/api/src/main/java/org/killbill/billing/util/AccountDateAndTimeZoneContext.java b/api/src/main/java/org/killbill/billing/util/AccountDateAndTimeZoneContext.java
new file mode 100644
index 0000000..d997be1
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/util/AccountDateAndTimeZoneContext.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.util;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+public interface AccountDateAndTimeZoneContext {
+
+ public LocalDate computeLocalDateFromFixedAccountOffset(final DateTime targetDateTime);
+
+ public DateTime computeUTCDateTimeFromLocalDate(final LocalDate invoiceItemEndDate);
+
+ public DateTimeZone getAccountTimeZone();
+}
+
beatrix/pom.xml 2(+1 -1)
diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 9308dd6..cb151f6 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
index 98c0ce5..8d7f06f 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegration.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.UUID;
import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
@@ -44,6 +45,7 @@ import org.killbill.billing.invoice.api.DryRunType;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.mock.MockAccountBuilder;
import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.payment.api.TransactionStatus;
import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -707,6 +709,5 @@ public class TestIntegration extends TestIntegrationBase {
assertListenerStatus();
checkNoMoreInvoiceToGenerate(account);
-
}
}
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
new file mode 100644
index 0000000..42aab33
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithTimeZones.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.SubscriptionEventType;
+import org.killbill.billing.invoice.api.DryRunType;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.mock.MockAccountBuilder;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestWithTimeZones extends TestIntegrationBase {
+
+ // Verify that recurring invoice items are correctly computed although we went through and out of daylight saving transitions
+ @Test(groups = "slow")
+ public void testWithDayLightSaving() throws Exception {
+
+ // Start with a date in daylight saving period and make sure we use a time of 8 hour so that we we reach standard time
+ // the next month where the difference is 9 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
+ //
+ clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
+ final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+ .firstNameLength(6)
+ .email(UUID.randomUUID().toString().substring(1, 8))
+ .phone(UUID.randomUUID().toString().substring(1, 8))
+ .migrated(false)
+ .isNotifiedForInvoices(false)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(1)
+ .currency(Currency.USD)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(tz)
+ .build();
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ final List<ExpectedInvoiceItemCheck> expectedInvoices = new ArrayList<ExpectedInvoiceItemCheck>();
+
+ TestDryRunArguments dryRun = new TestDryRunArguments(DryRunType.SUBSCRIPTION_ACTION, "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, null, null,
+ SubscriptionEventType.START_BILLING, null, null, clock.getUTCNow(), null);
+ Invoice dryRunInvoice = invoiceUserApi.triggerInvoiceGeneration(account.getId(), clock.getUTCToday(), dryRun, callContext);
+ expectedInvoices.add(new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ invoiceChecker.checkInvoiceNoAudits(dryRunInvoice, callContext, expectedInvoices);
+
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ // Check bundle after BP got created otherwise we get an error from auditApi.
+ subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, expectedInvoices);
+ expectedInvoices.clear();
+
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+ clock.addDays(30);
+ assertListenerStatus();
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2015, 10, 1), new LocalDate(2015, 11, 1), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+ LocalDate startDate = new LocalDate(2015, 11, 1);
+ // We loop 18 times to go over a year and transitions several times between winter and summer (daylight saving)
+ for (int i = 0; i < 18; i++) {
+ LocalDate endDate = startDate.plusMonths(1);
+
+ busHandler.pushExpectedEvents(NextEvent.INVOICE, NextEvent.PAYMENT);
+ clock.addMonths(1);
+ assertListenerStatus();
+ invoiceChecker.checkInvoice(account.getId(), i + 3, callContext,
+ new ExpectedInvoiceItemCheck(startDate, endDate, InvoiceItemType.RECURRING, new BigDecimal("249.95")));
+
+ startDate = endDate;
+ }
+ }
+
+ // Verify cancellation logic when we exit daylight savind period
+ @Test(groups = "slow")
+ public void testCancellationFrom_PDT_to_PST() throws Exception {
+
+ // Start with a date in daylight saving period (PDT) and make sure we use a time of 7 hour so that we we reach standard time (PST)
+ // the next month where the difference is 8 hours, a transformation from DateTime to LocalDate with the account time zone would bring us a day earlier
+ // (e.g new LocalDate("2015-12-01T07:01:01.000Z", tz) -> "2015-11-30.
+ clock.setTime(new DateTime("2015-11-01T07:01:01.000Z"));
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+ .firstNameLength(6)
+ .email(UUID.randomUUID().toString().substring(1, 8))
+ .phone(UUID.randomUUID().toString().substring(1, 8))
+ .migrated(false)
+ .isNotifiedForInvoices(false)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(1)
+ .currency(Currency.USD)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(tz)
+ .build();
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+ final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+ Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Cancel the next month specifying just a LocalDate
+ final LocalDate cancellationDate = new LocalDate("2015-12-01", tz);
+ entitlement = entitlement.cancelEntitlementWithDate(cancellationDate, true, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Verify first entitlement is correctly cancelled on the right date
+ Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
+
+ // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ Assert.assertEquals(invoices.size(), 1);
+ }
+
+ // Same test as previous test but this time going from PST -> PDT (somehow not too interesting in that direction because we start with
+ // an offset of 8 hours and then go through 7 hours so anyway we would stay in the same day.
+ @Test(groups = "slow")
+ public void testCancellationFrom_PST_to_PDT() throws Exception {
+
+ clock.setTime(new DateTime("2015-02-01T08:01:01.000Z"));
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Los_Angeles");
+ final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+ .firstNameLength(6)
+ .email(UUID.randomUUID().toString().substring(1, 8))
+ .phone(UUID.randomUUID().toString().substring(1, 8))
+ .migrated(false)
+ .isNotifiedForInvoices(false)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(1)
+ .currency(Currency.USD)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(tz)
+ .build();
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ busHandler.pushExpectedEvents(NextEvent.CREATE, NextEvent.INVOICE, NextEvent.PAYMENT);
+ final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Blowdart", ProductCategory.BASE, BillingPeriod.MONTHLY, "notrial", null);
+ final LocalDate effectiveDate = new LocalDate(clock.getUTCNow());
+ Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, "Something", ImmutableList.<PlanPhasePriceOverride>of(), effectiveDate, ImmutableList.<PluginProperty>of(), callContext);
+ assertListenerStatus();
+
+ // Cancel the next month specifying just a LocalDate
+ final LocalDate cancellationDate = new LocalDate("2015-03-01", tz);
+ entitlement = entitlement.cancelEntitlementWithDate(cancellationDate, true, ImmutableList.<PluginProperty>of(), callContext);
+
+ // Verify first entitlement is correctly cancelled on the right date
+ Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate);
+
+ // Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01"
+ List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(account.getId(), callContext);
+ Assert.assertEquals(invoices.size(), 1);
+ }
+
+}
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 6713be1..034573b 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
@@ -19,8 +19,11 @@ package org.killbill.billing.beatrix.integration.usage;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.account.api.AccountData;
@@ -29,14 +32,19 @@ import org.killbill.billing.beatrix.integration.TestIntegrationBase;
import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
import org.killbill.billing.catalog.api.BillingActionPolicy;
import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.ProductCategory;
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.mock.MockAccountBuilder;
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.killbill.billing.util.callcontext.CallContext;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.tweak.HandleCallback;
+import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -170,6 +178,72 @@ public class TestConsumableInArrear extends TestIntegrationBase {
}
+ @Test(groups = "slow")
+ public void testWithDayLightSaving() throws Exception {
+
+ clock.setTime(new DateTime("2015-09-01T08:01:01.000Z"));
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
+ final AccountData accountData = new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+ .firstNameLength(6)
+ .email(UUID.randomUUID().toString().substring(1, 8))
+ .phone(UUID.randomUUID().toString().substring(1, 8))
+ .migrated(false)
+ .isNotifiedForInvoices(false)
+ .externalKey(UUID.randomUUID().toString().substring(1, 8))
+ .billingCycleDayLocal(1)
+ .currency(Currency.USD)
+ .paymentMethodId(UUID.randomUUID())
+ .timeZone(tz)
+ .build();
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ //
+ // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+ //
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, NextEvent.CREATE, NextEvent.INVOICE);
+ // Check bundle after BP got created otherwise we get an error from auditApi.
+ subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2015, 9, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ assertListenerStatus();
+
+ //
+ // ADD ADD_ON ON THE SAME DAY
+ //
+ final DefaultEntitlement aoSubscription = addAOEntitlementAndCheckForCompletion(bpSubscription.getBundleId(), "Bullets", ProductCategory.ADD_ON, BillingPeriod.NO_BILLING_PERIOD, NextEvent.CREATE);
+ assertListenerStatus();
+
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.PAYMENT);
+ clock.addDays(30);
+ assertListenerStatus();
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2015, 10, 1), new LocalDate(2016, 10, 1), InvoiceItemType.RECURRING, new BigDecimal("2399.95")));
+
+ // 2015-11-1
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ // 2015-12-1
+ clock.addMonths(1);
+ assertListenerStatus();
+
+ // We sleep to let system creates lots of notification if an infinite loop was indeed happening
+ Thread.sleep(3000);
+ // And then we check that we only have the expected number of notifications in the history table.
+ final Integer countNotifications = dbi.withHandle(new HandleCallback<Integer>() {
+ @Override
+ public Integer withHandle(final Handle handle) throws Exception {
+
+ List<Map<String, Object>> res = handle.select("select count(*) as count from notifications_history;");
+ final Integer count = Integer.valueOf(res.get(0).get("count").toString());
+ return count;
+ }
+ }
+ );
+ Assert.assertEquals(countNotifications.intValue(), 4);
+ }
+
private void setUsage(final UUID subscriptionId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) {
final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
usageRecords.add(new UsageRecord(startDate, amount));
catalog/pom.xml 2(+1 -1)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index babfcfa..73321a6 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-catalog</artifactId>
catalog/src/test/resources/catalogTest.xml 30(+30 -0)
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index b45b587..56576c4 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -189,6 +189,31 @@
</rules>
<plans>
+ <plan name="blowdart-monthly-notrial">
+ <product>Blowdart</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <recurring>
+ <billingPeriod>MONTHLY</billingPeriod>
+ <recurringPrice>
+ <price>
+ <currency>USD</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>EUR</currency>
+ <value>29.95</value>
+ </price>
+ <price>
+ <currency>GBP</currency>
+ <value>29.95</value>
+ </price>
+ </recurringPrice>
+ </recurring>
+ </finalPhase>
+ </plan>
<plan name="blowdart-monthly">
<product>Blowdart</product>
<initialPhases>
@@ -1108,6 +1133,11 @@
<plan>assault-rifle-annual-rescue</plan>
</plans>
</childPriceList>
+ <childPriceList name="notrial">
+ <plans>
+ <plan>blowdart-monthly-notrial</plan>
+ </plans>
+ </childPriceList>
</priceLists>
</catalog>
currency/pom.xml 2(+1 -1)
diff --git a/currency/pom.xml b/currency/pom.xml
index d9dc4d1..cbd0abd 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-currency</artifactId>
entitlement/pom.xml 2(+1 -1)
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 34c7467..da6cc59 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-entitlement</artifactId>
invoice/pom.xml 2(+1 -1)
diff --git a/invoice/pom.xml b/invoice/pom.xml
index 2d1e0dc..857e2b7 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
index ba2227e..b537bff 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/migration/DefaultInvoiceMigrationApi.java
@@ -25,7 +25,7 @@ import org.joda.time.LocalDate;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications;
import org.killbill.billing.invoice.InvoiceDispatcher.FutureAccountNotifications.SubscriptionNotification;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -82,7 +82,7 @@ public class DefaultInvoiceMigrationApi implements InvoiceMigrationApi {
targetDate, null, balance, null, currency, null);
final DateTime wrongEffectiveDateButDoesNotMatter = null;
- final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(wrongEffectiveDateButDoesNotMatter, account.getTimeZone(), clock);
+ final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(wrongEffectiveDateButDoesNotMatter, account.getTimeZone());
dao.createInvoice(migrationInvoice, ImmutableList.<InvoiceItemModelDao>of(migrationInvoiceItem),
true, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContextFactory.createInternalCallContext(accountId, context));
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
index c9a290a..8fa4886 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/DefaultInvoiceGenerator.java
@@ -74,7 +74,9 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
validateTargetDate(targetDate);
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);
- final Invoice invoice = new DefaultInvoice(account.getId(), new LocalDate(clock.getUTCNow(), account.getTimeZone()), adjustedTargetDate, targetCurrency);
+ // STEPH ?
+ final LocalDate invoiceDate = events.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(clock.getUTCNow());
+ final Invoice invoice = new DefaultInvoice(account.getId(), invoiceDate, adjustedTargetDate, targetCurrency);
final UUID invoiceId = invoice.getId();
final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates = new HashMap<UUID, SubscriptionFutureNotificationDates>();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
index 940f746..0c8f03a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/FixedAndRecurringInvoiceItemGenerator.java
@@ -45,6 +45,7 @@ import org.killbill.billing.invoice.model.RecurringInvoiceItemDataWithNextBillin
import org.killbill.billing.invoice.tree.AccountItemTree;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.currency.KillBillMoney;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -111,11 +112,11 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
if (!events.getSubscriptionIdsWithAutoInvoiceOff().
contains(thisEvent.getSubscription().getId())) { // don't consider events for subscriptions that have auto_invoice_off
final BillingEvent adjustedNextEvent = (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) ? nextEvent : null;
- final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate);
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, thisEvent, adjustedNextEvent, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, events.getAccountDateAndTimeZoneContext());
proposedItems.addAll(newProposedItems);
}
}
- final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate);
+ final List<InvoiceItem> newProposedItems = processRecurringEvent(invoiceId, accountId, nextEvent, null, targetDate, currency, logStringBuilder, events.getRecurringBillingMode(), perSubscriptionFutureNotificationDate, events.getAccountDateAndTimeZoneContext());
proposedItems.addAll(newProposedItems);
log.info(logStringBuilder.toString());
@@ -128,7 +129,7 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
while (eventIt.hasNext()) {
final BillingEvent thisEvent = eventIt.next();
- final InvoiceItem fixedPriceInvoiceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency);
+ final InvoiceItem fixedPriceInvoiceItem = generateFixedPriceItem(invoiceId, accountId, thisEvent, targetDate, currency, events.getAccountDateAndTimeZoneContext());
if (fixedPriceInvoiceItem != null) {
proposedItems.add(fixedPriceInvoiceItem);
}
@@ -140,16 +141,17 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
private List<InvoiceItem> processRecurringEvent(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent, @Nullable final BillingEvent nextEvent,
final LocalDate targetDate, final Currency currency,
final StringBuilder logStringBuilder, final BillingMode billingMode,
- final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate) throws InvoiceApiException {
+ final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDate,
+ final AccountDateAndTimeZoneContext dateAndTimeZoneContext) throws InvoiceApiException {
final List<InvoiceItem> items = new ArrayList<InvoiceItem>();
// Handle recurring items
final BillingPeriod billingPeriod = thisEvent.getBillingPeriod();
if (billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
- final LocalDate startDate = new LocalDate(thisEvent.getEffectiveDate(), thisEvent.getTimeZone());
+ final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
if (!startDate.isAfter(targetDate)) {
- final LocalDate endDate = (nextEvent == null) ? null : new LocalDate(nextEvent.getEffectiveDate(), nextEvent.getTimeZone());
+ final LocalDate endDate = (nextEvent == null) ? null : dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
final int billCycleDayLocal = thisEvent.getBillCycleDayLocal();
@@ -316,8 +318,8 @@ public class FixedAndRecurringInvoiceItemGenerator extends InvoiceItemGenerator
}
private InvoiceItem generateFixedPriceItem(final UUID invoiceId, final UUID accountId, final BillingEvent thisEvent,
- final LocalDate targetDate, final Currency currency) {
- final LocalDate roundedStartDate = new LocalDate(thisEvent.getEffectiveDate(), thisEvent.getTimeZone());
+ final LocalDate targetDate, final Currency currency, final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
+ final LocalDate roundedStartDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
if (roundedStartDate.isAfter(targetDate)) {
return null;
} else {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
index b19e774..5051892 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/generator/UsageInvoiceItemGenerator.java
@@ -94,7 +94,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
while (events.hasNext()) {
final BillingEvent event = events.next();
// Skip events that are posterior to the targetDate
- final LocalDate eventLocalEffectiveDate = new LocalDate(event.getEffectiveDate(), account.getTimeZone());
+ final LocalDate eventLocalEffectiveDate = eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(event.getEffectiveDate());
if (eventLocalEffectiveDate.isAfter(targetDate)) {
continue;
}
@@ -118,7 +118,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
final UUID subscriptionId = event.getSubscription().getId();
if (curSubscriptionId != null && !curSubscriptionId.equals(subscriptionId)) {
- final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate());
+ final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), eventSet.getAccountDateAndTimeZoneContext());
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
@@ -131,7 +131,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
curEvents.add(event);
}
if (curSubscriptionId != null) {
- final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate());
+ final SubscriptionConsumableInArrear subscriptionConsumableInArrear = new SubscriptionConsumableInArrear(account.getId(), invoiceId, curEvents, rawUsageOptimizerResult.getRawUsage(), targetDate, rawUsageOptimizerResult.getRawUsageStartDate(), eventSet.getAccountDateAndTimeZoneContext());
final List<InvoiceItem> consumableInUsageArrearItems = perSubscriptionConsumableInArrearUsageItems.get(curSubscriptionId);
final SubscriptionConsumableInArrearItemsAndNextNotificationDate subscriptionResult = subscriptionConsumableInArrear.computeMissingUsageInvoiceItems(consumableInUsageArrearItems != null ? consumableInUsageArrearItems : ImmutableList.<InvoiceItem>of());
@@ -156,7 +156,7 @@ public class UsageInvoiceItemGenerator extends InvoiceItemGenerator {
minDate = cur.getEffectiveDate();
}
}
- return new LocalDate(minDate, accountTimeZone);
+ return eventSet.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(minDate);
}
private void updatePerSubscriptionNextNotificationUsageDate(final UUID subscriptionId, final Map<String, LocalDate> nextBillingCycleDates, final BillingMode usageBillingMode, final Map<UUID, SubscriptionFutureNotificationDates> perSubscriptionFutureNotificationDates) {
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 8ddc243..cf7c200 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -83,12 +83,12 @@ import org.killbill.billing.junction.BillingInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.config.InvoiceConfig;
import org.killbill.billing.util.globallocker.LockerType;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
import org.killbill.clock.Clock;
@@ -248,7 +248,10 @@ public class InvoiceDispatcher {
}
}
return null;
- } catch (CatalogApiException e) {
+ } catch (final CatalogApiException e) {
+ log.error("Failed handling SubscriptionBase change.", e);
+ return null;
+ } catch (final AccountApiException e) {
log.error("Failed handling SubscriptionBase change.", e);
return null;
}
@@ -297,8 +300,6 @@ public class InvoiceDispatcher {
try {
final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
- final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(billingEvents.iterator().next().getEffectiveDate(), account.getTimeZone(), clock);
-
final List<Invoice> invoices = billingEvents.isAccountAutoInvoiceOff() ?
ImmutableList.<Invoice>of() :
ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(context),
@@ -310,12 +311,12 @@ public class InvoiceDispatcher {
}));
final Currency targetCurrency = account.getCurrency();
- final LocalDate targetDate = dateAndTimeZoneContext.computeTargetDate(targetDateTime);
+ final LocalDate targetDate = billingEvents.getAccountDateAndTimeZoneContext().computeLocalDateFromFixedAccountOffset(targetDateTime);
final InvoiceWithMetadata invoiceWithMetadata = generator.generateInvoice(account, billingEvents, invoices, targetDate, targetCurrency, context);
final Invoice invoice = invoiceWithMetadata.getInvoice();
// Compute future notifications
- final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, dateAndTimeZoneContext, context);
+ final FutureAccountNotifications futureAccountNotifications = createNextFutureNotificationDate(invoiceWithMetadata, billingEvents.getAccountDateAndTimeZoneContext(), context);
//
@@ -365,7 +366,7 @@ public class InvoiceDispatcher {
final boolean isRealInvoiceWithNonEmptyItems = isThereAnyItemsLeft ? isRealInvoiceWithItems : false;
- setChargedThroughDates(dateAndTimeZoneContext, invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
+ setChargedThroughDates(billingEvents.getAccountDateAndTimeZoneContext(), invoice.getInvoiceItems(FixedPriceInvoiceItem.class), invoice.getInvoiceItems(RecurringInvoiceItem.class), context);
// TODO we should send bus events when we commit the ionvoice on disk in commitInvoice
postEvents(account, invoice, adjustedUniqueOtherInvoiceId, isRealInvoiceWithNonEmptyItems, context);
@@ -382,7 +383,7 @@ public class InvoiceDispatcher {
}
}
- private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final DateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) {
+ private FutureAccountNotifications createNextFutureNotificationDate(final InvoiceWithMetadata invoiceWithMetadata, final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final InternalCallContext context) {
final Map<UUID, List<SubscriptionNotification>> result = new HashMap<UUID, List<SubscriptionNotification>>();
@@ -537,7 +538,7 @@ public class InvoiceDispatcher {
return internalCallContextFactory.createCallContext(context);
}
- private void setChargedThroughDates(final DateAndTimeZoneContext dateAndTimeZoneContext,
+ private void setChargedThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
final Collection<InvoiceItem> fixedPriceItems,
final Collection<InvoiceItem> recurringItems,
final InternalCallContext context) throws SubscriptionBaseApiException {
@@ -561,7 +562,7 @@ public class InvoiceDispatcher {
}
}
- private void addInvoiceItemsToChargeThroughDates(final DateAndTimeZoneContext dateAndTimeZoneContext,
+ private void addInvoiceItemsToChargeThroughDates(final AccountDateAndTimeZoneContext dateAndTimeZoneContext,
final Map<UUID, DateTime> chargeThroughDates,
final Collection<InvoiceItem> items) {
@@ -582,16 +583,16 @@ public class InvoiceDispatcher {
public static class FutureAccountNotifications {
- private final DateAndTimeZoneContext accountDateAndTimeZoneContext;
+ private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
private final Map<UUID, List<SubscriptionNotification>> notifications;
- public FutureAccountNotifications(final DateAndTimeZoneContext accountDateAndTimeZoneContext, final Map<UUID, List<SubscriptionNotification>> notifications) {
- this.accountDateAndTimeZoneContext = accountDateAndTimeZoneContext;
+ public FutureAccountNotifications(final AccountDateAndTimeZoneContext dateAndTimeZoneContext, final Map<UUID, List<SubscriptionNotification>> notifications) {
+ this.dateAndTimeZoneContext = dateAndTimeZoneContext;
this.notifications = notifications;
}
- public DateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
- return accountDateAndTimeZoneContext;
+ public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
+ return dateAndTimeZoneContext;
}
public Map<UUID, List<SubscriptionNotification>> getNotifications() {
@@ -690,6 +691,7 @@ public class InvoiceDispatcher {
public BillingActionPolicy getBillingActionPolicy() {
return null;
}
+
@Override
public List<PlanPhasePriceOverride> getPlanPhasePriceOverrides() {
return null;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
index 4a04504..955801c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/DefaultNextBillingDatePoster.java
@@ -26,8 +26,8 @@ import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.invoice.api.DefaultInvoiceService;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
import org.killbill.notificationq.api.NotificationEventWithMetadata;
import org.killbill.notificationq.api.NotificationQueue;
import org.killbill.notificationq.api.NotificationQueueService;
@@ -52,35 +52,33 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
@Override
public void insertNextBillingNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+ final UUID subscriptionId, final DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.FALSE, futureNotificationTime, futureNotificationTime, accountDateAndTimeZoneContext, internalCallContext);
}
-
@Override
public void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+ final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
insertNextBillingFromTransactionInternal(entitySqlDaoWrapperFactory, accountId, subscriptionId, Boolean.TRUE, futureNotificationTime, targetDate, accountDateAndTimeZoneContext, internalCallContext);
}
-
private void insertNextBillingFromTransactionInternal(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
+ final UUID subscriptionId, final Boolean isDryRunForInvoiceNotification, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext) {
final NotificationQueue nextBillingQueue;
try {
nextBillingQueue = notificationQueueService.getNotificationQueue(DefaultInvoiceService.INVOICE_SERVICE_NAME,
DefaultNextBillingDateNotifier.NEXT_BILLING_DATE_NOTIFIER_QUEUE);
// If we see existing notification for the same date (and isDryRunForInvoiceNotification mode), we don't insert a new notification
- final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications = nextBillingQueue.getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection());
+ final List<NotificationEventWithMetadata<NextBillingDateNotificationKey>> futureNotifications = nextBillingQueue.getFutureNotificationFromTransactionForSearchKeys(internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(), entitySqlDaoWrapperFactory.getHandle().getConnection());
final NotificationEventWithMetadata<NextBillingDateNotificationKey> existingFutureNotificationWithSameDate = Iterables.tryFind(futureNotifications, new Predicate<NotificationEventWithMetadata<NextBillingDateNotificationKey>>() {
@Override
public boolean apply(final NotificationEventWithMetadata<NextBillingDateNotificationKey> input) {
final boolean isEventDryRunForNotifications = input.getEvent().isDryRunForInvoiceNotification() != null ?
input.getEvent().isDryRunForInvoiceNotification() : false;
- final LocalDate notificationEffectiveLocaleDate = new LocalDate(futureNotificationTime, accountDateAndTimeZoneContext.getAccountTimeZone());
- final LocalDate eventEffectiveLocaleDate = new LocalDate(input.getEffectiveDate(), accountDateAndTimeZoneContext.getAccountTimeZone());
+ final LocalDate notificationEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(futureNotificationTime);
+ final LocalDate eventEffectiveLocaleDate = accountDateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(input.getEffectiveDate());
return notificationEffectiveLocaleDate.compareTo(eventEffectiveLocaleDate) == 0 &&
((isDryRunForInvoiceNotification && isEventDryRunForNotifications) ||
@@ -95,7 +93,7 @@ public class DefaultNextBillingDatePoster implements NextBillingDatePoster {
new NextBillingDateNotificationKey(subscriptionId, targetDate, isDryRunForInvoiceNotification), internalCallContext.getUserToken(),
internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId());
} else if (log.isDebugEnabled()) {
- log.debug("********************* SKIPPING Queuing next billing date notification at {} for subscriptionId {} *******************", futureNotificationTime.toString(), subscriptionId.toString());
+ log.debug("********************* SKIPPING Queuing next billing date notification at {} for subscriptionId {} *******************", futureNotificationTime.toString(), subscriptionId.toString());
}
} catch (final NoSuchNotificationQueue e) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java b/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
index 233191e..011dc35 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/notification/NextBillingDatePoster.java
@@ -22,15 +22,15 @@ import java.util.UUID;
import org.joda.time.DateTime;
import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
public interface NextBillingDatePoster {
void insertNextBillingNotificationFromTransaction(EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, UUID accountId,
- UUID subscriptionId, DateTime futureNotificationTime, final DateAndTimeZoneContext accountDateAndTimeZoneContext, InternalCallContext internalCallContext);
+ UUID subscriptionId, DateTime futureNotificationTime, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, InternalCallContext internalCallContext);
void insertNextBillingDryRunNotificationFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
- final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final DateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext);
+ final UUID subscriptionId, final DateTime futureNotificationTime, final DateTime targetDate, final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext, final InternalCallContext internalCallContext);
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
index 16a3d38..592f05e 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/ContiguousIntervalConsumableInArrear.java
@@ -41,6 +41,7 @@ import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.usage.RawUsage;
import org.killbill.billing.usage.api.RolledUpUnit;
import org.killbill.billing.usage.api.RolledUpUsage;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,8 +74,15 @@ public class ContiguousIntervalConsumableInArrear {
private final UUID invoiceId;
private final AtomicBoolean isBuilt;
private final LocalDate rawUsageStartDate;
-
- public ContiguousIntervalConsumableInArrear(final Usage usage, final UUID accountId, final UUID invoiceId, final List<RawUsage> rawSubscriptionUsage, final LocalDate targetDate, final LocalDate rawUsageStartDate) {
+ private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+
+ public ContiguousIntervalConsumableInArrear(final Usage usage,
+ final UUID accountId,
+ final UUID invoiceId,
+ final List<RawUsage> rawSubscriptionUsage,
+ final LocalDate targetDate,
+ final LocalDate rawUsageStartDate,
+ final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
this.usage = usage;
this.accountId = accountId;
this.invoiceId = invoiceId;
@@ -82,6 +90,7 @@ public class ContiguousIntervalConsumableInArrear {
this.rawSubscriptionUsage = rawSubscriptionUsage;
this.targetDate = targetDate;
this.rawUsageStartDate = rawUsageStartDate;
+ this.dateAndTimeZoneContext = dateAndTimeZoneContext;
this.billingEvents = Lists.newLinkedList();
this.transitionTimes = Lists.newLinkedList();
this.isBuilt = new AtomicBoolean(false);
@@ -103,11 +112,11 @@ public class ContiguousIntervalConsumableInArrear {
Preconditions.checkState((!closedInterval && billingEvents.size() >= 1) ||
(closedInterval && billingEvents.size() >= 2));
- final LocalDate startDate = new LocalDate(billingEvents.get(0).getEffectiveDate(), getAccountTimeZone());
+ final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(0).getEffectiveDate());
if (targetDate.isBefore(startDate)) {
return this;
}
- final LocalDate endDate = closedInterval ? new LocalDate(billingEvents.get(billingEvents.size() - 1).getEffectiveDate(), getAccountTimeZone()) : targetDate;
+ final LocalDate endDate = closedInterval ? dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(billingEvents.get(billingEvents.size() - 1).getEffectiveDate()) : targetDate;
final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, getBCD(), usage.getBillingPeriod(), usage.getBillingMode());
@@ -221,15 +230,15 @@ public class ContiguousIntervalConsumableInArrear {
while (eventIt.hasNext()) {
final BillingEvent thisEvent = nextEvent;
nextEvent = eventIt.next();
- final LocalDate startDate = new LocalDate(thisEvent.getEffectiveDate(), thisEvent.getTimeZone());
- final LocalDate endDate = new LocalDate(nextEvent.getEffectiveDate(), nextEvent.getTimeZone());
+ final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(thisEvent.getEffectiveDate());
+ final LocalDate endDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, endDate, targetDate, thisEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR);
final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate();
result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result;
}
- final LocalDate startDate = new LocalDate(nextEvent.getEffectiveDate(), nextEvent.getTimeZone());
+ final LocalDate startDate = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(nextEvent.getEffectiveDate());
final BillingIntervalDetail bid = new BillingIntervalDetail(startDate, null, targetDate, nextEvent.getBillCycleDayLocal(), usage.getBillingPeriod(), BillingMode.IN_ARREAR);
final LocalDate nextBillingCycleDate = bid.getNextBillingCycleDate();
result = (result == null || result.compareTo(nextBillingCycleDate) < 0) ? nextBillingCycleDate : result;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
index 9a531a7..6db0640 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/usage/SubscriptionConsumableInArrear.java
@@ -35,6 +35,7 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.usage.ContiguousIntervalConsumableInArrear.ConsumableInArrearItemsAndNextNotificationDate;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.usage.RawUsage;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
@@ -74,13 +75,22 @@ public class SubscriptionConsumableInArrear {
private final LocalDate targetDate;
private final List<RawUsage> rawSubscriptionUsage;
private final LocalDate rawUsageStartDate;
+ private final AccountDateAndTimeZoneContext dateAndTimeZoneContext;
+
+ public SubscriptionConsumableInArrear(final UUID accountId,
+ final UUID invoiceId,
+ final List<BillingEvent> subscriptionBillingEvents,
+ final List<RawUsage> rawUsage,
+ final LocalDate targetDate,
+ final LocalDate rawUsageStartDate,
+ final AccountDateAndTimeZoneContext dateAndTimeZoneContext) {
- public SubscriptionConsumableInArrear(final UUID accountId, final UUID invoiceId, final List<BillingEvent> subscriptionBillingEvents, final List<RawUsage> rawUsage, final LocalDate targetDate, final LocalDate rawUsageStartDate) {
this.accountId = accountId;
this.invoiceId = invoiceId;
this.subscriptionBillingEvents = subscriptionBillingEvents;
this.targetDate = targetDate;
this.rawUsageStartDate = rawUsageStartDate;
+ this.dateAndTimeZoneContext= dateAndTimeZoneContext;
// Extract raw usage for that subscription and sort it by date
this.rawSubscriptionUsage = Ordering.<RawUsage>from(RAW_USAGE_DATE_COMPARATOR).sortedCopy(Iterables.filter(rawUsage, new Predicate<RawUsage>() {
@Override
@@ -138,7 +148,7 @@ public class SubscriptionConsumableInArrear {
// Add inflight usage interval if non existent
ContiguousIntervalConsumableInArrear existingInterval = inFlightInArrearUsageIntervals.get(usage.getName());
if (existingInterval == null) {
- existingInterval = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, targetDate, rawUsageStartDate);
+ existingInterval = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawSubscriptionUsage, targetDate, rawUsageStartDate, dateAndTimeZoneContext);
inFlightInArrearUsageIntervals.put(usage.getName(), existingInterval);
}
// Add billing event for that usage interval
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
index d93cf5b..356cb3d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/MockBillingEventSet.java
@@ -17,28 +17,59 @@
package org.killbill.billing.invoice;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.Usage;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
public class MockBillingEventSet extends TreeSet<BillingEvent> implements BillingEventSet {
private static final long serialVersionUID = 1L;
private boolean isAccountInvoiceOff;
- private List<UUID> subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
+ private List<UUID> subscriptionIdsWithAutoInvoiceOff;
+ private AccountDateAndTimeZoneContext accountDateAndTimeZoneContext;
+
+ public MockBillingEventSet() {
+ super();
+ this.isAccountInvoiceOff = false;
+ this.subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
+ }
+
+ @Override
+ public boolean add(final BillingEvent e) {
+ if (accountDateAndTimeZoneContext == null) {
+ this.accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(e.getEffectiveDate(), DateTimeZone.UTC);
+ }
+ return super.add(e);
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends BillingEvent> all) {
+ if (accountDateAndTimeZoneContext == null) {
+ this.accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(all.iterator().next().getEffectiveDate(), DateTimeZone.UTC);
+ }
+ return super.addAll(all);
+ }
+
public void addSubscriptionWithAutoInvoiceOff(final UUID subscriptionId) {
subscriptionIdsWithAutoInvoiceOff.add(subscriptionId);
}
+
@Override
public boolean isAccountAutoInvoiceOff() {
return isAccountInvoiceOff;
@@ -55,6 +86,11 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
}
@Override
+ public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
+ return accountDateAndTimeZoneContext;
+ }
+
+ @Override
public Map<String, Usage> getUsages() {
return Collections.emptyMap();
}
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
index bd3103c..0f90be1 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/proRations/InvoiceTestUtils.java
@@ -42,7 +42,7 @@ import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
import org.killbill.billing.invoice.model.FixedPriceInvoiceItem;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.killbill.clock.Clock;
import org.mockito.Mockito;
import org.testng.Assert;
@@ -106,7 +106,7 @@ public class InvoiceTestUtils {
}
Mockito.when(invoice.getInvoiceItems()).thenReturn(invoiceItems);
- final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC, clock);
+ final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC);
invoiceDao.createInvoice(new InvoiceModelDao(invoice), invoiceModelItems, true, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
index 0ff2d28..c360655 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/TestInvoiceHelper.java
@@ -58,7 +58,6 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceNotifier;
import org.killbill.billing.invoice.api.InvoicePayment;
-import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceItemSqlDao;
@@ -81,7 +80,7 @@ import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.InvoiceConfig;
import org.killbill.billing.util.currency.KillBillMoney;
-import org.killbill.billing.util.timezone.DateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.killbill.clock.Clock;
import org.killbill.commons.locker.GlobalLocker;
import org.mockito.Mockito;
@@ -286,7 +285,7 @@ public class TestInvoiceHelper {
}));
// The test does not use the invoice callback notifier hence the empty map
- final DateAndTimeZoneContext dateAndTimeZoneContext = new DateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC, clock);
+ final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC);
invoiceDao.createInvoice(invoiceModelDao, invoiceItemModelDaos, isRealInvoiceWithItems, new FutureAccountNotifications(dateAndTimeZoneContext, ImmutableMap.<UUID, List<SubscriptionNotification>>of()), internalCallContext);
}
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 bff7839..ce4dcb6 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
@@ -28,6 +28,8 @@ import org.killbill.billing.catalog.api.BillingPeriod;
import org.killbill.billing.catalog.api.Usage;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.usage.RawUsage;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -73,7 +75,9 @@ public class TestSubscriptionConsumableInArrear extends TestUsageInArrearBase {
LocalDate targetDate = new LocalDate(2013, 6, 23);
- final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC));
+ final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC);
+
+ final SubscriptionConsumableInArrear foo = new SubscriptionConsumableInArrear(accountId, invoiceId, billingEvents, ImmutableList.<RawUsage>of(), targetDate, new LocalDate(dt1, DateTimeZone.UTC), accountDateAndTimeZoneContext);
final List<ContiguousIntervalConsumableInArrear> 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 ac6dfdd..8f4d001 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
@@ -41,6 +41,8 @@ import org.killbill.billing.invoice.InvoiceTestSuiteNoDB;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.subscription.api.SubscriptionBase;
import org.killbill.billing.usage.RawUsage;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import org.mockito.Mockito;
import org.testng.annotations.BeforeClass;
@@ -71,7 +73,8 @@ public abstract class TestUsageInArrearBase extends InvoiceTestSuiteNoDB {
}
protected ContiguousIntervalConsumableInArrear createContiguousIntervalConsumableInArrear(final DefaultUsage usage, List<RawUsage> rawUsages, final LocalDate targetDate, final boolean closedInterval, final BillingEvent... events) {
- final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawUsages, targetDate, new LocalDate(events[0].getEffectiveDate()));
+ final AccountDateAndTimeZoneContext accountDateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(clock.getUTCNow(), DateTimeZone.UTC);
+ final ContiguousIntervalConsumableInArrear intervalConsumableInArrear = new ContiguousIntervalConsumableInArrear(usage, accountId, invoiceId, rawUsages, targetDate, new LocalDate(events[0].getEffectiveDate()), accountDateAndTimeZoneContext);
for (BillingEvent event : events) {
intervalConsumableInArrear.addBillingEvent(event);
}
jaxrs/pom.xml 2(+1 -1)
diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 1d97e35..15db23c 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
index be9377e..4a0f4f2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/CatalogJson.java
@@ -34,12 +34,14 @@ import org.killbill.billing.catalog.api.Catalog;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.CurrencyValueNull;
+import org.killbill.billing.catalog.api.Duration;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.Price;
import org.killbill.billing.catalog.api.PriceList;
import org.killbill.billing.catalog.api.PriceListSet;
import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.TimeUnit;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -99,7 +101,15 @@ public class CatalogJson {
}
}
- final PhaseJson phaseJson = new PhaseJson(phase.getPhaseType().toString(), prices);
+ final List<PriceJson> fixedPrices = new LinkedList<PriceJson>();
+ if (phase.getFixed() != null && phase.getFixed().getPrice() != null) {
+ for (final Price price : phase.getFixed().getPrice().getPrices()) {
+ fixedPrices.add(new PriceJson(price));
+ }
+ }
+
+ final DurationJson durationJson = new DurationJson(phase.getDuration().getUnit(), phase.getDuration().getNumber());
+ final PhaseJson phaseJson = new PhaseJson(phase.getPhaseType().toString(), prices, fixedPrices, durationJson);
phases.add(phaseJson);
}
@@ -369,12 +379,18 @@ public class CatalogJson {
private final String type;
private final List<PriceJson> prices;
+ private final List<PriceJson> fixedPrices;
+ private final DurationJson duration;
@JsonCreator
public PhaseJson(@JsonProperty("type") final String type,
- @JsonProperty("prices") final List<PriceJson> prices) {
+ @JsonProperty("prices") final List<PriceJson> prices,
+ @JsonProperty("fixedPrices") final List<PriceJson> fixedPrices,
+ @JsonProperty("duration") final DurationJson duration) {
this.type = type;
this.prices = prices;
+ this.fixedPrices = fixedPrices;
+ this.duration = duration;
}
public String getType() {
@@ -383,12 +399,20 @@ public class CatalogJson {
public List<PriceJson> getPrices() {
return prices;
}
+ public List<PriceJson> getFixedPrices() {
+ return fixedPrices;
+ }
+ public DurationJson getDuration() {
+ return duration;
+ }
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("PhaseJson{");
sb.append("type='").append(type).append('\'');
sb.append(", prices=").append(prices);
+ sb.append(", fixedPrices=").append(fixedPrices);
+ sb.append(", duration=").append(duration);
sb.append('}');
return sb.toString();
}
@@ -407,9 +431,15 @@ public class CatalogJson {
if (prices != null ? !prices.equals(phaseJson.prices) : phaseJson.prices != null) {
return false;
}
+ if (fixedPrices != null ? !fixedPrices.equals(phaseJson.fixedPrices) : phaseJson.fixedPrices != null) {
+ return false;
+ }
if (type != null ? !type.equals(phaseJson.type) : phaseJson.type != null) {
return false;
}
+ if (duration != null ? !duration.equals(phaseJson.duration) : phaseJson.duration != null) {
+ return false;
+ }
return true;
}
@@ -418,6 +448,8 @@ public class CatalogJson {
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (prices != null ? prices.hashCode() : 0);
+ result = 31 * result + (fixedPrices != null ? fixedPrices.hashCode() : 0);
+ result = 31 * result + (duration != null ? duration.hashCode() : 0);
return result;
}
}
@@ -548,4 +580,64 @@ public class CatalogJson {
}
}
+
+ public static class DurationJson {
+
+ private final TimeUnit unit;
+ private final int number;
+
+ @JsonCreator
+ public DurationJson(@JsonProperty("unit") final TimeUnit unit,
+ @JsonProperty("number") final int number) {
+ this.unit = unit;
+ this.number = number;
+ }
+
+ public DurationJson(final Duration duration) throws CurrencyValueNull {
+ this(duration.getUnit(), duration.getNumber());
+ }
+
+ public TimeUnit getUnit() {
+ return unit;
+ }
+
+ public int getNumber() {
+ return number;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DurationJson{");
+ sb.append("unit='").append(unit).append('\'');
+ sb.append(", number=").append(number);
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DurationJson that = (DurationJson) o;
+
+ if (unit != null ? !unit.equals(that.unit) : that.unit != null) {
+ return false;
+ }
+
+ return number == that.number;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = unit != null ? unit.hashCode() : 0;
+ result = 31 * result + number;
+ return result;
+ }
+ }
}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PlanDetailJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PlanDetailJson.java
index dde8096..0b8249c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PlanDetailJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/PlanDetailJson.java
@@ -24,7 +24,7 @@ import org.killbill.billing.catalog.api.CurrencyValueNull;
import org.killbill.billing.catalog.api.Listing;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.Price;
-import org.killbill.billing.jaxrs.json.CatalogJsonSimple.PriceJson;
+import org.killbill.billing.jaxrs.json.CatalogJson.PriceJson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
junction/pom.xml 2(+1 -1)
diff --git a/junction/pom.xml b/junction/pom.xml
index 4902c15..279db21 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-junction</artifactId>
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index fbe8bc8..3e7cd5b 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -17,39 +17,66 @@
package org.killbill.billing.junction.plumbing.billing;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
-import javax.annotation.Nullable;
-
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
import org.killbill.billing.catalog.api.BillingMode;
import org.killbill.billing.catalog.api.Usage;
import org.killbill.billing.junction.BillingEvent;
import org.killbill.billing.junction.BillingEventSet;
+import org.killbill.billing.util.AccountDateAndTimeZoneContext;
+import org.killbill.billing.util.timezone.DefaultAccountDateAndTimeZoneContext;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements SortedSet<BillingEvent>, BillingEventSet {
private static final long serialVersionUID = 1L;
- private boolean accountAutoInvoiceOff = false;
- private List<UUID> subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
- private BillingMode recurringBillingMode;
+ private final boolean accountAutoInvoiceOff;
+ private final List<UUID> subscriptionIdsWithAutoInvoiceOff;
+ private final BillingMode recurringBillingMode;
+ private final DateTimeZone accountTimeZone;
+
+ private DefaultAccountDateAndTimeZoneContext dateTimeZoneContext;
+
+ public DefaultBillingEventSet(final boolean accountAutoInvoiceOff, final BillingMode recurringBillingMode, final DateTimeZone timeZone) {
+ this.accountAutoInvoiceOff = accountAutoInvoiceOff;
+ this.recurringBillingMode = recurringBillingMode;
+ this.accountTimeZone = timeZone;
+ this.subscriptionIdsWithAutoInvoiceOff = new ArrayList<UUID>();
+ }
+
+ @Override
+ public boolean add(final BillingEvent e) {
+ if (dateTimeZoneContext == null) {
+ this.dateTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(e.getEffectiveDate(), accountTimeZone);
+ }
+ return super.add(e);
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends BillingEvent> all) {
+ if (dateTimeZoneContext == null) {
+ this.dateTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(all.iterator().next().getEffectiveDate(), accountTimeZone);
+ }
+ return super.addAll(all);
+ }
/* (non-Javadoc)
- * @see org.killbill.billing.junction.plumbing.billing.BillingEventSet#isAccountAutoInvoiceOff()
- */
+ * @see org.killbill.billing.junction.plumbing.billing.BillingEventSet#isAccountAutoInvoiceOff()
+ */
@Override
public boolean isAccountAutoInvoiceOff() {
return accountAutoInvoiceOff;
@@ -69,6 +96,14 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
}
@Override
+ public AccountDateAndTimeZoneContext getAccountDateAndTimeZoneContext() {
+ if (dateTimeZoneContext == null) {
+ throw new IllegalArgumentException("AccountDateAndTimeZoneContext is not initialized because there is no billing event");
+ }
+ return dateTimeZoneContext;
+ }
+
+ @Override
public Map<String, Usage> getUsages() {
final Iterable<Usage> allUsages = Iterables.concat(Iterables.transform(this, new Function<BillingEvent, List<Usage>>() {
@Override
@@ -86,13 +121,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
return result;
}
- public void setAccountAutoInvoiceIsOff(final boolean accountAutoInvoiceIsOff) {
- this.accountAutoInvoiceOff = accountAutoInvoiceIsOff;
- }
-
- public void setRecurringBillingMode(final BillingMode recurringBillingMode) {
- this.recurringBillingMode = recurringBillingMode;
- }
@Override
public String toString() {
@@ -100,5 +128,4 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
+ ", subscriptionIdsWithAutoInvoiceOff=" + subscriptionIdsWithAutoInvoiceOff + ", Events="
+ super.toString() + "]";
}
-
}
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 3fbf992..ed51da8 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -25,7 +25,6 @@ import javax.annotation.Nullable;
import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.AccountApiException;
import org.killbill.billing.account.api.AccountInternalApi;
-import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.catalog.api.CatalogApiException;
@@ -82,26 +81,26 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
}
@Override
- public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException {
+ public BillingEventSet getBillingEventsForAccountAndUpdateAccountBCD(final UUID accountId, final DryRunArguments dryRunArguments, final InternalCallContext context) throws CatalogApiException, AccountApiException {
final List<SubscriptionBaseBundle> bundles = subscriptionApi.getBundlesForAccount(accountId, context);
- final DefaultBillingEventSet result = new DefaultBillingEventSet();
final StaticCatalog currentCatalog = catalogService.getCurrentCatalog(context);
- result.setRecurringBillingMode(currentCatalog.getRecurringBillingMode());
+
+ final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
+ final DefaultBillingEventSet result = new DefaultBillingEventSet(false, currentCatalog.getRecurringBillingMode(), account.getTimeZone());
+
+
try {
- final ImmutableAccountData account = accountApi.getImmutableAccountDataById(accountId, context);
+
// Check to see if billing is off for the account
final List<Tag> accountTags = tagApi.getTags(accountId, ObjectType.ACCOUNT, context);
final boolean found_AUTO_INVOICING_OFF = is_AUTO_INVOICING_OFF(accountTags);
if (found_AUTO_INVOICING_OFF) {
- result.setAccountAutoInvoiceIsOff(true);
- return result; // billing is off, we are done
+ return new DefaultBillingEventSet(true, currentCatalog.getRecurringBillingMode(), account.getTimeZone()); // billing is off, we are done
}
addBillingEventsForBundles(bundles, account, dryRunArguments, context, result);
- } catch (AccountApiException e) {
- log.warn("Failed while getting BillingEvent", e);
} catch (SubscriptionBaseApiException e) {
log.warn("Failed while getting BillingEvent", e);
}
NEWS 3(+3 -0)
diff --git a/NEWS b/NEWS
index e854650..c428f7f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.15.10
+ See https://github.com/killbill/killbill/releases/tag/killbill-0.15.10
+
0.15.7
See https://github.com/killbill/killbill/releases/tag/killbill-0.15.7
overdue/pom.xml 2(+1 -1)
diff --git a/overdue/pom.xml b/overdue/pom.xml
index 2ccd856..5b9cf56 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-overdue</artifactId>
payment/pom.xml 2(+1 -1)
diff --git a/payment/pom.xml b/payment/pom.xml
index 38da306..cc36953 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
index b5af8b3..210d6da 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentMethodSqlDao.sql.stg
@@ -71,6 +71,7 @@ getPaymentMethodIncludedDelete(accountId) ::= <<
select <allTableFields()>
from <tableName()>
where id = :id
+<AND_CHECK_TENANT()>
;
>>
@@ -80,6 +81,7 @@ select
from <tableName()>
where account_id = :accountId
and is_active = true
+<AND_CHECK_TENANT()>
;
>>
@@ -88,6 +90,7 @@ select
<allTableFields()>
from <tableName()>
where account_id = :accountId
+<AND_CHECK_TENANT()>
;
>>
@@ -104,6 +107,7 @@ select
from <tableName()> t
where t.plugin_name = :pluginName
and t.is_active = true
+<AND_CHECK_TENANT("t.")>
order by t.record_id
limit :rowCount offset :offset
;
@@ -115,5 +119,6 @@ select
from <tableName()> t
where t.plugin_name = :pluginName
and t.is_active = true
+<AND_CHECK_TENANT("t.")>
;
>>
diff --git a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
index ea1de08..5b4ff43 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
+++ b/payment/src/main/resources/org/killbill/billing/payment/dao/PaymentSqlDao.sql.stg
@@ -71,6 +71,7 @@ select
<allTableFields("")>
from <tableName()>
where external_key = :externalKey
+<AND_CHECK_TENANT()>
;
>>
@@ -88,6 +89,7 @@ select
from <tableName()> t
join payment_methods pm on pm.id = t.payment_method_id
where pm.plugin_name = :pluginName
+<AND_CHECK_TENANT("t.")>
order by t.record_id asc
limit :rowCount offset :offset
;
@@ -99,6 +101,7 @@ select
from <tableName()> t
join payment_methods pm on pm.id = t.payment_method_id
where pm.plugin_name = :pluginName
+<AND_CHECK_TENANT("t.")>
;
>>
pom.xml 4(+2 -2)
diff --git a/pom.xml b/pom.xml
index b4b72df..d33dd3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,10 +21,10 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.70</version>
+ <version>0.71-SNAPSHOT</version>
</parent>
<artifactId>killbill</artifactId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<packaging>pom</packaging>
<name>killbill</name>
<description>Library for managing recurring subscriptions and the associated billing</description>
profiles/killbill/pom.xml 2(+1 -1)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 9aa62a4..482be27 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index 35a8890..7a24e1c 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -55,7 +55,7 @@ public class TestCatalog extends TestJaxrsBase {
Assert.assertEquals(catalogJson.getEffectiveDate(), Date.valueOf("2011-01-01"));
Assert.assertEquals(catalogJson.getCurrencies().size(), 3);
Assert.assertEquals(catalogJson.getProducts().size(), 11);
- Assert.assertEquals(catalogJson.getPriceLists().size(), 3);
+ Assert.assertEquals(catalogJson.getPriceLists().size(), 4);
for (final Product productJson : catalogJson.getProducts()) {
if (!"BASE".equals(productJson.getType())) {
profiles/killpay/pom.xml 2(+1 -1)
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index 4afb37e..f6627e8 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
<parent>
<artifactId>killbill-profiles</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles-killpay</artifactId>
profiles/pom.xml 2(+1 -1)
diff --git a/profiles/pom.xml b/profiles/pom.xml
index 05a7073..48eff8c 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-profiles</artifactId>
subscription/pom.xml 2(+1 -1)
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 8f2d689..ac5080d 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-subscription</artifactId>
tenant/pom.xml 2(+1 -1)
diff --git a/tenant/pom.xml b/tenant/pom.xml
index 89769d6..813cffb 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-tenant</artifactId>
usage/pom.xml 2(+1 -1)
diff --git a/usage/pom.xml b/usage/pom.xml
index c32e595..8c45851 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-usage</artifactId>
util/pom.xml 2(+1 -1)
diff --git a/util/pom.xml b/util/pom.xml
index 1d45b8b..26cf048 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
<parent>
<artifactId>killbill</artifactId>
<groupId>org.kill-bill.billing</groupId>
- <version>0.15.10-SNAPSHOT</version>
+ <version>0.15.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>killbill-util</artifactId>
diff --git a/util/src/test/java/org/killbill/billing/util/timezone/TestDateAndTimeZoneContext.java b/util/src/test/java/org/killbill/billing/util/timezone/TestDateAndTimeZoneContext.java
index f7e50a0..ba6b67f 100644
--- a/util/src/test/java/org/killbill/billing/util/timezone/TestDateAndTimeZoneContext.java
+++ b/util/src/test/java/org/killbill/billing/util/timezone/TestDateAndTimeZoneContext.java
@@ -57,7 +57,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, -1);
}
@@ -71,7 +71,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, 0);
}
@@ -85,7 +85,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, 0);
}
@@ -99,7 +99,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, 1);
}
@@ -113,7 +113,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, 1);
}
@@ -127,7 +127,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
- int offset = DateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
+ int offset = DefaultAccountDateAndTimeZoneContext.computeOffsetFromUtc(effectiveDateTime, timeZone);
assertEquals(offset, 0);
}
@@ -137,7 +137,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 19);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
@@ -151,7 +151,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 20);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
@@ -165,7 +165,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 20);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
@@ -178,7 +178,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 21);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
@@ -191,7 +191,7 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 21);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
@@ -204,10 +204,36 @@ public class TestDateAndTimeZoneContext extends UtilTestSuiteNoDB {
final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC);
final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8);
- final DateAndTimeZoneContext dateContext = new DateAndTimeZoneContext(effectiveDateTime, timeZone, clock);
+ final DefaultAccountDateAndTimeZoneContext dateContext = new DefaultAccountDateAndTimeZoneContext(effectiveDateTime, timeZone);
final LocalDate endDate = new LocalDate(2013, 01, 20);
final DateTime endDateTimeInUTC = dateContext.computeUTCDateTimeFromLocalDate(endDate);
assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0);
}
+
+
+ @Test(groups = "fast")
+ public void testComputeTargetDateWithDayLigthSaving() {
+
+ final DateTimeZone tz = DateTimeZone.forID("America/Juneau");
+ final DateTime originalDateTime = new DateTime("2015-09-01T08:01:01.000Z");
+
+ final DefaultAccountDateAndTimeZoneContext dateAndTimeZoneContext = new DefaultAccountDateAndTimeZoneContext(originalDateTime, tz);
+
+
+ final LocalDate l1 = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(originalDateTime);
+ assertEquals(l1, new LocalDate("2015-09-01"));
+
+ final LocalDate l1ComputedFromAccountTz = new LocalDate(originalDateTime, tz);
+ assertEquals(l1ComputedFromAccountTz, new LocalDate("2015-09-01"));
+
+ final DateTime newDateTime = new DateTime("2015-12-01T08:01:01.000Z");
+
+ final LocalDate l2 = dateAndTimeZoneContext.computeLocalDateFromFixedAccountOffset(newDateTime);
+ assertEquals(l2, new LocalDate("2015-12-01"));
+
+ final LocalDate l2ComputedFromAccountTz = new LocalDate(newDateTime, tz);
+ assertEquals(l2ComputedFromAccountTz, new LocalDate("2015-11-30"));
+
+ }
}